From d564baafed0863813a87d872f8663134e74228c8 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 14 May 2023 20:03:30 -0400 Subject: Clean up documentation and remove junk files from install --- doc/Doxyfile.in | 31 +++++++++ doc/api/meson.build | 9 +++ doc/c/Doxyfile.in | 31 --------- doc/c/api/meson.build | 9 --- doc/c/index.rst | 14 ---- doc/c/meson.build | 54 ---------------- doc/c/overview.rst | 22 ------- doc/c/plugins.rst | 94 --------------------------- doc/c/uis.rst | 25 -------- doc/c/world.rst | 157 --------------------------------------------- doc/c/xml/meson.build | 21 ------ doc/conf.py.in | 121 +++++++++++++++++++++++----------- doc/html/meson.build | 32 +++++++++ doc/index.rst | 23 +++++++ doc/meson.build | 58 ++++++++++++++--- doc/overview.rst | 22 +++++++ doc/plugins.rst | 94 +++++++++++++++++++++++++++ doc/singlehtml/meson.build | 26 ++++++++ doc/summary.rst | 13 ---- doc/uis.rst | 25 ++++++++ doc/world.rst | 157 +++++++++++++++++++++++++++++++++++++++++++++ doc/xml/meson.build | 21 ++++++ 22 files changed, 573 insertions(+), 486 deletions(-) create mode 100644 doc/Doxyfile.in create mode 100644 doc/api/meson.build delete mode 100644 doc/c/Doxyfile.in delete mode 100644 doc/c/api/meson.build delete mode 100644 doc/c/index.rst delete mode 100644 doc/c/meson.build delete mode 100644 doc/c/overview.rst delete mode 100644 doc/c/plugins.rst delete mode 100644 doc/c/uis.rst delete mode 100644 doc/c/world.rst delete mode 100644 doc/c/xml/meson.build create mode 100644 doc/html/meson.build create mode 100644 doc/index.rst create mode 100644 doc/overview.rst create mode 100644 doc/plugins.rst create mode 100644 doc/singlehtml/meson.build delete mode 100644 doc/summary.rst create mode 100644 doc/uis.rst create mode 100644 doc/world.rst create mode 100644 doc/xml/meson.build (limited to 'doc') diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in new file mode 100644 index 0000000..2ca5694 --- /dev/null +++ b/doc/Doxyfile.in @@ -0,0 +1,31 @@ +# Copyright 2021-2022 David Robillard +# SPDX-License-Identifier: 0BSD OR ISC + +PROJECT_NAME = Lilv +PROJECT_BRIEF = "A library for simple use of LV2 plugins" + +QUIET = YES +WARN_AS_ERROR = NO +WARN_IF_UNDOCUMENTED = NO +WARN_NO_PARAMDOC = NO + +JAVADOC_AUTOBRIEF = YES + +CASE_SENSE_NAMES = YES +HIDE_IN_BODY_DOCS = YES +REFERENCES_LINK_SOURCE = NO + +GENERATE_HTML = NO +GENERATE_LATEX = NO +GENERATE_XML = YES +XML_PROGRAMLISTING = NO +SHOW_FILES = NO + +MACRO_EXPANSION = YES +PREDEFINED = LILV_API LILV_DEPRECATED + +RECURSIVE = YES +STRIP_FROM_PATH = @LILV_SRCDIR@ +INPUT = @LILV_SRCDIR@/include/lilv/lilv.h + +OUTPUT_DIRECTORY = @DOX_OUTPUT@ diff --git a/doc/api/meson.build b/doc/api/meson.build new file mode 100644 index 0000000..8c8293b --- /dev/null +++ b/doc/api/meson.build @@ -0,0 +1,9 @@ +# Copyright 2021-2022 David Robillard +# SPDX-License-Identifier: 0BSD OR ISC + +c_lilv_rst = custom_target( + 'lilv.rst', + command: [sphinxygen, '-f', '@INPUT0@', '@OUTDIR@'], + input: [c_index_xml] + c_rst_files, + output: 'lilv.rst', +) diff --git a/doc/c/Doxyfile.in b/doc/c/Doxyfile.in deleted file mode 100644 index 2ca5694..0000000 --- a/doc/c/Doxyfile.in +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2021-2022 David Robillard -# SPDX-License-Identifier: 0BSD OR ISC - -PROJECT_NAME = Lilv -PROJECT_BRIEF = "A library for simple use of LV2 plugins" - -QUIET = YES -WARN_AS_ERROR = NO -WARN_IF_UNDOCUMENTED = NO -WARN_NO_PARAMDOC = NO - -JAVADOC_AUTOBRIEF = YES - -CASE_SENSE_NAMES = YES -HIDE_IN_BODY_DOCS = YES -REFERENCES_LINK_SOURCE = NO - -GENERATE_HTML = NO -GENERATE_LATEX = NO -GENERATE_XML = YES -XML_PROGRAMLISTING = NO -SHOW_FILES = NO - -MACRO_EXPANSION = YES -PREDEFINED = LILV_API LILV_DEPRECATED - -RECURSIVE = YES -STRIP_FROM_PATH = @LILV_SRCDIR@ -INPUT = @LILV_SRCDIR@/include/lilv/lilv.h - -OUTPUT_DIRECTORY = @DOX_OUTPUT@ diff --git a/doc/c/api/meson.build b/doc/c/api/meson.build deleted file mode 100644 index 8c8293b..0000000 --- a/doc/c/api/meson.build +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright 2021-2022 David Robillard -# SPDX-License-Identifier: 0BSD OR ISC - -c_lilv_rst = custom_target( - 'lilv.rst', - command: [sphinxygen, '-f', '@INPUT0@', '@OUTDIR@'], - input: [c_index_xml] + c_rst_files, - output: 'lilv.rst', -) diff --git a/doc/c/index.rst b/doc/c/index.rst deleted file mode 100644 index e93b0e8..0000000 --- a/doc/c/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. - Copyright 2020-2022 David Robillard - SPDX-License-Identifier: ISC - -#### -Lilv -#### - -.. include:: summary.rst - -.. toctree:: - - overview - api/lilv diff --git a/doc/c/meson.build b/doc/c/meson.build deleted file mode 100644 index 53e69dc..0000000 --- a/doc/c/meson.build +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright 2021-2022 David Robillard -# SPDX-License-Identifier: 0BSD OR ISC - -config = configuration_data() -config.set('LILV_VERSION', meson.project_version()) - -conf_py = configure_file( - configuration: config, - input: files('../conf.py.in'), - output: 'conf.py', -) - -configure_file( - copy: true, - input: files('../summary.rst'), - output: 'summary.rst', -) - -c_rst_files = files( - 'index.rst', - 'overview.rst', - 'plugins.rst', - 'uis.rst', - 'world.rst', -) - -foreach f : c_rst_files - configure_file(copy: true, input: f, output: '@PLAINNAME@') -endforeach - -subdir('xml') -subdir('api') - -docs = custom_target( - 'singlehtml', - build_by_default: true, - command: [sphinx_build, '-M', 'singlehtml', '@OUTDIR@', '@OUTDIR@', - '-E', '-q', '-t', 'singlehtml'], - input: [c_rst_files, c_lilv_rst, c_index_xml], - install: true, - install_dir: docdir / 'lilv-0', - output: 'singlehtml', -) - -docs = custom_target( - 'html', - build_by_default: true, - command: [sphinx_build, '-M', 'html', '@OUTDIR@', '@OUTDIR@', - '-E', '-q', '-t', 'html'], - input: [c_rst_files, c_lilv_rst, c_index_xml], - install: true, - install_dir: docdir / 'lilv-0', - output: 'html', -) diff --git a/doc/c/overview.rst b/doc/c/overview.rst deleted file mode 100644 index 17ebec9..0000000 --- a/doc/c/overview.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. - Copyright 2020-2022 David Robillard - SPDX-License-Identifier: ISC - -.. default-domain:: c -.. highlight:: c - -######## -Overview -######## - -The complete API is declared in ``lilv.h``: - -.. code-block:: c - - #include - -.. toctree:: - - world - plugins - uis diff --git a/doc/c/plugins.rst b/doc/c/plugins.rst deleted file mode 100644 index c7ad547..0000000 --- a/doc/c/plugins.rst +++ /dev/null @@ -1,94 +0,0 @@ -.. - Copyright 2020-2022 David Robillard - SPDX-License-Identifier: ISC - -.. default-domain:: c -.. highlight:: c - -####### -Plugins -####### - -After bundles are loaded, -all discovered plugins can be accessed via :func:`lilv_world_get_all_plugins`: - -.. code-block:: c - - LilvPlugins* plugins = lilv_world_get_all_plugins(world); - -:struct:`LilvPlugins` is a collection of plugins that can be accessed by index or plugin URI. -The convenicne macro :macro:`LILV_FOREACH` is provided to make iterating over collections simple. -For example, to print the URI of every plugin: - -.. code-block:: c - - LILV_FOREACH (plugins, i, list) { - const LilvPlugin* p = lilv_plugins_get(list, i); - printf("%s\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); - } - } - -More typically, -you want to load a specific plugin, -which can be done with :func:`lilv_plugins_get_by_uri`: - -.. code-block:: c - - LilvNode* plugin_uri = lilv_new_uri(world, "http://example.org/Osc"); - - const LilvPlugin* plugin = lilv_plugins_get_by_uri(list, plugin_uri); - -:struct:`LilvPlugin` has various accessors that can be used to get information about the plugin. -See the :doc:`API reference ` for details. - -********* -Instances -********* - -:struct:`LilvPlugin` only represents the data of the plugin, -it does not load or access the actual plugin code. -To do that, you must instantiate a plugin to create :struct:`LilvInstance`: - -.. code-block:: c - - LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL); - -Connecting Ports -================ - -Before running a plugin instance, its ports must be connected to some data. -This is done with :func:`lilv_instance_connect_port`. -Assuming the plugins has two control input ports and one audio output port, -in that order: - -.. code-block:: c - - float control_in_1 = 0.0f; - float control_in_2 = 0.0f; - - float audio_out[128]; - - lilv_instance_connect_port(instance, 0, &control_in_1); - lilv_instance_connect_port(instance, 1, &control_in_2); - lilv_instance_connect_port(instance, 2, &audio_out); - -Processing Data -=============== - -Once the ports are connected, the instance can be activated and run: - -.. code-block:: c - - lilv_instance_activate(instance); - - lilv_instance_run(instance, 128); - // Copy buffers around and probably run several times here... - - lilv_instance_deactivate(instance); - -Once you are done with an instance, -it can be destroyed with :func:`lilv_instance_free`: - -.. code-block:: c - - lilv_instance_free(instance); diff --git a/doc/c/uis.rst b/doc/c/uis.rst deleted file mode 100644 index 1169060..0000000 --- a/doc/c/uis.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. - Copyright 2020-2022 David Robillard - SPDX-License-Identifier: ISC - -.. default-domain:: c -.. highlight:: c - -############### -User Interfaces -############### - -Plugins may have custom user interfaces, or `UIs`, -which are installed in bundles just like plugins. - -The available UIs for a plugin can be accessed with :func:`lilv_plugin_get_uis`: - -.. code-block:: c - - LilvUIs* uis = lilv_plugin_get_uis(plugin); - -:struct:`LilvUIs` is a collection much like `LilvPlugins`, -except it is of course a set of :struct:`LilvUI` rather than a set of :struct:`LilvPlugin`. -Also like plugins, -the :struct:`LilvUI` class has various accessors that can be used to get information about the UI. -See the :doc:`API reference ` for details. diff --git a/doc/c/world.rst b/doc/c/world.rst deleted file mode 100644 index 8b71165..0000000 --- a/doc/c/world.rst +++ /dev/null @@ -1,157 +0,0 @@ -.. - Copyright 2020-2022 David Robillard - SPDX-License-Identifier: ISC - -.. default-domain:: c -.. highlight:: c - -##### -World -##### - -The world is the top-level object which represents an instance of Lilv. -It is used to discover and load LV2 data, -and stores an internal cache of loaded data for fast searching. - -An application typically has a single world, -which is constructed once on startup and used throughout the application's lifetime. - -************ -Construction -************ - -A world must be created before anything else. -A world is created with :func:`lilv_world_new`, for example: - -.. code-block:: c - - LilvWorld* world = lilv_world_new(); - -*************** -Setting Options -*************** - -Various options to control Lilv's behavior can be set with :func:`lilv_world_set_option`. -The currently supported options are :c:macro:`LILV_OPTION_FILTER_LANG`, -:c:macro:`LILV_OPTION_DYN_MANIFEST`, and :c:macro:`LILV_OPTION_LV2_PATH`. - -For example, to set the LV2 path to only load plugins bundled in the application: - -.. code-block:: c - - LilvNode* lv2_path = lilv_new_file_uri(world, NULL, "/myapp/lv2"); - - lilv_world_set_option(world, LILV_OPTION_LV2_PATH, lv2_path); - -************ -Loading Data -************ - -Before using anything, data must be loaded from disk. -All LV2 data (plugins, UIs, specifications, presets, and so on) is installed in `bundles`, -a standard directory format - -Discovering and Loading Bundles -=============================== - -Typical hosts will simply load all bundles in the standard LV2 locations on the system: - -.. code-block:: c - - lilv_world_load_all(world); - -This will discover all bundles on the system, -as well as load the required data defined in any discovered specifications. - -It is also possible to load a specific bundle: - -.. code-block:: c - - LilvNode* bundle_uri = lilv_new_file_uri(world, NULL, "/some/plugin.lv2"); - - lilv_world_load_bundle(world, bundle_uri); - -The LV2 specification itself is also installed in bundles, -so if you are not using :func:`lilv_world_load_all`, -it is necessary to manually load the discovered specification data: - -.. code-block:: c - - lilv_world_load_specifications(world); - lilv_world_load_plugin_classes(world); - -************* -Querying Data -************* - -The world contains a model of all the loaded data in memory which can be queried. - -Data Model -========== - -LV2 data is a set of "statements", -where a statement is a bit like a simple machine-readable sentence. -The "subject" and "object" are as in natural language, -and the "predicate" is like the verb, but more general. - -For example, we could make a statement about a plugin in english: - - MyOsc has the name "Super Oscillator" - -We can break this statement into 3 pieces like so: - -.. list-table:: - :header-rows: 1 - - * - Subject - - Predicate - - Object - * - MyOsc - - has the name - - "My Super Oscillator" - -Statements use URIs to identify things. -In this case, we assume that this plugin has the URI ``http://example.org/Osc``. -The LV2 specification defines that ``http://usefulinc.com/ns/doap#name`` is the property used to describe a plugin's name. -So, this statement is: - -.. list-table:: - :header-rows: 1 - - * - Subject - - Predicate - - Object - * - ``http://example.org/Osc`` - - ``http://usefulinc.com/ns/doap#name`` - - "My Oscillator" - -Finding Values -============== - -Based on this model, you can find all values that match a certain pattern. -Patterns are just statements, -but with ``NULL`` used as a wildcard that matches anything. -So, for example, you can get the name of a plugin using :func:`lilv_world_find_nodes`: - -.. code-block:: c - - LilvNode* plugin_uri = lilv_new_uri(world, "http://example.org/Osc"); - LilvNode* doap_name = lilv_new_uri(world, "http://usefulinc.com/ns/doap#name"); - - LilvNodes* values = lilv_world_find_nodes(world, plugin_uri, doap_name, NULL); - -Note that a set of values is returned, -because some properties may have several values. -When you are only interested in one value, -you can use the simpler :func:`lilv_world_get` instead: - -.. code-block:: c - - LilvNode* value = lilv_world_get(world, plugin_uri, doap_name, NULL); - -If you are only interested if a value exists at all, -use :func:`lilv_world_ask`: - -.. code-block:: c - - bool has_name = lilv_world_ask(world, plugin_uri, doap_name, NULL); diff --git a/doc/c/xml/meson.build b/doc/c/xml/meson.build deleted file mode 100644 index dc234f3..0000000 --- a/doc/c/xml/meson.build +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2021-2022 David Robillard -# SPDX-License-Identifier: 0BSD OR ISC - -doxygen = find_program('doxygen') - -config = configuration_data() -config.set('LILV_SRCDIR', lilv_src_root) -config.set('DOX_OUTPUT', meson.current_build_dir() / '..') - -c_doxyfile = configure_file( - configuration: config, - input: files('../Doxyfile.in'), - output: 'Doxyfile', -) - -c_index_xml = custom_target( - 'index.xml', - command: [doxygen, '@INPUT0@'], - input: [c_doxyfile] + c_headers, - output: 'index.xml', -) diff --git a/doc/conf.py.in b/doc/conf.py.in index b53e9ca..444b70a 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -3,10 +3,12 @@ # Project information -project = "Lilv" -copyright = "2020, David Robillard" +project = "@LILV_TITLE@" +copyright = "2020-2023, David Robillard" author = "David Robillard" release = "@LILV_VERSION@" +version = "@LILV_VERSION@" +desc = "A library for loading LV2 plugins" # General configuration @@ -15,6 +17,13 @@ language = "en" nitpicky = True pygments_style = "friendly" +try: + import sphinx_lv2_theme + + have_lv2_theme = True +except ModuleNotFoundError: + have_lv2_theme = False + # Ignore everything opaque or external for nitpicky mode _opaque = [ "FILE", @@ -41,42 +50,78 @@ nitpick_ignore = list(map(lambda x: ("c:identifier", x), _opaque)) # HTML output -html_theme = "sphinx_lv2_theme" html_copy_source = False -html_short_title = "Lilv" - -if tags.has('singlehtml'): - html_sidebars = { - "**": [ - "globaltoc.html", - ] - } - - html_theme_options = { - "body_max_width": "65em", - "body_min_width": "50em", - "description": "A library for loading LV2 plugins", - "show_footer_version": True, - "show_logo_version": False, - "logo_name": True, - "nosidebar": False, - "page_width": "80em", - "sidebar_width": "11em", - "globaltoc_maxdepth": 3, - "globaltoc_collapse": False, - } +html_secnumber_suffix = " " +html_short_title = "@LILV_TITLE@" + +html_theme_options = { + "description": desc, + "logo_name": True, + "logo_width": "8em", +} +if tags.has("singlehtml"): + html_sidebars = {"**": ["globaltoc.html"]} + html_theme_options.update( + { + "globaltoc_collapse": False, + "globaltoc_maxdepth": 3, + "nosidebar": False, + "page_width": "80em", + } + ) else: - html_theme_options = { - "body_max_width": "60em", - "body_min_width": "40em", - "description": "A library for loading LV2 plugins", - "show_footer_version": True, - "show_logo_version": False, - "logo_name": True, - "nosidebar": True, - "page_width": "60em", - "sidebar_width": "14em", - "globaltoc_maxdepth": 1, - "globaltoc_collapse": True, - } + html_theme_options.update( + { + "globaltoc_collapse": True, + "globaltoc_maxdepth": 1, + "nosidebar": True, + "page_width": "60em", + } + ) + +if have_lv2_theme: + html_theme = "sphinx_lv2_theme" + + html_theme_options.update( + { + "show_footer_version": True, + "show_logo_version": True, + } + ) + + if tags.has("singlehtml"): + html_theme_options.update( + { + "body_max_width": "64em", + "body_min_width": "64em", + "nosidebar": False, + "sidebar_width": "12em", + } + ) + + else: + html_theme_options.update( + { + "body_max_width": "60em", + "body_min_width": "40em", + } + ) + +else: + html_theme = "alabaster" + + if tags.has("singlehtml"): + html_theme_options = { + "body_max_width": "58em", + "body_min_width": "40em", + "sidebar_width": "16em", + } + + else: + html_theme_options = { + "body_max_width": "60em", + "body_min_width": "40em", + "nosidebar": True, + "show_relbars": True, + } diff --git a/doc/html/meson.build b/doc/html/meson.build new file mode 100644 index 0000000..26d3a03 --- /dev/null +++ b/doc/html/meson.build @@ -0,0 +1,32 @@ +# Copyright 2021-2023 David Robillard +# SPDX-License-Identifier: 0BSD OR ISC + +html_dir = docdir / versioned_name / 'html' + +# TODO: Add install_tag: 'doc' after requiring meson 0.60.0 + +custom_target( + 'html', + build_by_default: true, + command: [ + sphinx_build, '-M', 'html', '@OUTDIR@' / '..', '@OUTDIR@' / '..', + '-t', 'html', + ] + sphinx_flags, + input: [c_rst_files, c_lilv_rst, c_index_xml, conf_py], + install: true, + install_dir: html_dir, + output: [ + '_static', + 'api', + 'genindex.html', + 'index.html', + 'overview.html', + 'plugins.html', + 'uis.html', + 'world.html', + ], +) + +if not meson.is_subproject() + summary('Paginated HTML', get_option('prefix') / html_dir, section: 'Directories') +endif diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..46a9026 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,23 @@ +.. + Copyright 2020-2022 David Robillard + SPDX-License-Identifier: ISC + +#### +Lilv +#### + +Lilv is a library for working with LV2_ plugins. +It provides several types and functions that can be used to discover plugins, +investigate their data, load and run them, and save and restore their state. + +Lilv is the standard implementation used by nearly all LV2 hosts. +It implements the details of the LV2 specification on POSIX, MacOS, and Windows, +and provides a simpler portable API for applications. + +.. toctree:: + + overview + api/lilv + +.. _LV2: http://lv2plug.in/ + diff --git a/doc/meson.build b/doc/meson.build index 7c633a0..787ab4f 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -3,22 +3,64 @@ docdir = get_option('datadir') / 'doc' +# Find required programs doxygen = find_program('doxygen', required: get_option('docs')) -sphinxygen = find_program('sphinxygen', required: false) sphinx_build = find_program('sphinx-build', required: get_option('docs')) -if not sphinxygen.found() - subproject('sphinxygen') - sphinxygen = find_program('sphinxygen', required: get_option('docs')) +# Find sphinxygen or fall back to subproject +sphinxygen = disabler() +if doxygen.found() and sphinx_build.found() + sphinxygen = find_program('sphinxygen', required: false) + if not sphinxygen.found() + subproject('sphinxygen') + sphinxygen = find_program('sphinxygen', required: get_option('docs')) + endif endif +# Build documentation if all required tools are found build_docs = doxygen.found() and sphinxygen.found() and sphinx_build.found() - if build_docs - subdir('c') + # Configure conf.py for Sphinx + conf_config = configuration_data() + conf_config.set('LILV_SRCDIR', lilv_src_root) + conf_config.set('LILV_TITLE', get_option('title')) + conf_config.set('LILV_VERSION', meson.project_version()) + conf_py = configure_file( + configuration: conf_config, + input: files('conf.py.in'), + output: 'conf.py', + ) + + # Copy hand-written documentation files + c_rst_files = files( + 'index.rst', + 'overview.rst', + 'plugins.rst', + 'uis.rst', + 'world.rst', + ) + foreach f : c_rst_files + configure_file(copy: true, input: f, output: '@PLAINNAME@') + endforeach + + # Generate reference documentation input with Doxygen and Sphinxygen + subdir('xml') + subdir('api') + + # Build strict Sphinx flags, with termination on warnings if werror=true + sphinx_flags = ['-E', '-a', '-q'] + if get_option('werror') + sphinx_flags += ['-W'] + endif + + # Run Sphinx to generate final documentation for each format + foreach format : ['html', 'singlehtml'] + if not get_option(format).disabled() + subdir(format) + endif + endforeach endif if not meson.is_subproject() - summary('Documentation', build_docs, bool_yn: true) + summary('Documentation', build_docs, bool_yn: true, section: 'Components') endif - diff --git a/doc/overview.rst b/doc/overview.rst new file mode 100644 index 0000000..17ebec9 --- /dev/null +++ b/doc/overview.rst @@ -0,0 +1,22 @@ +.. + Copyright 2020-2022 David Robillard + SPDX-License-Identifier: ISC + +.. default-domain:: c +.. highlight:: c + +######## +Overview +######## + +The complete API is declared in ``lilv.h``: + +.. code-block:: c + + #include + +.. toctree:: + + world + plugins + uis diff --git a/doc/plugins.rst b/doc/plugins.rst new file mode 100644 index 0000000..c7ad547 --- /dev/null +++ b/doc/plugins.rst @@ -0,0 +1,94 @@ +.. + Copyright 2020-2022 David Robillard + SPDX-License-Identifier: ISC + +.. default-domain:: c +.. highlight:: c + +####### +Plugins +####### + +After bundles are loaded, +all discovered plugins can be accessed via :func:`lilv_world_get_all_plugins`: + +.. code-block:: c + + LilvPlugins* plugins = lilv_world_get_all_plugins(world); + +:struct:`LilvPlugins` is a collection of plugins that can be accessed by index or plugin URI. +The convenicne macro :macro:`LILV_FOREACH` is provided to make iterating over collections simple. +For example, to print the URI of every plugin: + +.. code-block:: c + + LILV_FOREACH (plugins, i, list) { + const LilvPlugin* p = lilv_plugins_get(list, i); + printf("%s\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); + } + } + +More typically, +you want to load a specific plugin, +which can be done with :func:`lilv_plugins_get_by_uri`: + +.. code-block:: c + + LilvNode* plugin_uri = lilv_new_uri(world, "http://example.org/Osc"); + + const LilvPlugin* plugin = lilv_plugins_get_by_uri(list, plugin_uri); + +:struct:`LilvPlugin` has various accessors that can be used to get information about the plugin. +See the :doc:`API reference ` for details. + +********* +Instances +********* + +:struct:`LilvPlugin` only represents the data of the plugin, +it does not load or access the actual plugin code. +To do that, you must instantiate a plugin to create :struct:`LilvInstance`: + +.. code-block:: c + + LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL); + +Connecting Ports +================ + +Before running a plugin instance, its ports must be connected to some data. +This is done with :func:`lilv_instance_connect_port`. +Assuming the plugins has two control input ports and one audio output port, +in that order: + +.. code-block:: c + + float control_in_1 = 0.0f; + float control_in_2 = 0.0f; + + float audio_out[128]; + + lilv_instance_connect_port(instance, 0, &control_in_1); + lilv_instance_connect_port(instance, 1, &control_in_2); + lilv_instance_connect_port(instance, 2, &audio_out); + +Processing Data +=============== + +Once the ports are connected, the instance can be activated and run: + +.. code-block:: c + + lilv_instance_activate(instance); + + lilv_instance_run(instance, 128); + // Copy buffers around and probably run several times here... + + lilv_instance_deactivate(instance); + +Once you are done with an instance, +it can be destroyed with :func:`lilv_instance_free`: + +.. code-block:: c + + lilv_instance_free(instance); diff --git a/doc/singlehtml/meson.build b/doc/singlehtml/meson.build new file mode 100644 index 0000000..4822d8c --- /dev/null +++ b/doc/singlehtml/meson.build @@ -0,0 +1,26 @@ +# Copyright 2021-2023 David Robillard +# SPDX-License-Identifier: 0BSD OR ISC + +singlehtml_dir = docdir / versioned_name / 'singlehtml' + +# TODO: Add install_tag: 'doc' after requiring meson 0.60.0 + +custom_target( + 'singlehtml', + build_by_default: true, + command: [ + sphinx_build, '-M', 'singlehtml', '@OUTDIR@' / '..', '@OUTDIR@' / '..', + '-t', 'singlehtml', + ] + sphinx_flags, + input: [c_rst_files, c_lilv_rst, c_index_xml, conf_py], + install: true, + install_dir: singlehtml_dir, + output: [ + '_static', + 'index.html', + ], +) + +if not meson.is_subproject() + summary('Unified HTML', get_option('prefix') / singlehtml_dir, section: 'Directories') +endif diff --git a/doc/summary.rst b/doc/summary.rst deleted file mode 100644 index 0349af1..0000000 --- a/doc/summary.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. - Copyright 2020-2022 David Robillard - SPDX-License-Identifier: ISC - -Lilv is a library for working with LV2_ plugins. -It provides several types and functions that can be used to discover plugins, -investigate their data, load and run them, and save and restore their state. - -Lilv is the standard implementation used by nearly all LV2 hosts. -It implements the details of the LV2 specification on POSIX, MacOS, and Windows, -and provides a simpler portable API for applications. - -.. _LV2: http://lv2plug.in/ diff --git a/doc/uis.rst b/doc/uis.rst new file mode 100644 index 0000000..1169060 --- /dev/null +++ b/doc/uis.rst @@ -0,0 +1,25 @@ +.. + Copyright 2020-2022 David Robillard + SPDX-License-Identifier: ISC + +.. default-domain:: c +.. highlight:: c + +############### +User Interfaces +############### + +Plugins may have custom user interfaces, or `UIs`, +which are installed in bundles just like plugins. + +The available UIs for a plugin can be accessed with :func:`lilv_plugin_get_uis`: + +.. code-block:: c + + LilvUIs* uis = lilv_plugin_get_uis(plugin); + +:struct:`LilvUIs` is a collection much like `LilvPlugins`, +except it is of course a set of :struct:`LilvUI` rather than a set of :struct:`LilvPlugin`. +Also like plugins, +the :struct:`LilvUI` class has various accessors that can be used to get information about the UI. +See the :doc:`API reference ` for details. diff --git a/doc/world.rst b/doc/world.rst new file mode 100644 index 0000000..8b71165 --- /dev/null +++ b/doc/world.rst @@ -0,0 +1,157 @@ +.. + Copyright 2020-2022 David Robillard + SPDX-License-Identifier: ISC + +.. default-domain:: c +.. highlight:: c + +##### +World +##### + +The world is the top-level object which represents an instance of Lilv. +It is used to discover and load LV2 data, +and stores an internal cache of loaded data for fast searching. + +An application typically has a single world, +which is constructed once on startup and used throughout the application's lifetime. + +************ +Construction +************ + +A world must be created before anything else. +A world is created with :func:`lilv_world_new`, for example: + +.. code-block:: c + + LilvWorld* world = lilv_world_new(); + +*************** +Setting Options +*************** + +Various options to control Lilv's behavior can be set with :func:`lilv_world_set_option`. +The currently supported options are :c:macro:`LILV_OPTION_FILTER_LANG`, +:c:macro:`LILV_OPTION_DYN_MANIFEST`, and :c:macro:`LILV_OPTION_LV2_PATH`. + +For example, to set the LV2 path to only load plugins bundled in the application: + +.. code-block:: c + + LilvNode* lv2_path = lilv_new_file_uri(world, NULL, "/myapp/lv2"); + + lilv_world_set_option(world, LILV_OPTION_LV2_PATH, lv2_path); + +************ +Loading Data +************ + +Before using anything, data must be loaded from disk. +All LV2 data (plugins, UIs, specifications, presets, and so on) is installed in `bundles`, +a standard directory format + +Discovering and Loading Bundles +=============================== + +Typical hosts will simply load all bundles in the standard LV2 locations on the system: + +.. code-block:: c + + lilv_world_load_all(world); + +This will discover all bundles on the system, +as well as load the required data defined in any discovered specifications. + +It is also possible to load a specific bundle: + +.. code-block:: c + + LilvNode* bundle_uri = lilv_new_file_uri(world, NULL, "/some/plugin.lv2"); + + lilv_world_load_bundle(world, bundle_uri); + +The LV2 specification itself is also installed in bundles, +so if you are not using :func:`lilv_world_load_all`, +it is necessary to manually load the discovered specification data: + +.. code-block:: c + + lilv_world_load_specifications(world); + lilv_world_load_plugin_classes(world); + +************* +Querying Data +************* + +The world contains a model of all the loaded data in memory which can be queried. + +Data Model +========== + +LV2 data is a set of "statements", +where a statement is a bit like a simple machine-readable sentence. +The "subject" and "object" are as in natural language, +and the "predicate" is like the verb, but more general. + +For example, we could make a statement about a plugin in english: + + MyOsc has the name "Super Oscillator" + +We can break this statement into 3 pieces like so: + +.. list-table:: + :header-rows: 1 + + * - Subject + - Predicate + - Object + * - MyOsc + - has the name + - "My Super Oscillator" + +Statements use URIs to identify things. +In this case, we assume that this plugin has the URI ``http://example.org/Osc``. +The LV2 specification defines that ``http://usefulinc.com/ns/doap#name`` is the property used to describe a plugin's name. +So, this statement is: + +.. list-table:: + :header-rows: 1 + + * - Subject + - Predicate + - Object + * - ``http://example.org/Osc`` + - ``http://usefulinc.com/ns/doap#name`` + - "My Oscillator" + +Finding Values +============== + +Based on this model, you can find all values that match a certain pattern. +Patterns are just statements, +but with ``NULL`` used as a wildcard that matches anything. +So, for example, you can get the name of a plugin using :func:`lilv_world_find_nodes`: + +.. code-block:: c + + LilvNode* plugin_uri = lilv_new_uri(world, "http://example.org/Osc"); + LilvNode* doap_name = lilv_new_uri(world, "http://usefulinc.com/ns/doap#name"); + + LilvNodes* values = lilv_world_find_nodes(world, plugin_uri, doap_name, NULL); + +Note that a set of values is returned, +because some properties may have several values. +When you are only interested in one value, +you can use the simpler :func:`lilv_world_get` instead: + +.. code-block:: c + + LilvNode* value = lilv_world_get(world, plugin_uri, doap_name, NULL); + +If you are only interested if a value exists at all, +use :func:`lilv_world_ask`: + +.. code-block:: c + + bool has_name = lilv_world_ask(world, plugin_uri, doap_name, NULL); diff --git a/doc/xml/meson.build b/doc/xml/meson.build new file mode 100644 index 0000000..dc234f3 --- /dev/null +++ b/doc/xml/meson.build @@ -0,0 +1,21 @@ +# Copyright 2021-2022 David Robillard +# SPDX-License-Identifier: 0BSD OR ISC + +doxygen = find_program('doxygen') + +config = configuration_data() +config.set('LILV_SRCDIR', lilv_src_root) +config.set('DOX_OUTPUT', meson.current_build_dir() / '..') + +c_doxyfile = configure_file( + configuration: config, + input: files('../Doxyfile.in'), + output: 'Doxyfile', +) + +c_index_xml = custom_target( + 'index.xml', + command: [doxygen, '@INPUT0@'], + input: [c_doxyfile] + c_headers, + output: 'index.xml', +) -- cgit v1.2.1