diff options
| author | David Robillard <d@drobilla.net> | 2025-11-13 17:55:18 -0500 |
|---|---|---|
| committer | David Robillard <d@drobilla.net> | 2025-11-13 19:59:41 -0500 |
| commit | cbe7327ff36ca6b142c43c0e0491b989726ecc9b (patch) | |
| tree | acc598ee074a58152cc91953c272be728cc8c9c6 | |
| parent | c664e354487332ba5f204c62a4a5df41b2cbda1f (diff) | |
| download | lv2-cbe7327ff36ca6b142c43c0e0491b989726ecc9b.tar.xz | |
Remove example plugins
These are now maintained in the separate "lv2-examples" project to keep the
dependencies of lv2 itself minimal.
54 files changed, 8 insertions, 5801 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a67a043..a42bbeb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -86,7 +86,7 @@ mingw64: wasm: image: lv2plugin/debian-wasm script: - - meson setup build --cross-file=/usr/share/meson/cross/wasm.ini -Dbuildtype=debug -Dwarning_level=3 -Dwerror=true -Ddefault_library=static -Ddocs=disabled -Dplugins=disabled + - 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 diff --git a/.reuse/dep5 b/.reuse/dep5 index 580c2fb..5f805a7 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -26,11 +26,7 @@ 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 2013 Robin Gareus <robin@gareus.org> Copyright 2006 Steve Harris <steve@plugin.org.uk> @@ -5,12 +5,13 @@ lv2 (1.18.11) unstable; urgency=medium * 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:51:44 +0000 + -- David Robillard <d@drobilla.net> Thu, 13 Nov 2025 22:54:05 +0000 lv2 (1.18.10) stable; urgency=medium diff --git a/meson.build b/meson.build index c63c43c..572d19e 100644 --- a/meson.build +++ b/meson.build @@ -47,22 +47,15 @@ c_suppressions = [] if cc.get_id() in ['clang', 'emscripten'] if warning_level == 'everything' c_suppressions += [ - '-Wno-bad-function-cast', '-Wno-cast-align', - '-Wno-cast-function-type-strict', '-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-default', - '-Wno-switch-enum', '-Wno-unsafe-buffer-usage', ] @@ -82,10 +75,8 @@ if cc.get_id() in ['clang', 'emscripten'] elif cc.get_id() == 'gcc' if warning_level == 'everything' c_suppressions += [ - '-Wno-bad-function-cast', '-Wno-cast-align', '-Wno-cast-qual', - '-Wno-conversion', '-Wno-double-promotion', '-Wno-float-equal', '-Wno-inline', @@ -93,14 +84,15 @@ elif cc.get_id() == 'gcc' '-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', ] if target_machine.system() == 'windows' - c_suppressions += ['-Wno-suggest-attribute=format'] + c_suppressions += [ + '-Wno-sign-conversion', + '-Wno-suggest-attribute=format', + ] endif endif @@ -111,14 +103,7 @@ elif cc.get_id() == 'gcc' elif cc.get_id() == 'msvc' if warning_level == 'everything' c_suppressions += [ - '/wd4061', # enumerator in switch is not explicitly handled - '/wd4244', # conversion with possible loss of data - '/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 @@ -183,12 +168,10 @@ if is_variable('cpp') 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 - '/wd5264', # const variable is not used ] endif endif @@ -227,7 +210,6 @@ if get_option('bundles') or get_option('headers') or get_option('old_headers') variables: [ 'lv2dir=' + lv2dir, 'lv2specdatadir=' + lv2dir, - 'plugindir=' + lv2dir, ], version: meson.project_version(), ) @@ -239,7 +221,6 @@ lv2_dep = declare_dependency( variables: [ 'lv2dir=' + lv2dir, 'lv2specdatadir=' + lv2_source_root / 'lv2', - 'plugindir=' + lv2_build_root / 'plugins', ], version: meson.project_version(), ) @@ -498,15 +479,6 @@ if build_docs subdir('doc/ns') endif -########### -# Plugins # -########### - -# Example plugins and "Programming LV2 Plugins" book -if not get_option('plugins').disabled() - subdir('plugins') -endif - ############ # Programs # ############ diff --git a/meson_options.txt b/meson_options.txt index 4b12f68..365f916 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -22,9 +22,6 @@ option('old_headers', type: 'boolean', option('online_docs', type: 'boolean', value: false, description: 'Build documentation for online hosting') -option('plugins', type: 'feature', - description: 'Build example plugins') - option('tests', type: 'feature', description: 'Build tests') diff --git a/plugins/.clang-tidy b/plugins/.clang-tidy deleted file mode 100644 index b327b36..0000000 --- a/plugins/.clang-tidy +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2020-2025 David Robillard <d@drobilla.net> -# SPDX-License-Identifier: 0BSD OR ISC - -Checks: > - -*-narrowing-conversions, - -bugprone-assignment-in-if-condition, - -bugprone-easily-swappable-parameters, - -bugprone-multi-level-implicit-pointer-conversion, - -bugprone-suspicious-realloc-usage, - -cert-err33-c, - -clang-analyzer-core.NullDereference, - -hicpp-signed-bitwise, - -llvm-header-guard, - -misc-unused-parameters, - -readability-function-cognitive-complexity, -InheritParentConfig: true 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 c85d6ab..0000000 --- a/plugins/eg-amp.lv2/amp.c +++ /dev/null @@ -1,203 +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 2912e3f..0000000 --- a/plugins/eg-amp.lv2/meson.build +++ /dev/null @@ -1,42 +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', - implicit_include_directories: false, - 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 22d5f8a..0000000 --- a/plugins/eg-fifths.lv2/fifths.c +++ /dev/null @@ -1,167 +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; - const 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 d0cbc6a..0000000 --- a/plugins/eg-fifths.lv2/meson.build +++ /dev/null @@ -1,42 +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', - implicit_include_directories: false, - 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 f32a6bd..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 ee4016e..0000000 --- a/plugins/eg-metro.lv2/meson.build +++ /dev/null @@ -1,42 +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', - implicit_include_directories: false, - 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 8e4c738..0000000 --- a/plugins/eg-metro.lv2/metro.c +++ /dev/null @@ -1,341 +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) -{ - Metro* self = (Metro*)instance; - free(self->wave); - free(self); -} - -/** - 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); - const float attack_den = self->attack_len ? (float)self->attack_len : 1.0f; - - 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 / attack_den; - 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 fc6010b..0000000 --- a/plugins/eg-midigate.lv2/meson.build +++ /dev/null @@ -1,42 +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', - implicit_include_directories: false, - 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 b4861db..0000000 --- a/plugins/eg-midigate.lv2/midigate.c +++ /dev/null @@ -1,220 +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 83c6ef9..0000000 --- a/plugins/eg-params.lv2/meson.build +++ /dev/null @@ -1,42 +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', - implicit_include_directories: false, - 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 9fbaa46..0000000 --- a/plugins/eg-params.lv2/params.c +++ /dev/null @@ -1,519 +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) { - const 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 c81ea29..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; // NOLINT(cppcoreguidelines-init-variables) - 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 3319767..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 a0f8799..0000000 --- a/plugins/eg-sampler.lv2/meson.build +++ /dev/null @@ -1,82 +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', - include_type: 'system', - required: get_option('plugins'), - version: '>= 0.1.0', -) - -sndfile_dep = dependency( - 'sndfile', - include_type: 'system', - required: get_option('plugins'), - version: '>= 1.0.0', -) - -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', - implicit_include_directories: false, - 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', - implicit_include_directories: false, - 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 ff91546..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 6fc04c5..0000000 --- a/plugins/eg-sampler.lv2/sampler.c +++ /dev/null @@ -1,695 +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; - const 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 769dde0..0000000 --- a/plugins/eg-sampler.lv2/sampler_ui.c +++ /dev/null @@ -1,458 +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) -{ - const SamplerUI* ui = (const 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 9922f51..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*)&value[1]; -} - -#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 5cdb610..0000000 --- a/plugins/eg-scope.lv2/examploscope.c +++ /dev/null @@ -1,412 +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, - const 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 4f366aa..0000000 --- a/plugins/eg-scope.lv2/examploscope_ui.c +++ /dev/null @@ -1,667 +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); - - lv2_atom_forge_set_buffer(&ui->forge, NULL, 0); -} - -/** 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); - - lv2_atom_forge_set_buffer(&ui->forge, NULL, 0); -} - -/** - 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) -{ - const EgScopeUI* ui = (const 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); - assert(ui->n_channels <= 2U); - - 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(const 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 84e17ba..0000000 --- a/plugins/eg-scope.lv2/meson.build +++ /dev/null @@ -1,64 +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', - implicit_include_directories: false, - 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', - implicit_include_directories: false, - 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 d9d94be..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 reuse 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 ff70af1..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') - book_inputs += files( - '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/test/meson.build b/test/meson.build index 5b6a41c..c4a483d 100644 --- a/test/meson.build +++ b/test/meson.build @@ -32,7 +32,6 @@ if get_option('lint') lv2_source_root / 'doc', lv2_source_root / 'lv2', lv2_source_root / 'lv2specgen', - lv2_source_root / 'plugins', lv2_source_root / 'schemas.lv2', ], suite: 'data', @@ -109,7 +108,6 @@ if get_option('lint') python_scripts = lv2_scripts + files( '../lv2specgen/lv2docgen.py', '../lv2specgen/lv2specgen.py', - '../plugins/literasc.py', ) # Check script formatting |