aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/.clang-tidy14
-rw-r--r--plugins/eg-amp.lv2/amp.c134
-rw-r--r--plugins/eg-amp.lv2/meson.build41
l---------plugins/eg-amp.lv2/waf1
-rw-r--r--plugins/eg-amp.lv2/wscript51
-rw-r--r--plugins/eg-fifths.lv2/fifths.c255
-rw-r--r--plugins/eg-fifths.lv2/meson.build41
-rw-r--r--plugins/eg-fifths.lv2/uris.h59
l---------plugins/eg-fifths.lv2/waf1
-rw-r--r--plugins/eg-fifths.lv2/wscript49
-rw-r--r--plugins/eg-metro.lv2/meson.build41
-rw-r--r--plugins/eg-metro.lv2/metro.c476
l---------plugins/eg-metro.lv2/waf1
-rw-r--r--plugins/eg-metro.lv2/wscript50
-rw-r--r--plugins/eg-midigate.lv2/meson.build41
-rw-r--r--plugins/eg-midigate.lv2/midigate.c243
l---------plugins/eg-midigate.lv2/waf1
-rw-r--r--plugins/eg-midigate.lv2/wscript49
-rw-r--r--plugins/eg-params.lv2/meson.build41
-rw-r--r--plugins/eg-params.lv2/params.c710
-rw-r--r--plugins/eg-params.lv2/state_map.h98
-rw-r--r--plugins/eg-params.lv2/wscript49
-rw-r--r--plugins/eg-sampler.lv2/atom_sink.h34
-rw-r--r--plugins/eg-sampler.lv2/manifest.ttl.in2
-rw-r--r--plugins/eg-sampler.lv2/meson.build78
-rw-r--r--plugins/eg-sampler.lv2/peaks.h317
-rw-r--r--plugins/eg-sampler.lv2/sampler.c957
-rw-r--r--plugins/eg-sampler.lv2/sampler.ttl2
-rw-r--r--plugins/eg-sampler.lv2/sampler_ui.c608
-rw-r--r--plugins/eg-sampler.lv2/uris.h212
l---------plugins/eg-sampler.lv2/waf1
-rw-r--r--plugins/eg-sampler.lv2/wscript64
-rw-r--r--plugins/eg-scope.lv2/examploscope.c590
-rw-r--r--plugins/eg-scope.lv2/examploscope_ui.c995
-rw-r--r--plugins/eg-scope.lv2/manifest.ttl.in2
-rw-r--r--plugins/eg-scope.lv2/meson.build62
-rw-r--r--plugins/eg-scope.lv2/uris.h92
-rw-r--r--plugins/eg-scope.lv2/wscript56
-rwxr-xr-xplugins/literasc.py158
-rw-r--r--plugins/meson.build82
-rw-r--r--plugins/wscript45
41 files changed, 3388 insertions, 3415 deletions
diff --git a/plugins/.clang-tidy b/plugins/.clang-tidy
new file mode 100644
index 0000000..5fc86ab
--- /dev/null
+++ b/plugins/.clang-tidy
@@ -0,0 +1,14 @@
+# Copyright 2020-2023 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+Checks: >
+ -*-narrowing-conversions,
+ -bugprone-assignment-in-if-condition,
+ -bugprone-easily-swappable-parameters,
+ -bugprone-suspicious-realloc-usage,
+ -cert-err33-c,
+ -hicpp-signed-bitwise,
+ -llvm-header-guard,
+ -misc-unused-parameters,
+ -readability-function-cognitive-complexity,
+InheritParentConfig: true
diff --git a/plugins/eg-amp.lv2/amp.c b/plugins/eg-amp.lv2/amp.c
index c3ba279..90a5769 100644
--- a/plugins/eg-amp.lv2/amp.c
+++ b/plugins/eg-amp.lv2/amp.c
@@ -1,19 +1,6 @@
-/*
- Copyright 2006-2016 David Robillard <d@drobilla.net>
- Copyright 2006 Steve Harris <steve@plugin.org.uk>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// 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
@@ -42,11 +29,7 @@
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;
+typedef enum { AMP_GAIN = 0, AMP_INPUT = 1, AMP_OUTPUT = 2 } PortIndex;
/**
Every plugin defines a private structure for the plugin instance. All data
@@ -55,10 +38,10 @@ typedef enum {
stored, since there is no additional instance data.
*/
typedef struct {
- // Port buffers
- const float* gain;
- const float* input;
- float* output;
+ // Port buffers
+ const float* gain;
+ const float* input;
+ float* output;
} Amp;
/**
@@ -77,9 +60,9 @@ instantiate(const LV2_Descriptor* descriptor,
const char* bundle_path,
const LV2_Feature* const* features)
{
- Amp* amp = (Amp*)calloc(1, sizeof(Amp));
+ Amp* amp = (Amp*)calloc(1, sizeof(Amp));
- return (LV2_Handle)amp;
+ return (LV2_Handle)amp;
}
/**
@@ -91,23 +74,21 @@ instantiate(const LV2_Descriptor* descriptor,
context as run().
*/
static void
-connect_port(LV2_Handle instance,
- uint32_t port,
- void* data)
+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;
- }
+ 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;
+ }
}
/**
@@ -121,11 +102,10 @@ connect_port(LV2_Handle instance,
*/
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)
+#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
@@ -136,34 +116,33 @@ activate(LV2_Handle instance)
static void
run(LV2_Handle instance, uint32_t n_samples)
{
- const Amp* amp = (const Amp*)instance;
+ const Amp* amp = (const Amp*)instance;
- const float gain = *(amp->gain);
- const float* const input = amp->input;
- float* const output = amp->output;
+ const float gain = *(amp->gain);
+ const float* const input = amp->input;
+ float* const output = amp->output;
- const float coef = DB_CO(gain);
+ const float coef = DB_CO(gain);
- for (uint32_t pos = 0; pos < n_samples; pos++) {
- output[pos] = input[pos] * coef;
- }
+ 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.
+ 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()`).
@@ -174,7 +153,7 @@ deactivate(LV2_Handle instance)
static void
cleanup(LV2_Handle instance)
{
- free(instance);
+ free(instance);
}
/**
@@ -190,7 +169,7 @@ cleanup(LV2_Handle instance)
static const void*
extension_data(const char* uri)
{
- return NULL;
+ return NULL;
}
/**
@@ -198,22 +177,20 @@ extension_data(const char* uri)
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
-};
+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
+ The `lv2_descriptor()` function is the entry point to the plugin library. The
host will load the library and call this function repeatedly with increasing
indices to find all the plugins defined in the library. The index is not an
- indentifier, the URI of the returned descriptor is used to determine the
+ 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
@@ -223,8 +200,5 @@ LV2_SYMBOL_EXPORT
const LV2_Descriptor*
lv2_descriptor(uint32_t index)
{
- switch (index) {
- case 0: return &descriptor;
- default: return NULL;
- }
+ return index == 0 ? &descriptor : NULL;
}
diff --git a/plugins/eg-amp.lv2/meson.build b/plugins/eg-amp.lv2/meson.build
new file mode 100644
index 0000000..726038e
--- /dev/null
+++ b/plugins/eg-amp.lv2/meson.build
@@ -0,0 +1,41 @@
+# Copyright 2022 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+plugin_sources = files('amp.c')
+bundle_name = 'eg-amp.lv2'
+data_filenames = ['manifest.ttl.in', 'amp.ttl']
+
+module = shared_library(
+ 'amp',
+ plugin_sources,
+ c_args: c_suppressions,
+ dependencies: [lv2_dep, m_dep],
+ gnu_symbol_visibility: 'hidden',
+ install: true,
+ install_dir: lv2dir / bundle_name,
+ name_prefix: '',
+)
+
+config = configuration_data(
+ {
+ 'LIB_EXT': '.' + module.full_path().split('.')[-1],
+ },
+)
+
+foreach filename : data_filenames
+ if filename.endswith('.in')
+ configure_file(
+ configuration: config,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename.substring(0, -3),
+ )
+ else
+ configure_file(
+ copy: true,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename,
+ )
+ endif
+endforeach
diff --git a/plugins/eg-amp.lv2/waf b/plugins/eg-amp.lv2/waf
deleted file mode 120000
index 59a1ac9..0000000
--- a/plugins/eg-amp.lv2/waf
+++ /dev/null
@@ -1 +0,0 @@
-../../waf \ No newline at end of file
diff --git a/plugins/eg-amp.lv2/wscript b/plugins/eg-amp.lv2/wscript
deleted file mode 100644
index 822825d..0000000
--- a/plugins/eg-amp.lv2/wscript
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-amp.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
- opt.load('compiler_c')
- opt.load('lv2')
- autowaf.set_options(opt)
-
-def configure(conf):
- conf.load('compiler_c', cache=True)
- conf.load('lv2', cache=True)
- conf.load('autowaf', cache=True)
-
- conf.check_pkg('lv2', uselib_store='LV2')
-
- conf.check(features='c cshlib', lib='m', uselib_store='M', mandatory=False)
-
-def build(bld):
- bundle = 'eg-amp.lv2'
-
- # Build manifest.ttl by substitution (for portable lib extension)
- bld(features = 'subst',
- source = 'manifest.ttl.in',
- target = 'lv2/%s/%s' % (bundle, 'manifest.ttl'),
- install_path = '${LV2DIR}/%s' % bundle,
- LIB_EXT = bld.env.LV2_LIB_EXT)
-
- # Copy other data files to build bundle (build/eg-amp.lv2)
- for i in ['amp.ttl']:
- bld(features = 'subst',
- is_copy = True,
- source = i,
- target = 'lv2/%s/%s' % (bundle, i),
- install_path = '${LV2DIR}/%s' % bundle)
-
- # Build plugin library
- obj = bld(features = 'c cshlib lv2lib',
- source = 'amp.c',
- name = 'amp',
- target = 'lv2/%s/amp' % bundle,
- install_path = '${LV2DIR}/%s' % bundle,
- uselib = 'M LV2')
diff --git a/plugins/eg-fifths.lv2/fifths.c b/plugins/eg-fifths.lv2/fifths.c
index 5782417..9f6a388 100644
--- a/plugins/eg-fifths.lv2/fifths.c
+++ b/plugins/eg-fifths.lv2/fifths.c
@@ -1,19 +1,5 @@
-/*
- LV2 Fifths Example Plugin
- Copyright 2014-2016 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// Copyright 2014-2016 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
#include "./uris.h"
@@ -31,40 +17,35 @@
#include <stdio.h>
#include <stdlib.h>
-enum {
- FIFTHS_IN = 0,
- FIFTHS_OUT = 1
-};
+enum { FIFTHS_IN = 0, FIFTHS_OUT = 1 };
typedef struct {
- // Features
- LV2_URID_Map* map;
- LV2_Log_Logger logger;
+ // Features
+ LV2_URID_Map* map;
+ LV2_Log_Logger logger;
- // Ports
- const LV2_Atom_Sequence* in_port;
- LV2_Atom_Sequence* out_port;
+ // Ports
+ const LV2_Atom_Sequence* in_port;
+ LV2_Atom_Sequence* out_port;
- // URIs
- FifthsURIs uris;
+ // URIs
+ FifthsURIs uris;
} Fifths;
static void
-connect_port(LV2_Handle instance,
- uint32_t port,
- void* data)
+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;
- }
+ 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
@@ -73,121 +54,115 @@ instantiate(const LV2_Descriptor* descriptor,
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
- const char* missing = lv2_features_query(
- features,
- LV2_LOG__log, &self->logger.log, false,
- LV2_URID__map, &self->map, true,
- NULL);
- 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;
+ // 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);
+ free(instance);
}
static void
-run(LV2_Handle instance,
- uint32_t sample_count)
+run(LV2_Handle instance, uint32_t sample_count)
{
- Fifths* self = (Fifths*)instance;
- FifthsURIs* uris = &self->uris;
-
- // Struct for a 3 byte MIDI event, used for writing notes
- typedef struct {
- LV2_Atom_Event event;
- uint8_t msg[3];
- } MIDINoteEvent;
-
- // Initially self->out_port contains a Chunk with size set to capacity
-
- // Get the capacity
- const uint32_t out_capacity = self->out_port->atom.size;
-
- // Write an empty Sequence header to the output
- lv2_atom_sequence_clear(self->out_port);
- self->out_port->atom.type = self->in_port->atom.type;
-
- // Read incoming events
- LV2_ATOM_SEQUENCE_FOREACH(self->in_port, ev) {
- if (ev->body.type == uris->midi_Event) {
- const uint8_t* const msg = (const uint8_t*)LV2_ATOM_BODY_CONST(ev);
- 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;
- }
- }
- }
+ Fifths* self = (Fifths*)instance;
+ FifthsURIs* uris = &self->uris;
+
+ // Struct for a 3 byte MIDI event, used for writing notes
+ typedef struct {
+ LV2_Atom_Event event;
+ uint8_t msg[3];
+ } MIDINoteEvent;
+
+ // Initially self->out_port contains a Chunk with size set to capacity
+
+ // Get the capacity
+ const uint32_t out_capacity = self->out_port->atom.size;
+
+ // Write an empty Sequence header to the output
+ lv2_atom_sequence_clear(self->out_port);
+ self->out_port->atom.type = self->in_port->atom.type;
+
+ // Read incoming events
+ LV2_ATOM_SEQUENCE_FOREACH (self->in_port, ev) {
+ if (ev->body.type == uris->midi_Event) {
+ const uint8_t* const msg = (const uint8_t*)(ev + 1);
+ switch (lv2_midi_message_type(msg)) {
+ case LV2_MIDI_MSG_NOTE_ON:
+ case LV2_MIDI_MSG_NOTE_OFF:
+ // Forward note to output
+ lv2_atom_sequence_append_event(self->out_port, out_capacity, ev);
+
+ if (msg[1] <= 127 - 7) {
+ // Make a note one 5th (7 semitones) higher than input
+ MIDINoteEvent fifth;
+
+ // Could simply do fifth.event = *ev here instead...
+ fifth.event.time.frames = ev->time.frames; // Same time
+ fifth.event.body.type = ev->body.type; // Same type
+ fifth.event.body.size = ev->body.size; // Same size
+
+ fifth.msg[0] = msg[0]; // Same status
+ fifth.msg[1] = msg[1] + 7; // Pitch up 7 semitones
+ fifth.msg[2] = msg[2]; // Same velocity
+
+ // Write 5th event
+ lv2_atom_sequence_append_event(
+ self->out_port, out_capacity, &fifth.event);
+ }
+ break;
+ default:
+ // Forward all other MIDI events directly
+ lv2_atom_sequence_append_event(self->out_port, out_capacity, ev);
+ break;
+ }
+ }
+ }
}
static const void*
extension_data(const char* uri)
{
- return NULL;
+ return NULL;
}
-static const LV2_Descriptor descriptor = {
- EG_FIFTHS_URI,
- instantiate,
- connect_port,
- NULL, // activate,
- run,
- NULL, // deactivate,
- cleanup,
- extension_data
-};
+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)
+const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
{
- switch (index) {
- case 0:
- return &descriptor;
- default:
- return NULL;
- }
+ return index == 0 ? &descriptor : NULL;
}
diff --git a/plugins/eg-fifths.lv2/meson.build b/plugins/eg-fifths.lv2/meson.build
new file mode 100644
index 0000000..bba21ce
--- /dev/null
+++ b/plugins/eg-fifths.lv2/meson.build
@@ -0,0 +1,41 @@
+# Copyright 2022 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+plugin_sources = files('fifths.c')
+bundle_name = 'eg-fifths.lv2'
+data_filenames = ['manifest.ttl.in', 'fifths.ttl']
+
+module = shared_library(
+ 'fifths',
+ plugin_sources,
+ c_args: c_suppressions,
+ dependencies: [lv2_dep, m_dep],
+ gnu_symbol_visibility: 'hidden',
+ install: true,
+ install_dir: lv2dir / bundle_name,
+ name_prefix: '',
+)
+
+config = configuration_data(
+ {
+ 'LIB_EXT': '.' + module.full_path().split('.')[-1],
+ },
+)
+
+foreach filename : data_filenames
+ if filename.endswith('.in')
+ configure_file(
+ configuration: config,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename.substring(0, -3),
+ )
+ else
+ configure_file(
+ copy: true,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename,
+ )
+ endif
+endforeach
diff --git a/plugins/eg-fifths.lv2/uris.h b/plugins/eg-fifths.lv2/uris.h
index 04b09f6..7cbbd94 100644
--- a/plugins/eg-fifths.lv2/uris.h
+++ b/plugins/eg-fifths.lv2/uris.h
@@ -1,55 +1,40 @@
-/*
- LV2 Fifths Example Plugin
- Copyright 2014-2015 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// 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/log/log.h"
#include "lv2/midi/midi.h"
#include "lv2/patch/patch.h"
-#include "lv2/state/state.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;
+ 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);
+ 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 */
+#endif // FIFTHS_URIS_H
diff --git a/plugins/eg-fifths.lv2/waf b/plugins/eg-fifths.lv2/waf
deleted file mode 120000
index 59a1ac9..0000000
--- a/plugins/eg-fifths.lv2/waf
+++ /dev/null
@@ -1 +0,0 @@
-../../waf \ No newline at end of file
diff --git a/plugins/eg-fifths.lv2/wscript b/plugins/eg-fifths.lv2/wscript
deleted file mode 100644
index 8b2991b..0000000
--- a/plugins/eg-fifths.lv2/wscript
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-fifths.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
- opt.load('compiler_c')
- opt.load('lv2')
- autowaf.set_options(opt)
-
-def configure(conf):
- conf.load('compiler_c', cache=True)
- conf.load('lv2', cache=True)
- conf.load('autowaf', cache=True)
-
- conf.check_pkg('lv2 >= 1.2.1', uselib_store='LV2')
-
-def build(bld):
- bundle = 'eg-fifths.lv2'
-
- # Build manifest.ttl by substitution (for portable lib extension)
- bld(features = 'subst',
- source = 'manifest.ttl.in',
- target = 'lv2/%s/%s' % (bundle, 'manifest.ttl'),
- install_path = '${LV2DIR}/%s' % bundle,
- LIB_EXT = bld.env.LV2_LIB_EXT)
-
- # Copy other data files to build bundle (build/eg-fifths.lv2)
- for i in ['fifths.ttl']:
- bld(features = 'subst',
- is_copy = True,
- source = i,
- target = 'lv2/%s/%s' % (bundle, i),
- install_path = '${LV2DIR}/%s' % bundle)
-
- # Build plugin library
- obj = bld(features = 'c cshlib lv2lib',
- source = 'fifths.c',
- name = 'fifths',
- target = 'lv2/%s/fifths' % bundle,
- install_path = '${LV2DIR}/%s' % bundle,
- use = 'LV2')
diff --git a/plugins/eg-metro.lv2/meson.build b/plugins/eg-metro.lv2/meson.build
new file mode 100644
index 0000000..5d83c9e
--- /dev/null
+++ b/plugins/eg-metro.lv2/meson.build
@@ -0,0 +1,41 @@
+# Copyright 2022 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+plugin_sources = files('metro.c')
+bundle_name = 'eg-metro.lv2'
+data_filenames = ['manifest.ttl.in', 'metro.ttl']
+
+module = shared_library(
+ 'metro',
+ plugin_sources,
+ c_args: c_suppressions,
+ dependencies: [lv2_dep, m_dep],
+ gnu_symbol_visibility: 'hidden',
+ install: true,
+ install_dir: lv2dir / bundle_name,
+ name_prefix: '',
+)
+
+config = configuration_data(
+ {
+ 'LIB_EXT': '.' + module.full_path().split('.')[-1],
+ },
+)
+
+foreach filename : data_filenames
+ if filename.endswith('.in')
+ configure_file(
+ configuration: config,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename.substring(0, -3),
+ )
+ else
+ configure_file(
+ copy: true,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename,
+ )
+ endif
+endforeach
diff --git a/plugins/eg-metro.lv2/metro.c b/plugins/eg-metro.lv2/metro.c
index a7231d2..f3fe164 100644
--- a/plugins/eg-metro.lv2/metro.c
+++ b/plugins/eg-metro.lv2/metro.c
@@ -1,19 +1,5 @@
-/*
- LV2 Metronome Example Plugin
- Copyright 2012-2016 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// Copyright 2012-2016 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
#include "lv2/atom/atom.h"
#include "lv2/atom/util.h"
@@ -32,37 +18,34 @@
#include <string.h>
#ifndef M_PI
-# define M_PI 3.14159265
+# 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;
+ 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
-};
+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_ATTACK, // Envelope rising
+ STATE_DECAY, // Envelope lowering
+ STATE_OFF // Silent
} State;
/**
@@ -77,50 +60,48 @@ typedef enum {
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;
+ 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)
+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;
- }
+ 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;
+ }
}
/**
@@ -130,11 +111,11 @@ connect_port(LV2_Handle instance,
static void
activate(LV2_Handle instance)
{
- Metro* self = (Metro*)instance;
+ Metro* self = (Metro*)instance;
- self->elapsed_len = 0;
- self->wave_offset = 0;
- self->state = STATE_OFF;
+ self->elapsed_len = 0;
+ self->wave_offset = 0;
+ self->state = STATE_OFF;
}
/**
@@ -149,61 +130,64 @@ instantiate(const LV2_Descriptor* descriptor,
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
- const char* missing = lv2_features_query(
- features,
- LV2_LOG__log, &self->logger.log, false,
- LV2_URID__map, &self->map, true,
- NULL);
- 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;
+ Metro* self = (Metro*)calloc(1, sizeof(Metro));
+ if (!self) {
+ return NULL;
+ }
+
+ // Scan host features for URID map
+ // clang-format off
+ const char* missing = lv2_features_query(
+ features,
+ LV2_LOG__log, &self->logger.log, false,
+ LV2_URID__map, &self->map, true,
+ NULL);
+ // clang-format on
+
+ lv2_log_logger_set_map(&self->logger, self->map);
+ if (missing) {
+ lv2_log_error(&self->logger, "Missing feature <%s>\n", missing);
+ free(self);
+ return NULL;
+ }
+
+ // Map URIS
+ MetroURIs* const uris = &self->uris;
+ LV2_URID_Map* const map = self->map;
+ uris->atom_Blank = map->map(map->handle, LV2_ATOM__Blank);
+ uris->atom_Float = map->map(map->handle, LV2_ATOM__Float);
+ uris->atom_Object = map->map(map->handle, LV2_ATOM__Object);
+ uris->atom_Path = map->map(map->handle, LV2_ATOM__Path);
+ uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource);
+ uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence);
+ uris->time_Position = map->map(map->handle, LV2_TIME__Position);
+ uris->time_barBeat = map->map(map->handle, LV2_TIME__barBeat);
+ uris->time_beatsPerMinute = map->map(map->handle, LV2_TIME__beatsPerMinute);
+ uris->time_speed = map->map(map->handle, LV2_TIME__speed);
+
+ // Initialise instance fields
+ self->rate = rate;
+ self->bpm = 120.0f;
+ self->attack_len = (uint32_t)(attack_s * rate);
+ self->decay_len = (uint32_t)(decay_s * rate);
+ self->state = STATE_OFF;
+
+ // Generate one cycle of a sine wave at the desired frequency
+ const double freq = 440.0 * 2.0;
+ const double amp = 0.5;
+ self->wave_len = (uint32_t)(rate / freq);
+ self->wave = (float*)malloc(self->wave_len * sizeof(float));
+ for (uint32_t i = 0; i < self->wave_len; ++i) {
+ self->wave[i] = (float)(sin(i * 2 * M_PI * freq / rate) * amp);
+ }
+
+ return (LV2_Handle)self;
}
static void
cleanup(LV2_Handle instance)
{
- free(instance);
+ free(instance);
}
/**
@@ -213,47 +197,47 @@ cleanup(LV2_Handle instance)
static void
play(Metro* self, uint32_t begin, uint32_t end)
{
- float* const output = self->ports.output;
- const uint32_t frames_per_beat = 60.0f / self->bpm * self->rate;
-
- if (self->speed == 0.0f) {
- memset(output, 0, (end - begin) * sizeof(float));
- return;
- }
-
- for (uint32_t i = begin; i < end; ++i) {
- switch (self->state) {
- case STATE_ATTACK:
- // Amplitude increases from 0..1 until attack_len
- output[i] = self->wave[self->wave_offset] *
- self->elapsed_len / (float)self->attack_len;
- if (self->elapsed_len >= self->attack_len) {
- self->state = STATE_DECAY;
- }
- break;
- case STATE_DECAY:
- // Amplitude decreases from 1..0 until attack_len + decay_len
- output[i] = 0.0f;
- output[i] = self->wave[self->wave_offset] *
- (1 - ((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;
- }
- }
+ float* const output = self->ports.output;
+ const uint32_t frames_per_beat = (uint32_t)(60.0f / self->bpm * self->rate);
+
+ if (self->speed == 0.0f) {
+ memset(output, 0, (end - begin) * sizeof(float));
+ return;
+ }
+
+ for (uint32_t i = begin; i < end; ++i) {
+ switch (self->state) {
+ case STATE_ATTACK:
+ // Amplitude increases from 0..1 until attack_len
+ output[i] = self->wave[self->wave_offset] * (float)self->elapsed_len /
+ (float)self->attack_len;
+ if (self->elapsed_len >= self->attack_len) {
+ self->state = STATE_DECAY;
+ }
+ break;
+ case STATE_DECAY:
+ // Amplitude decreases from 1..0 until attack_len + decay_len
+ output[i] = 0.0f;
+ output[i] = self->wave[self->wave_offset] *
+ (1 - ((float)(self->elapsed_len - self->attack_len) /
+ (float)self->decay_len));
+ if (self->elapsed_len >= self->attack_len + self->decay_len) {
+ self->state = STATE_OFF;
+ }
+ break;
+ case STATE_OFF:
+ output[i] = 0.0f;
+ }
+
+ // We continuously play the sine wave regardless of envelope
+ self->wave_offset = (self->wave_offset + 1) % self->wave_len;
+
+ // Update elapsed time and start attack if necessary
+ if (++self->elapsed_len == frames_per_beat) {
+ self->state = STATE_ATTACK;
+ self->elapsed_len = 0;
+ }
+ }
}
/**
@@ -263,93 +247,93 @@ play(Metro* self, uint32_t begin, uint32_t end)
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, *bpm = NULL, *speed = NULL;
- lv2_atom_object_get(obj,
- uris->time_barBeat, &beat,
- uris->time_beatsPerMinute, &bpm,
- uris->time_speed, &speed,
- NULL);
- 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 = 60.0f / 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 = 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;
- }
- }
+ 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, 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 = ev->time.frames;
- }
-
- // Play for remainder of cycle
- play(self, last_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
+ EG_METRO_URI,
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL, // extension_data
};
-LV2_SYMBOL_EXPORT const LV2_Descriptor*
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor*
lv2_descriptor(uint32_t index)
{
- switch (index) {
- case 0:
- return &descriptor;
- default:
- return NULL;
- }
+ return index == 0 ? &descriptor : NULL;
}
diff --git a/plugins/eg-metro.lv2/waf b/plugins/eg-metro.lv2/waf
deleted file mode 120000
index 59a1ac9..0000000
--- a/plugins/eg-metro.lv2/waf
+++ /dev/null
@@ -1 +0,0 @@
-../../waf \ No newline at end of file
diff --git a/plugins/eg-metro.lv2/wscript b/plugins/eg-metro.lv2/wscript
deleted file mode 100644
index 5fb0d07..0000000
--- a/plugins/eg-metro.lv2/wscript
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-metro.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
- opt.load('compiler_c')
- opt.load('lv2')
- autowaf.set_options(opt)
-
-def configure(conf):
- conf.load('compiler_c', cache=True)
- conf.load('lv2', cache=True)
- conf.load('autowaf', cache=True)
-
- conf.check_pkg('lv2 >= 0.2.0', uselib_store='LV2')
-
- conf.check(features='c cshlib', lib='m', uselib_store='M', mandatory=False)
-
-def build(bld):
- bundle = 'eg-metro.lv2'
-
- # Build manifest.ttl by substitution (for portable lib extension)
- bld(features = 'subst',
- source = 'manifest.ttl.in',
- target = 'lv2/%s/%s' % (bundle, 'manifest.ttl'),
- install_path = '${LV2DIR}/%s' % bundle,
- LIB_EXT = bld.env.LV2_LIB_EXT)
-
- # Copy other data files to build bundle (build/eg-metro.lv2)
- bld(features = 'subst',
- is_copy = True,
- source = 'metro.ttl',
- target = 'lv2/%s/metro.ttl' % bundle,
- install_path = '${LV2DIR}/%s' % bundle)
-
- # Build plugin library
- obj = bld(features = 'c cshlib lv2lib',
- source = 'metro.c',
- name = 'metro',
- target = 'lv2/%s/metro' % bundle,
- install_path = '${LV2DIR}/%s' % bundle,
- use = ['M', 'LV2'])
diff --git a/plugins/eg-midigate.lv2/meson.build b/plugins/eg-midigate.lv2/meson.build
new file mode 100644
index 0000000..9f0a5ac
--- /dev/null
+++ b/plugins/eg-midigate.lv2/meson.build
@@ -0,0 +1,41 @@
+# Copyright 2022 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+plugin_sources = files('midigate.c')
+bundle_name = 'eg-midigate.lv2'
+data_filenames = ['manifest.ttl.in', 'midigate.ttl']
+
+module = shared_library(
+ 'midigate',
+ plugin_sources,
+ c_args: c_suppressions,
+ dependencies: [lv2_dep, m_dep],
+ gnu_symbol_visibility: 'hidden',
+ install: true,
+ install_dir: lv2dir / bundle_name,
+ name_prefix: '',
+)
+
+config = configuration_data(
+ {
+ 'LIB_EXT': '.' + module.full_path().split('.')[-1],
+ },
+)
+
+foreach filename : data_filenames
+ if filename.endswith('.in')
+ configure_file(
+ configuration: config,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename.substring(0, -3),
+ )
+ else
+ configure_file(
+ copy: true,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename,
+ )
+ endif
+endforeach
diff --git a/plugins/eg-midigate.lv2/midigate.c b/plugins/eg-midigate.lv2/midigate.c
index 6c77828..db2fdea 100644
--- a/plugins/eg-midigate.lv2/midigate.c
+++ b/plugins/eg-midigate.lv2/midigate.c
@@ -1,18 +1,5 @@
-/*
- Copyright 2013-2016 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// Copyright 2013-2016 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
#include "lv2/atom/atom.h"
#include "lv2/atom/util.h"
@@ -32,27 +19,27 @@
#define MIDIGATE_URI "http://lv2plug.in/plugins/eg-midigate"
typedef enum {
- MIDIGATE_CONTROL = 0,
- MIDIGATE_IN = 1,
- MIDIGATE_OUT = 2
+ MIDIGATE_CONTROL = 0,
+ MIDIGATE_IN = 1,
+ MIDIGATE_OUT = 2
} PortIndex;
typedef struct {
- // Port buffers
- const LV2_Atom_Sequence* control;
- const float* in;
- float* out;
+ // Port buffers
+ const LV2_Atom_Sequence* control;
+ const float* in;
+ float* out;
- // Features
- LV2_URID_Map* map;
- LV2_Log_Logger logger;
+ // Features
+ LV2_URID_Map* map;
+ LV2_Log_Logger logger;
- struct {
- LV2_URID midi_MidiEvent;
- } uris;
+ struct {
+ LV2_URID midi_MidiEvent;
+ } uris;
- unsigned n_active_notes;
- unsigned program; // 0 = normal, 1 = inverted
+ unsigned n_active_notes;
+ unsigned program; // 0 = normal, 1 = inverted
} Midigate;
static LV2_Handle
@@ -61,56 +48,57 @@ instantiate(const LV2_Descriptor* descriptor,
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
- const char* missing = lv2_features_query(
- features,
- LV2_LOG__log, &self->logger.log, false,
- LV2_URID__map, &self->map, true,
- NULL);
- 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;
+ 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)
+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;
- }
+ 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;
+ Midigate* self = (Midigate*)instance;
+ self->n_active_notes = 0;
+ self->program = 0;
}
/**
@@ -121,14 +109,13 @@ activate(LV2_Handle instance)
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));
- }
+ 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));
+ }
}
/**
@@ -156,40 +143,41 @@ write_output(Midigate* self, uint32_t offset, uint32_t len)
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) {
- if (ev->body.type == self->uris.midi_MidiEvent) {
- const uint8_t* const msg = (const uint8_t*)LV2_ATOM_BODY_CONST(ev);
- 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, ev->time.frames - offset);
- offset = (uint32_t)ev->time.frames;
- }
-
- write_output(self, offset, sample_count - offset);
+ 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);
}
/**
@@ -204,7 +192,7 @@ deactivate(LV2_Handle instance)
static void
cleanup(LV2_Handle instance)
{
- free(instance);
+ free(instance);
}
/**
@@ -213,28 +201,21 @@ cleanup(LV2_Handle instance)
static const void*
extension_data(const char* uri)
{
- return NULL;
+ return NULL;
}
-static const LV2_Descriptor descriptor = {
- MIDIGATE_URI,
- instantiate,
- connect_port,
- activate,
- run,
- deactivate,
- cleanup,
- extension_data
-};
+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)
{
- switch (index) {
- case 0:
- return &descriptor;
- default:
- return NULL;
- }
+ return index == 0 ? &descriptor : NULL;
}
diff --git a/plugins/eg-midigate.lv2/waf b/plugins/eg-midigate.lv2/waf
deleted file mode 120000
index 59a1ac9..0000000
--- a/plugins/eg-midigate.lv2/waf
+++ /dev/null
@@ -1 +0,0 @@
-../../waf \ No newline at end of file
diff --git a/plugins/eg-midigate.lv2/wscript b/plugins/eg-midigate.lv2/wscript
deleted file mode 100644
index 5862721..0000000
--- a/plugins/eg-midigate.lv2/wscript
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-midigate.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
- opt.load('compiler_c')
- opt.load('lv2')
- autowaf.set_options(opt)
-
-def configure(conf):
- conf.load('compiler_c', cache=True)
- conf.load('lv2', cache=True)
- conf.load('autowaf', cache=True)
-
- conf.check_pkg('lv2', uselib_store='LV2')
-
-def build(bld):
- bundle = 'eg-midigate.lv2'
-
- # Build manifest.ttl by substitution (for portable lib extension)
- bld(features = 'subst',
- source = 'manifest.ttl.in',
- target = 'lv2/%s/%s' % (bundle, 'manifest.ttl'),
- install_path = '${LV2DIR}/%s' % bundle,
- LIB_EXT = bld.env.LV2_LIB_EXT)
-
- # Copy other data files to build bundle (build/eg-midigate.lv2)
- for i in ['midigate.ttl']:
- bld(features = 'subst',
- is_copy = True,
- source = i,
- target = 'lv2/%s/%s' % (bundle, i),
- install_path = '${LV2DIR}/%s' % bundle)
-
- # Build plugin library
- obj = bld(features = 'c cshlib lv2lib',
- source = 'midigate.c',
- name = 'midigate',
- target = 'lv2/%s/midigate' % bundle,
- install_path = '${LV2DIR}/%s' % bundle,
- uselib = 'LV2')
diff --git a/plugins/eg-params.lv2/meson.build b/plugins/eg-params.lv2/meson.build
new file mode 100644
index 0000000..00602bc
--- /dev/null
+++ b/plugins/eg-params.lv2/meson.build
@@ -0,0 +1,41 @@
+# Copyright 2022 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+plugin_sources = files('params.c')
+bundle_name = 'eg-params.lv2'
+data_filenames = ['manifest.ttl.in', 'params.ttl']
+
+module = shared_library(
+ 'params',
+ plugin_sources,
+ c_args: c_suppressions,
+ dependencies: [lv2_dep, m_dep],
+ gnu_symbol_visibility: 'hidden',
+ install: true,
+ install_dir: lv2dir / bundle_name,
+ name_prefix: '',
+)
+
+config = configuration_data(
+ {
+ 'LIB_EXT': '.' + module.full_path().split('.')[-1],
+ },
+)
+
+foreach filename : data_filenames
+ if filename.endswith('.in')
+ configure_file(
+ configuration: config,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename.substring(0, -3),
+ )
+ else
+ configure_file(
+ copy: true,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename,
+ )
+ endif
+endforeach
diff --git a/plugins/eg-params.lv2/params.c b/plugins/eg-params.lv2/params.c
index 94d34a5..af96fc8 100644
--- a/plugins/eg-params.lv2/params.c
+++ b/plugins/eg-params.lv2/params.c
@@ -1,19 +1,5 @@
-/*
- LV2 Parameter Example Plugin
- Copyright 2014-2016 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// Copyright 2014-2016 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
#include "state_map.h"
@@ -37,106 +23,101 @@
#define MAX_STRING 1024
-#define EG_PARAMS_URI "http://lv2plug.in/plugins/eg-params"
+#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;
+ 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;
+ 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);
+ 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
-};
+enum { PARAMS_IN = 0, PARAMS_OUT = 1 };
typedef struct {
- // Features
- LV2_URID_Map* map;
- LV2_URID_Unmap* unmap;
- LV2_Log_Logger log;
+ // Features
+ LV2_URID_Map* map;
+ LV2_URID_Unmap* unmap;
+ LV2_Log_Logger log;
- // Forge for creating atoms
- LV2_Atom_Forge forge;
+ // Forge for creating atoms
+ LV2_Atom_Forge forge;
- // Ports
- const LV2_Atom_Sequence* in_port;
- LV2_Atom_Sequence* out_port;
+ // Ports
+ const LV2_Atom_Sequence* in_port;
+ LV2_Atom_Sequence* out_port;
- // URIs
- URIs uris;
+ // URIs
+ URIs uris;
- // Plugin state
- StateMapItem props[N_PROPS];
- State state;
+ // Plugin state
+ StateMapItem props[N_PROPS];
+ State state;
- // Buffer for making strings from URIDs if unmap is not provided
- char urid_buf[12];
+ // 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)
+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;
- }
+ 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
@@ -145,81 +126,83 @@ instantiate(const LV2_Descriptor* descriptor,
const char* path,
const LV2_Feature* const* features)
{
- // Allocate instance
- Params* self = (Params*)calloc(1, sizeof(Params));
- if (!self) {
- return NULL;
- }
-
- // Get host features
- 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);
- 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
- 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);
-
- return (LV2_Handle)self;
+ // 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);
+ 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);
- } else {
- snprintf(self->urid_buf, sizeof(self->urid_buf), "%u", urid);
- return self->urid_buf;
- }
+ 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)
+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;
+ 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
@@ -230,36 +213,36 @@ set_parameter(Params* self,
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;
+ // 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;
+ 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
@@ -270,15 +253,15 @@ write_param_to_forge(LV2_State_Handle handle,
uint32_t type,
uint32_t flags)
{
- LV2_Atom_Forge* forge = (LV2_Atom_Forge*)handle;
+ 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;
- }
+ 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;
+ return LV2_STATE_SUCCESS;
}
static void
@@ -290,31 +273,31 @@ store_prop(Params* self,
LV2_URID key,
const LV2_Atom* value)
{
- LV2_State_Status st;
- 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;
- }
+ 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;
+ }
}
/**
@@ -331,17 +314,17 @@ save(LV2_Handle instance,
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);
+ Params* self = (Params*)instance;
+ LV2_State_Map_Path* map_path =
+ (LV2_State_Map_Path*)lv2_features_data(features, LV2_STATE__mapPath);
- LV2_State_Status st = LV2_STATE_SUCCESS;
- for (unsigned i = 0; i < N_PROPS; ++i) {
- StateMapItem* prop = &self->props[i];
- store_prop(self, map_path, &st, store, handle, prop->urid, prop->value);
- }
+ LV2_State_Status st = LV2_STATE_SUCCESS;
+ for (unsigned i = 0; i < N_PROPS; ++i) {
+ StateMapItem* prop = &self->props[i];
+ store_prop(self, map_path, &st, store, handle, prop->urid, prop->value);
+ }
- return st;
+ return st;
}
static void
@@ -351,20 +334,20 @@ retrieve_prop(Params* self,
LV2_State_Handle handle,
LV2_URID key)
{
- // Retrieve value from saved state
- size_t vsize;
- uint32_t vtype;
- uint32_t vflags;
- 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
- }
+ // 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. */
@@ -375,152 +358,163 @@ restore(LV2_Handle instance,
uint32_t flags,
const LV2_Feature* const* features)
{
- Params* self = (Params*)instance;
- LV2_State_Status st = LV2_STATE_SUCCESS;
+ 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);
- }
+ for (unsigned i = 0; i < N_PROPS; ++i) {
+ retrieve_prop(self, &st, retrieve, handle, self->props[i].urid);
+ }
- return st;
+ 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));
+ // 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;
- lv2_atom_object_get(obj,
- uris->patch_subject, (const LV2_Atom**)&subject,
- uris->patch_property, (const LV2_Atom**)&property,
- uris->patch_value, &value,
- 0);
- 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;
- lv2_atom_object_get(obj,
- uris->patch_subject, (const LV2_Atom**)&subject,
- uris->patch_property, (const LV2_Atom**)&property,
- 0);
- 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.001 : 0.0;
- 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);
+ 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_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*
+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;
+ return (index == 0) ? &descriptor : NULL;
}
diff --git a/plugins/eg-params.lv2/state_map.h b/plugins/eg-params.lv2/state_map.h
index c80d4a2..2a29e09 100644
--- a/plugins/eg-params.lv2/state_map.h
+++ b/plugins/eg-params.lv2/state_map.h
@@ -1,52 +1,40 @@
-/*
- LV2 State Map
- Copyright 2016 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// 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;
+ 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;
- } else if (kb->urid < ka->urid) {
- return 1;
- }
- return 0;
+ 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)
+ (LV2_ATOM__##type), (sizeof(*(ptr)) - sizeof(LV2_Atom)), (ptr)
/**
Initialise a state map.
@@ -72,29 +60,30 @@ state_map_cmp(const void* a, const void* b)
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 */ ...)
+state_map_init(
+ StateMapItem dict[],
+ LV2_URID_Map* map,
+ LV2_URID_Map_Handle handle,
+ /* const char* uri, const char* type, uint32_t size, LV2_Atom* value */...)
{
- // Set dict entries from parameters
- unsigned i = 0;
- va_list args;
- va_start(args, handle);
- for (const char* uri; (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);
+ // 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);
+ // Sort for fast lookup by URID by state_map_find()
+ qsort(dict, i, sizeof(StateMapItem), state_map_cmp);
}
/**
@@ -107,8 +96,7 @@ state_map_init(StateMapItem dict[],
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);
+ const StateMapItem key = {NULL, urid, NULL};
+ return (StateMapItem*)bsearch(
+ &key, dict, n_entries, sizeof(StateMapItem), state_map_cmp);
}
-
diff --git a/plugins/eg-params.lv2/wscript b/plugins/eg-params.lv2/wscript
deleted file mode 100644
index 503e8db..0000000
--- a/plugins/eg-params.lv2/wscript
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-params.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
- opt.load('compiler_c')
- opt.load('lv2')
- autowaf.set_options(opt)
-
-def configure(conf):
- conf.load('compiler_c', cache=True)
- conf.load('lv2', cache=True)
- conf.load('autowaf', cache=True)
-
- conf.check_pkg('lv2 >= 1.12.1', uselib_store='LV2')
-
-def build(bld):
- bundle = 'eg-params.lv2'
-
- # Build manifest.ttl by substitution (for portable lib extension)
- bld(features = 'subst',
- source = 'manifest.ttl.in',
- target = 'lv2/%s/%s' % (bundle, 'manifest.ttl'),
- install_path = '${LV2DIR}/%s' % bundle,
- LIB_EXT = bld.env.LV2_LIB_EXT)
-
- # Copy other data files to build bundle (build/eg-params.lv2)
- for i in ['params.ttl']:
- bld(features = 'subst',
- is_copy = True,
- source = i,
- target = 'lv2/%s/%s' % (bundle, i),
- install_path = '${LV2DIR}/%s' % bundle)
-
- # Build plugin library
- obj = bld(features = 'c cshlib lv2lib',
- source = 'params.c',
- name = 'params',
- target = 'lv2/%s/params' % bundle,
- install_path = '${LV2DIR}/%s' % bundle,
- use = 'LV2')
diff --git a/plugins/eg-sampler.lv2/atom_sink.h b/plugins/eg-sampler.lv2/atom_sink.h
index b84ca55..8966eb2 100644
--- a/plugins/eg-sampler.lv2/atom_sink.h
+++ b/plugins/eg-sampler.lv2/atom_sink.h
@@ -1,20 +1,12 @@
-/*
- Copyright 2016 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// 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.
@@ -25,11 +17,11 @@
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;
+ 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;
}
/**
@@ -38,5 +30,5 @@ atom_sink(LV2_Atom_Forge_Sink_Handle handle, const void* buf, uint32_t size)
static LV2_Atom*
atom_sink_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref)
{
- return (LV2_Atom*)((char*)handle + ref);
+ return (LV2_Atom*)((char*)handle + ref);
}
diff --git a/plugins/eg-sampler.lv2/manifest.ttl.in b/plugins/eg-sampler.lv2/manifest.ttl.in
index 8a01428..e688256 100644
--- a/plugins/eg-sampler.lv2/manifest.ttl.in
+++ b/plugins/eg-sampler.lv2/manifest.ttl.in
@@ -15,5 +15,5 @@
<http://lv2plug.in/plugins/eg-sampler#ui>
a ui:GtkUI ;
- ui:binary <sampler_ui@LIB_EXT@> ;
+ 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
new file mode 100644
index 0000000..9700df2
--- /dev/null
+++ b/plugins/eg-sampler.lv2/meson.build
@@ -0,0 +1,78 @@
+# 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',
+ required: get_option('plugins'),
+ version: '>= 0.1.0',
+)
+
+sndfile_dep = dependency(
+ 'sndfile',
+ 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',
+ install: true,
+ install_dir: lv2dir / bundle_name,
+ name_prefix: '',
+ )
+
+ extension = '.' + module.full_path().split('.')[-1]
+ config = configuration_data({'LIB_EXT': extension})
+
+ foreach filename : data_filenames
+ if filename.endswith('.in')
+ configure_file(
+ configuration: config,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename.substring(0, -3),
+ )
+ else
+ configure_file(
+ copy: true,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename,
+ )
+ endif
+ endforeach
+
+ if gtk2_dep.found()
+ ui_suppressions = c_suppressions
+ if cc.get_id() == 'gcc'
+ ui_suppressions += ['-Wno-strict-overflow']
+ endif
+
+ shared_library(
+ 'sampler_ui',
+ ui_sources,
+ c_args: ui_suppressions,
+ dependencies: [lv2_dep, gtk2_dep],
+ gnu_symbol_visibility: 'hidden',
+ install: true,
+ install_dir: lv2dir / bundle_name,
+ name_prefix: '',
+ )
+ endif
+endif
diff --git a/plugins/eg-sampler.lv2/peaks.h b/plugins/eg-sampler.lv2/peaks.h
index 45d3465..47d6616 100644
--- a/plugins/eg-sampler.lv2/peaks.h
+++ b/plugins/eg-sampler.lv2/peaks.h
@@ -1,19 +1,8 @@
-/*
- LV2 audio peaks utilities
- Copyright 2016 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// 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
@@ -25,50 +14,53 @@
requested, with reasonably sized incremental updates sent over plugin ports.
*/
-#ifndef PEAKS_H_INCLUDED
-#define PEAKS_H_INCLUDED
-
+#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"
+#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))
+# define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
-# define MAX(a, b) (((a) > (b)) ? (a) : (b))
+# 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;
+ 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
+ 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
+ PeaksURIs uris; ///< URIDs used in protocol
+ float* peaks; ///< Received peaks, or zeroes
+ uint32_t n_peaks; ///< Total number of peaks
} PeaksReceiver;
/**
@@ -77,13 +69,13 @@ typedef struct {
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);
+ 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);
}
/**
@@ -94,9 +86,9 @@ peaks_map_uris(PeaksURIs* uris, LV2_URID_Map* map)
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;
+ memset(sender, 0, sizeof(*sender));
+ peaks_map_uris(&sender->uris, map);
+ return sender;
}
/**
@@ -109,11 +101,11 @@ peaks_sender_start(PeaksSender* sender,
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;
+ sender->samples = samples;
+ sender->n_samples = n_samples;
+ sender->n_peaks = n_peaks;
+ sender->current_offset = 0;
+ sender->sending = true;
}
/**
@@ -123,10 +115,10 @@ peaks_sender_start(PeaksSender* sender,
[source,turtle]
----
[]
- a peaks:PeakUpdate ;
- peaks:offset 256 ;
- peaks:total 1024 ;
- peaks:magnitudes [ 0.2f, 0.3f, ... ] .
+ a peaks:PeakUpdate ;
+ peaks:offset 256 ;
+ peaks:total 1024 ;
+ peaks:magnitudes [ 0.2f, 0.3f, ... ] .
----
*/
static inline bool
@@ -135,53 +127,53 @@ peaks_sender_send(PeaksSender* sender,
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, sender->current_offset);
-
- // eg:total = TOTAL
- lv2_atom_forge_key(forge, uris->peaks_total);
- lv2_atom_forge_int(forge, 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 int chunk_size = MAX(1, 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 int n_update = MIN(remaining,
- MIN(n_frames / 4, space / sizeof(float)));
-
- // Calculate peak (maximum magnitude) for each chunk
- for (int i = 0; i < n_update; ++i) {
- const int start = (sender->current_offset + i) * chunk_size;
- float peak = 0.0f;
- for (int 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;
+ 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;
}
/**
@@ -191,9 +183,9 @@ peaks_sender_send(PeaksSender* sender,
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;
+ memset(receiver, 0, sizeof(*receiver));
+ peaks_map_uris(&receiver->uris, map);
+ return receiver;
}
/**
@@ -203,9 +195,9 @@ peaks_receiver_init(PeaksReceiver* receiver, LV2_URID_Map* map)
static inline void
peaks_receiver_clear(PeaksReceiver* receiver)
{
- free(receiver->peaks);
- receiver->peaks = NULL;
- receiver->n_peaks = 0;
+ free(receiver->peaks);
+ receiver->peaks = NULL;
+ receiver->n_peaks = 0;
}
/**
@@ -219,53 +211,56 @@ peaks_receiver_clear(PeaksReceiver* receiver)
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;
- 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);
-
- 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 int n_per = n / receiver->n_peaks;
- for (int 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 int n_per = receiver->n_peaks / n;
- for (int 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;
+ 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
index ed49903..d870a07 100644
--- a/plugins/eg-sampler.lv2/sampler.c
+++ b/plugins/eg-sampler.lv2/sampler.c
@@ -1,21 +1,7 @@
-/*
- LV2 Sampler Example Plugin
- 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>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// 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"
@@ -33,6 +19,7 @@
#include "lv2/urid/urid.h"
#include "lv2/worker/worker.h"
+#include <samplerate.h>
#include <sndfile.h>
#include <math.h>
@@ -42,46 +29,45 @@
#include <stdlib.h>
#include <string.h>
-enum {
- SAMPLER_CONTROL = 0,
- SAMPLER_NOTIFY = 1,
- SAMPLER_OUT = 2
-};
+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
+ 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;
- sf_count_t frame;
- bool play;
- bool activated;
- bool sample_changed;
+ // 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;
/**
@@ -93,11 +79,28 @@ typedef struct {
ringbuffer.
*/
typedef struct {
- LV2_Atom atom;
- Sample* sample;
+ 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
@@ -105,55 +108,95 @@ typedef struct {
not modified.
*/
static Sample*
-load_sample(LV2_Log_Logger* logger, const char* path)
+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 (info->channels != 1) {
- lv2_log_error(logger, "%s has %d channels\n", path, info->channels);
- } else if (!(data = (float*)malloc(sizeof(float) * info->frames))) {
- 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);
- sf_close(sndfile);
-
- // 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;
+ 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);
- }
+ if (sample) {
+ lv2_log_trace(&self->logger, "Freeing %s\n", sample->path);
+ free(sample->path);
+ free(sample->data);
+ free(sample);
+ }
}
/**
@@ -170,30 +213,30 @@ work(LV2_Handle instance,
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);
- if (sample) {
- // Send new sample to run() to be applied
- respond(handle, sizeof(sample), &sample);
- }
- }
-
- return LV2_WORKER_SUCCESS;
+ 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;
}
/**
@@ -204,50 +247,48 @@ work(LV2_Handle instance,
scheduled.
*/
static LV2_Worker_Status
-work_response(LV2_Handle instance,
- uint32_t size,
- const void* data)
+work_response(LV2_Handle instance, uint32_t size, const void* data)
{
- Sampler* self = (Sampler*)instance;
- Sample* old_sample = self->sample;
- Sample* new_sample = *(Sample*const*)data;
+ Sampler* self = (Sampler*)instance;
+ Sample* old_sample = self->sample;
+ Sample* new_sample = *(Sample* const*)data;
- // Install the new sample
- self->sample = *(Sample*const*)data;
+ // Install the new sample
+ self->sample = *(Sample* const*)data;
- // 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);
+ // Stop playing previous sample, which can be larger than new one
+ self->frame = 0;
+ self->play = false;
- // 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);
+ // 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);
- return LV2_WORKER_SUCCESS;
+ // 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)
+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;
- }
+ 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
@@ -256,58 +297,63 @@ instantiate(const LV2_Descriptor* descriptor,
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
- 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);
- 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.0;
-
- return (LV2_Handle)self;
+ // 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);
+ Sampler* self = (Sampler*)instance;
+ free_sample(self, self->sample);
+ free(self);
}
static void
activate(LV2_Handle instance)
{
- ((Sampler*)instance)->activated = true;
+ ((Sampler*)instance)->activated = true;
}
static void
deactivate(LV2_Handle instance)
{
- ((Sampler*)instance)->activated = false;
+ ((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)
+#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g)*0.05f) : 0.0f)
/**
Handle an incoming event in the audio thread.
@@ -318,79 +364,88 @@ deactivate(LV2_Handle instance)
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*)LV2_ATOM_BODY_CONST(ev);
- 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;
- lv2_atom_object_get(obj,
- uris->patch_property, &property,
- uris->patch_value, &value,
- 0);
- if (!property) {
- lv2_log_error(&self->logger, "Set message with no property\n");
- return;
- } else 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_CO(((LV2_Atom_Float*)value)->body);
- }
- }
- } else if (obj->body.otype == uris->patch_Get && self->sample) {
- const LV2_Atom_URID* accept = NULL;
- const LV2_Atom_Int* n_peaks = NULL;
- lv2_atom_object_get_typed(
- obj,
- uris->patch_accept, &accept, uris->atom_URID,
- peaks_uris->peaks_total, &n_peaks, peaks_uris->atom_Int, 0);
- 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 %d\n", obj->body.otype);
- }
- } else {
- lv2_log_trace(&self->logger,
- "Unknown event type %d\n", ev->body.type);
- }
-
+ 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);
+ }
}
/**
@@ -399,70 +454,76 @@ handle_event(Sampler* self, LV2_Atom_Event* ev)
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;
- }
+ 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 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 sychronous 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);
+ 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
@@ -472,30 +533,39 @@ save(LV2_Handle instance,
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);
- return LV2_STATE_SUCCESS;
+ 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
@@ -505,99 +575,122 @@ restore(LV2_Handle instance,
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;
- const char* missing = lv2_features_query(
- features,
- LV2_STATE__mapPath, &paths, true,
- LV2_WORKER__schedule, &schedule, false,
- NULL);
- 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;
- uint32_t type;
- uint32_t valflags;
- 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;
- } else 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);
- 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);
-
- return LV2_STATE_SUCCESS;
+ 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;
- } else if (!strcmp(uri, LV2_WORKER__interface)) {
- return &worker;
- }
- return NULL;
+ 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
-};
+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)
+const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
{
- switch (index) {
- case 0:
- return &descriptor;
- default:
- return NULL;
- }
+ return index == 0 ? &descriptor : NULL;
}
diff --git a/plugins/eg-sampler.lv2/sampler.ttl b/plugins/eg-sampler.lv2/sampler.ttl
index f5088f3..4a3c24c 100644
--- a/plugins/eg-sampler.lv2/sampler.ttl
+++ b/plugins/eg-sampler.lv2/sampler.ttl
@@ -58,7 +58,7 @@
] ;
state:state [
<http://lv2plug.in/plugins/eg-sampler#sample> <click.wav> ;
- param:gain "1.0"^^xsd:float
+ param:gain "0.0"^^xsd:float
] .
<http://lv2plug.in/plugins/eg-sampler#ui>
diff --git a/plugins/eg-sampler.lv2/sampler_ui.c b/plugins/eg-sampler.lv2/sampler_ui.c
index 13a8cd4..16a8c71 100644
--- a/plugins/eg-sampler.lv2/sampler_ui.c
+++ b/plugins/eg-sampler.lv2/sampler_ui.c
@@ -1,19 +1,5 @@
-/*
- LV2 Sampler Example Plugin UI
- Copyright 2011-2016 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// Copyright 2011-2016 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
#include "peaks.h"
#include "uris.h"
@@ -36,6 +22,7 @@
#include <gobject/gclosure.h>
#include <gtk/gtk.h>
+#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@@ -47,193 +34,198 @@
#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;
+ 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;
+ SamplerUI* ui = (SamplerUI*)handle;
- // Get the filename from the file chooser
- char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
+ // 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));
+ // 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));
- ui->write(ui->controller, 0, lv2_atom_total_size(msg),
- ui->uris.atom_eventTransfer,
- msg);
+ assert(msg);
- g_free(filename);
+ 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;
+ SamplerUI* ui = (SamplerUI*)handle;
- ui->request_value->request(ui->request_value->handle,
- ui->uris.eg_sample,
- 0,
- NULL);
+ 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,
- &note_on);
+ 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,
+ &note_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;
+ 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);
-
+ 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;
+ SamplerUI* ui = (SamplerUI*)data;
- GtkAllocation size;
- gtk_widget_get_allocation(widget, &size);
+ GtkAllocation size;
+ gtk_widget_get_allocation(widget, &size);
- ui->width = size.width;
- if ((uint32_t)ui->width > 2 * ui->requested_n_peaks) {
- request_peaks(ui, 2 * ui->requested_n_peaks);
- }
+ 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_t* cr = gdk_cairo_create(gtk_widget_get_window(widget));
- cairo_set_line_width(cr, 1.0);
- cairo_translate(cr, 0.5, 0.5);
+ cairo_set_line_width(cr, 1.0);
+ cairo_translate(cr, 0.5, 0.5);
- const int mid_y = size.height / 2;
+ 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);
+ 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);
+ // 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);
- }
+ // 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);
- }
+ // 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);
+ // 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->mid);
+ cairo_fill_preserve(cr);
- cairo_set_source_gdk(cr, widget->style->fg);
- cairo_stroke(cr);
- }
+ cairo_set_source_gdk(cr, widget->style->fg);
+ cairo_stroke(cr);
+ }
- cairo_destroy(cr);
- return TRUE;
+ 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;
- }
+ 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;
+ 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;
+ // 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;
+ return FALSE;
}
static LV2UI_Handle
@@ -245,97 +237,104 @@ instantiate(const LV2UI_Descriptor* descriptor,
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
- 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);
- 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);
- 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;
+ 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);
+ 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
@@ -345,121 +344,116 @@ port_event(LV2UI_Handle handle,
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");
- }
+ 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;
+ SamplerUI* ui = (SamplerUI*)handle;
- if (ui->window) {
- return 0;
- }
+ 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;
- }
+ 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);
+ 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);
+ 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));
+ gtk_widget_show_all(ui->window);
+ gtk_window_present(GTK_WINDOW(ui->window));
- return 0;
+ return 0;
}
/* Optional non-embedded UI hide interface. */
static int
ui_hide(LV2UI_Handle handle)
{
- SamplerUI* ui = (SamplerUI*)handle;
+ SamplerUI* ui = (SamplerUI*)handle;
- if (ui->window) {
- destroy_window(ui);
- }
+ if (ui->window) {
+ destroy_window(ui);
+ }
- return 0;
+ return 0;
}
/* Idle interface for optional non-embedded UI. */
static int
ui_idle(LV2UI_Handle handle)
{
- SamplerUI* ui = (SamplerUI*)handle;
- if (ui->window) {
- gtk_main_iteration_do(false);
- }
- return 0;
+ SamplerUI* ui = (SamplerUI*)handle;
+ if (ui->window) {
+ gtk_main_iteration_do(false);
+ }
+ return 0;
}
static const void*
extension_data(const char* uri)
{
- static const LV2UI_Show_Interface show = { ui_show, ui_hide };
- static const LV2UI_Idle_Interface idle = { ui_idle };
- if (!strcmp(uri, LV2_UI__showInterface)) {
- return &show;
- } else if (!strcmp(uri, LV2_UI__idleInterface)) {
- return &idle;
- }
- return NULL;
+ 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
-};
+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)
{
- switch (index) {
- case 0:
- return &descriptor;
- default:
- return NULL;
- }
+ return index == 0 ? &descriptor : NULL;
}
diff --git a/plugins/eg-sampler.lv2/uris.h b/plugins/eg-sampler.lv2/uris.h
index 1609db7..f934390 100644
--- a/plugins/eg-sampler.lv2/uris.h
+++ b/plugins/eg-sampler.lv2/uris.h
@@ -1,74 +1,89 @@
-/*
- LV2 Sampler Example Plugin
- Copyright 2011-2016 David Robillard <d@drobilla.net>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// Copyright 2011-2016 David Robillard <d@drobilla.net>
+// SPDX-License-Identifier: ISC
#ifndef SAMPLER_URIS_H
#define SAMPLER_URIS_H
-#include "lv2/log/log.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/state/state.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_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"
+#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;
+ 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);
+ 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;
}
/**
@@ -87,17 +102,17 @@ write_set_file(LV2_Atom_Forge* forge,
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_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_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;
+ lv2_atom_forge_pop(forge, &frame);
+ return set;
}
/**
@@ -111,40 +126,45 @@ write_set_file(LV2_Atom_Forge* forge,
----
*/
static inline const char*
-read_set_file(const SamplerURIs* uris,
- const LV2_Atom_Object* obj)
+read_set_file(const SamplerURIs* uris, const LV2_Atom_Object* obj)
{
- if (obj->body.otype != uris->patch_Set) {
- fprintf(stderr, "Ignoring unknown message type %d\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;
- } else if (property->type != uris->atom_URID) {
- fprintf(stderr, "Malformed set message has non-URID property.\n");
- return NULL;
- } else 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;
- } else if (value->type != uris->atom_Path) {
- fprintf(stderr, "Set message value is not a Path.\n");
- return NULL;
- }
-
- return (const char*)LV2_ATOM_BODY_CONST(value);
+ if (obj->body.otype != uris->patch_Set) {
+ fprintf(stderr, "Ignoring unknown message type %u\n", obj->body.otype);
+ return NULL;
+ }
+
+ /* Get property URI. */
+ const LV2_Atom* property = NULL;
+ lv2_atom_object_get(obj, uris->patch_property, &property, 0);
+ if (!property) {
+ fprintf(stderr, "Malformed set message has no body.\n");
+ return NULL;
+ }
+
+ if (property->type != uris->atom_URID) {
+ fprintf(stderr, "Malformed set message has non-URID property.\n");
+ return NULL;
+ }
+
+ if (((const LV2_Atom_URID*)property)->body != uris->eg_sample) {
+ fprintf(stderr, "Set message for unknown property.\n");
+ return NULL;
+ }
+
+ /* Get value. */
+ const LV2_Atom* value = NULL;
+ lv2_atom_object_get(obj, uris->patch_value, &value, 0);
+ if (!value) {
+ fprintf(stderr, "Malformed set message has no value.\n");
+ return NULL;
+ }
+
+ if (value->type != uris->atom_Path) {
+ fprintf(stderr, "Set message value is not a Path.\n");
+ return NULL;
+ }
+
+ return (const char*)LV2_ATOM_BODY_CONST(value);
}
-#endif /* SAMPLER_URIS_H */
+#endif /* SAMPLER_URIS_H */
diff --git a/plugins/eg-sampler.lv2/waf b/plugins/eg-sampler.lv2/waf
deleted file mode 120000
index 59a1ac9..0000000
--- a/plugins/eg-sampler.lv2/waf
+++ /dev/null
@@ -1 +0,0 @@
-../../waf \ No newline at end of file
diff --git a/plugins/eg-sampler.lv2/wscript b/plugins/eg-sampler.lv2/wscript
deleted file mode 100644
index 8c640c1..0000000
--- a/plugins/eg-sampler.lv2/wscript
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-sampler.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
- opt.load('compiler_c')
- opt.load('lv2')
- autowaf.set_options(opt)
-
-def configure(conf):
- conf.load('compiler_c', cache=True)
- conf.load('lv2', cache=True)
- conf.load('autowaf', cache=True)
-
- conf.check_pkg('lv2 >= 1.2.1', uselib_store='LV2')
- conf.check_pkg('sndfile >= 1.0.0', uselib_store='SNDFILE')
- conf.check_pkg('gtk+-2.0 >= 2.18.0',
- uselib_store='GTK2',
- system=True,
- mandatory=False)
- conf.check(features='c cshlib', lib='m', uselib_store='M', mandatory=False)
-
-def build(bld):
- bundle = 'eg-sampler.lv2'
-
- # Build manifest.ttl by substitution (for portable lib extension)
- bld(features = 'subst',
- source = 'manifest.ttl.in',
- target = 'lv2/%s/%s' % (bundle, 'manifest.ttl'),
- install_path = '${LV2DIR}/%s' % bundle,
- LIB_EXT = bld.env.LV2_LIB_EXT)
-
- # Copy other data files to build bundle (build/eg-sampler.lv2)
- for i in ['sampler.ttl', 'click.wav']:
- bld(features = 'subst',
- is_copy = True,
- source = i,
- target = 'lv2/%s/%s' % (bundle, i),
- install_path = '${LV2DIR}/%s' % bundle)
-
- # Build plugin library
- obj = bld(features = 'c cshlib lv2lib',
- source = 'sampler.c',
- name = 'sampler',
- target = 'lv2/%s/sampler' % bundle,
- install_path = '${LV2DIR}/%s' % bundle,
- use = ['M', 'SNDFILE', 'LV2'])
-
- # Build UI library
- if bld.env.HAVE_GTK2:
- obj = bld(features = 'c cshlib lv2lib',
- source = 'sampler_ui.c',
- name = 'sampler_ui',
- target = 'lv2/%s/sampler_ui' % bundle,
- install_path = '${LV2DIR}/%s' % bundle,
- use = ['GTK2', 'LV2'])
diff --git a/plugins/eg-scope.lv2/examploscope.c b/plugins/eg-scope.lv2/examploscope.c
index a4f5f3f..e3bb53b 100644
--- a/plugins/eg-scope.lv2/examploscope.c
+++ b/plugins/eg-scope.lv2/examploscope.c
@@ -1,19 +1,6 @@
-/*
- Copyright 2016 David Robillard <d@drobilla.net>
- Copyright 2013 Robin Gareus <robin@gareus.org>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// Copyright 2016 David Robillard <d@drobilla.net>
+// Copyright 2013 Robin Gareus <robin@gareus.org>
+// SPDX-License-Identifier: ISC
#include "./uris.h"
@@ -42,40 +29,40 @@
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;
+ // 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)
+ 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 ==== */
@@ -85,82 +72,83 @@ instantiate(const LV2_Descriptor* descriptor,
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
- const char* missing = lv2_features_query(
- features,
- LV2_LOG__log, &self->logger.log, false,
- LV2_URID__map, &self->map, true,
- NULL);
- 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.0;
-
- // 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;
+ (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)
+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;
- }
+ 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;
+ }
}
/**
@@ -171,9 +159,9 @@ connect_port(LV2_Handle handle,
[source,turtle]
--------
[]
- a sco:RawAudio ;
- sco:channelID 0 ;
- sco:audioData [ 0.0, 0.0, ... ] .
+ 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
@@ -187,134 +175,132 @@ tx_rawaudio(LV2_Atom_Forge* forge,
const size_t n_samples,
const float* data)
{
- LV2_Atom_Forge_Frame frame;
+ 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);
+ // 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 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);
+ // 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);
+ // 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, 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, 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, &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, 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);
+ 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, &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);
+ free(handle);
}
-
/**
==== State Methods ====
@@ -331,22 +317,26 @@ state_save(LV2_Handle instance,
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;
+ 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
@@ -356,72 +346,68 @@ state_restore(LV2_Handle instance,
uint32_t flags,
const LV2_Feature* const* features)
{
- EgScope* self = (EgScope*)instance;
-
- size_t size;
- uint32_t type;
- uint32_t valflags;
-
- 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;
+ 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;
+ 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
-};
+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;
- }
+ switch (index) {
+ case 0:
+ return &descriptor_mono;
+ case 1:
+ return &descriptor_stereo;
+ default:
+ return NULL;
+ }
}
diff --git a/plugins/eg-scope.lv2/examploscope_ui.c b/plugins/eg-scope.lv2/examploscope_ui.c
index ce0000c..1c82d44 100644
--- a/plugins/eg-scope.lv2/examploscope_ui.c
+++ b/plugins/eg-scope.lv2/examploscope_ui.c
@@ -1,18 +1,5 @@
-/*
- Copyright 2013 Robin Gareus <robin@gareus.org>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// Copyright 2013 Robin Gareus <robin@gareus.org>
+// SPDX-License-Identifier: ISC
#include "./uris.h"
@@ -38,7 +25,7 @@
#include <string.h>
// Drawing area size
-#define DAWIDTH (640)
+#define DAWIDTH (640)
#define DAHEIGHT (200)
/**
@@ -53,96 +40,100 @@
given 'index' position.
*/
typedef struct {
- float data_min[DAWIDTH];
- float data_max[DAWIDTH];
+ float data_min[DAWIDTH];
+ float data_max[DAWIDTH];
- uint32_t idx;
- uint32_t sub;
+ 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;
+ 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);
-
- // 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);
+ EgScopeUI* ui = (EgScopeUI*)handle;
+ const float gain = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_amp));
+
+ // Use local buffer on the stack to build atom
+ uint8_t obj_buf[1024];
+ lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf));
+
+ // Start a ui:State object
+ LV2_Atom_Forge_Frame frame;
+ LV2_Atom* msg =
+ (LV2_Atom*)lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.ui_State);
+
+ assert(msg);
+
+ // msg[samples-per-pixel] = integer
+ lv2_atom_forge_key(&ui->forge, ui->uris.ui_spp);
+ lv2_atom_forge_int(&ui->forge, ui->stride);
+
+ // msg[amplitude] = float
+ lv2_atom_forge_key(&ui->forge, ui->uris.ui_amp);
+ lv2_atom_forge_float(&ui->forge, gain);
+
+ // Finish ui:State object
+ lv2_atom_forge_pop(&ui->forge, &frame);
+
+ // Send message to plugin port '0'
+ ui->write(ui->controller,
+ 0,
+ lv2_atom_total_size(msg),
+ ui->uris.atom_eventTransfer,
+ msg);
}
/** Notify backend that UI is closed. */
static void
send_ui_disable(LV2UI_Handle handle)
{
- EgScopeUI* ui = (EgScopeUI*)handle;
- send_ui_state(handle);
-
- uint8_t obj_buf[64];
- lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf));
-
- LV2_Atom_Forge_Frame frame;
- LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(
- &ui->forge, &frame, 0, ui->uris.ui_Off);
- lv2_atom_forge_pop(&ui->forge, &frame);
- ui->write(ui->controller,
- 0,
- lv2_atom_total_size(msg),
- ui->uris.atom_eventTransfer,
- msg);
+ 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);
}
/**
@@ -153,32 +144,35 @@ send_ui_disable(LV2UI_Handle handle)
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);
- lv2_atom_forge_pop(&ui->forge, &frame);
- ui->write(ui->controller,
- 0,
- lv2_atom_total_size(msg),
- ui->uris.atom_eventTransfer,
- msg);
+ EgScopeUI* ui = (EgScopeUI*)handle;
+
+ uint8_t obj_buf[64];
+ lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf));
+
+ LV2_Atom_Forge_Frame frame;
+ LV2_Atom* msg =
+ (LV2_Atom*)lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.ui_On);
+
+ assert(msg);
+
+ lv2_atom_forge_pop(&ui->forge, &frame);
+ ui->write(ui->controller,
+ 0,
+ lv2_atom_total_size(msg),
+ ui->uris.atom_eventTransfer,
+ msg);
}
/** Gtk widget callback. */
static gboolean
on_cfg_changed(GtkWidget* widget, gpointer data)
{
- EgScopeUI* ui = (EgScopeUI*)data;
- if (!ui->updating) {
- // Only send UI state if the change is from user interaction
- send_ui_state(data);
- }
- return TRUE;
+ EgScopeUI* ui = (EgScopeUI*)data;
+ if (!ui->updating) {
+ // Only send UI state if the change is from user interaction
+ send_ui_state(data);
+ }
+ return TRUE;
}
/**
@@ -189,128 +183,129 @@ on_cfg_changed(GtkWidget* widget, gpointer 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;
- cr = gdk_cairo_create(ui->darea->window);
-
- // Limit cairo-drawing to exposed area
- cairo_rectangle(cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
- cairo_clip(cr);
-
- // Clear background
- cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
- cairo_rectangle(cr, 0, 0, DAWIDTH, DAHEIGHT * ui->n_channels);
- cairo_fill(cr);
-
- cairo_set_line_width(cr, 1.0);
-
- const uint32_t start = ev->area.x;
- const uint32_t end = ev->area.x + ev->area.width;
-
- assert(start < DAWIDTH);
- assert(end <= DAWIDTH);
- assert(start < end);
-
- for (uint32_t c = 0; c < ui->n_channels; ++c) {
- ScoChan* chn = &ui->chn[c];
-
- /* Drawing area Y-position of given sample-value.
- * Note: cairo-pixel at 0 spans -0.5 .. +0.5, hence (DAHEIGHT / 2.0 -0.5)
- * also the cairo Y-axis points upwards (hence 'minus value')
- *
- * == ( DAHEIGHT * (CHN) // channel offset
- * + (DAHEIGHT / 2) - 0.5 // vertical center -- '0'
- * - (DAHEIGHT / 2) * (VAL) * (GAIN)
- * )
- */
- const float chn_y_offset = DAHEIGHT * c + DAHEIGHT * 0.5f - 0.5f;
- const float chn_y_scale = DAHEIGHT * 0.5f * gain;
-
-#define CYPOS(VAL) (chn_y_offset - (VAL) * chn_y_scale)
-
- cairo_save(cr);
-
- /* Restrict drawing to current channel area, don't bleed drawing into
- neighboring channels. */
- cairo_rectangle(cr, 0, DAHEIGHT * c, DAWIDTH, DAHEIGHT);
- cairo_clip(cr);
-
- // Set color of wave-form
- cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0);
-
- /* This is a somewhat 'smart' mechanism to plot audio data using
- alternating up/down line-directions. It works well for both cases:
- 1 pixel <= 1 sample and 1 pixel represents more than 1 sample, but
- is not ideal for either. */
- if (start == chn->idx) {
- cairo_move_to(cr, start - 0.5, CYPOS(0));
- } else {
- cairo_move_to(cr, start - 0.5, CYPOS(chn->data_max[start]));
- }
-
- uint32_t pathlength = 0;
- for (uint32_t i = start; i < end; ++i) {
- if (i == chn->idx) {
- continue;
- } else 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;
+ EgScopeUI* ui = (EgScopeUI*)data;
+ const float gain = gtk_spin_button_get_value(GTK_SPIN_BUTTON(ui->spb_amp));
+
+ // Get cairo type for the gtk window
+ cairo_t* cr = gdk_cairo_create(ui->darea->window);
+
+ // Limit cairo-drawing to exposed area
+ cairo_rectangle(cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+ cairo_clip(cr);
+
+ // Clear background
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
+ cairo_rectangle(cr, 0, 0, DAWIDTH, DAHEIGHT * ui->n_channels);
+ cairo_fill(cr);
+
+ cairo_set_line_width(cr, 1.0);
+
+ const uint32_t start = ev->area.x;
+ const uint32_t end = ev->area.x + ev->area.width;
+
+ assert(start < DAWIDTH);
+ assert(end <= DAWIDTH);
+ assert(start < end);
+
+ for (uint32_t c = 0; c < ui->n_channels; ++c) {
+ ScoChan* chn = &ui->chn[c];
+
+ /* Drawing area Y-position of given sample-value.
+ * Note: cairo-pixel at 0 spans -0.5 .. +0.5, hence (DAHEIGHT / 2.0 -0.5)
+ * also the cairo Y-axis points upwards (hence 'minus value')
+ *
+ * == ( DAHEIGHT * (CHN) // channel offset
+ * + (DAHEIGHT / 2) - 0.5 // vertical center -- '0'
+ * - (DAHEIGHT / 2) * (VAL) * (GAIN)
+ * )
+ */
+ const float chn_y_offset = DAHEIGHT * c + DAHEIGHT * 0.5f - 0.5f;
+ const float chn_y_scale = DAHEIGHT * 0.5f * gain;
+
+#define CYPOS(VAL) (chn_y_offset - (VAL)*chn_y_scale)
+
+ cairo_save(cr);
+
+ /* Restrict drawing to current channel area, don't bleed drawing into
+ neighboring channels. */
+ cairo_rectangle(cr, 0, DAHEIGHT * c, DAWIDTH, DAHEIGHT);
+ cairo_clip(cr);
+
+ // Set color of wave-form
+ cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0);
+
+ /* This is a somewhat 'smart' mechanism to plot audio data using
+ alternating up/down line-directions. It works well for both cases:
+ 1 pixel <= 1 sample and 1 pixel represents more than 1 sample, but
+ is not ideal for either. */
+ if (start == chn->idx) {
+ cairo_move_to(cr, start - 0.5, CYPOS(0));
+ } else {
+ cairo_move_to(cr, start - 0.5, CYPOS(chn->data_max[start]));
+ }
+
+ uint32_t pathlength = 0;
+ for (uint32_t i = start; i < end; ++i) {
+ if (i == chn->idx) {
+ continue;
+ }
+
+ if (i % 2) {
+ cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i]));
+ cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i]));
+ ++pathlength;
+ } else {
+ cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i]));
+ cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i]));
+ ++pathlength;
+ }
+
+ /** Limit the max cairo path length. This is an optimization trade
+ off: too short path: high load CPU/GPU load. too-long path:
+ bad anti-aliasing, or possibly lost points */
+ if (pathlength > MAX_CAIRO_PATH) {
+ pathlength = 0;
+ cairo_stroke(cr);
+ if (i % 2) {
+ cairo_move_to(cr, i - .5, CYPOS(chn->data_max[i]));
+ } else {
+ cairo_move_to(cr, i - .5, CYPOS(chn->data_min[i]));
+ }
+ }
+ }
+
+ if (pathlength > 0) {
+ cairo_stroke(cr);
+ }
+
+ // Draw current position vertical line if display is slow
+ if (ui->stride >= ui->rate / 4800.0f || ui->paused) {
+ cairo_set_source_rgba(cr, .9, .2, .2, .6);
+ cairo_move_to(cr, chn->idx - .5, DAHEIGHT * c);
+ cairo_line_to(cr, chn->idx - .5, DAHEIGHT * (c + 1));
+ cairo_stroke(cr);
+ }
+
+ // Undo the 'clipping' restriction
+ cairo_restore(cr);
+
+ // Channel separator
+ if (c > 0) {
+ cairo_set_source_rgba(cr, .5, .5, .5, 1.0);
+ cairo_move_to(cr, 0, DAHEIGHT * c - .5);
+ cairo_line_to(cr, DAWIDTH, DAHEIGHT * c - .5);
+ cairo_stroke(cr);
+ }
+
+ // Zero scale line
+ cairo_set_source_rgba(cr, .3, .3, .7, .5);
+ cairo_move_to(cr, 0, DAHEIGHT * (c + .5) - .5);
+ cairo_line_to(cr, DAWIDTH, DAHEIGHT * (c + .5) - .5);
+ cairo_stroke(cr);
+ }
+
+ cairo_destroy(cr);
+ return TRUE;
}
/**
@@ -339,27 +334,27 @@ process_channel(EgScopeUI* ui,
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.0;
- chn->data_max[chn->idx] = -1.0;
- }
- }
- *idx_end = chn->idx;
- return overflow;
+ 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;
}
/**
@@ -372,56 +367,57 @@ update_scope(EgScopeUI* ui,
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; // Display pixel start
- uint32_t idx_end; // Display pixel end
- int overflow; // 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);
- }
- }
+ // 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
@@ -433,193 +429,191 @@ instantiate(const LV2UI_Descriptor* descriptor,
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;
+ 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);
+ 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;
+ 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, &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;
+ 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,
+ &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;
}
/**
@@ -637,40 +631,33 @@ port_event(LV2UI_Handle handle,
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);
- }
- }
+ 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
-};
+static const LV2UI_Descriptor descriptor = {SCO_URI "#ui",
+ instantiate,
+ cleanup,
+ port_event,
+ NULL};
LV2_SYMBOL_EXPORT
const LV2UI_Descriptor*
lv2ui_descriptor(uint32_t index)
{
- switch (index) {
- case 0:
- return &descriptor;
- default:
- return NULL;
- }
+ return index == 0 ? &descriptor : NULL;
}
diff --git a/plugins/eg-scope.lv2/manifest.ttl.in b/plugins/eg-scope.lv2/manifest.ttl.in
index a64aff1..66c3c9d 100644
--- a/plugins/eg-scope.lv2/manifest.ttl.in
+++ b/plugins/eg-scope.lv2/manifest.ttl.in
@@ -17,5 +17,5 @@
# ==== Gtk 2.0 UI ====
<http://lv2plug.in/plugins/eg-scope#ui>
a ui:GtkUI ;
- ui:binary <examploscope_ui@LIB_EXT@> ;
+ 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
new file mode 100644
index 0000000..d776ee7
--- /dev/null
+++ b/plugins/eg-scope.lv2/meson.build
@@ -0,0 +1,62 @@
+# Copyright 2022 David Robillard <d@drobilla.net>
+# SPDX-License-Identifier: 0BSD OR ISC
+
+plugin_sources = files('examploscope.c')
+ui_sources = files('examploscope_ui.c')
+bundle_name = 'eg-scope.lv2'
+data_filenames = ['manifest.ttl.in', 'examploscope.ttl.in']
+
+gtk2_dep = dependency(
+ 'gtk+-2.0',
+ include_type: 'system',
+ required: get_option('plugins'),
+ version: '>= 2.18.0',
+)
+
+module = shared_library(
+ 'examploscope',
+ plugin_sources,
+ c_args: c_suppressions,
+ dependencies: [lv2_dep, m_dep],
+ gnu_symbol_visibility: 'hidden',
+ install: true,
+ install_dir: lv2dir / bundle_name,
+ name_prefix: '',
+)
+
+config = configuration_data(
+ {
+ 'LIB_EXT': '.' + module.full_path().split('.')[-1],
+ },
+)
+
+foreach filename : data_filenames
+ if filename.endswith('.in')
+ configure_file(
+ configuration: config,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename.substring(0, -3),
+ )
+ else
+ configure_file(
+ copy: true,
+ input: files(filename),
+ install_dir: lv2dir / bundle_name,
+ output: filename,
+ )
+ endif
+endforeach
+
+if gtk2_dep.found()
+ shared_library(
+ 'examploscope_ui',
+ ui_sources,
+ c_args: c_suppressions,
+ dependencies: [lv2_dep, gtk2_dep],
+ gnu_symbol_visibility: 'hidden',
+ install: true,
+ install_dir: lv2dir / bundle_name,
+ name_prefix: '',
+ )
+endif
diff --git a/plugins/eg-scope.lv2/uris.h b/plugins/eg-scope.lv2/uris.h
index 2ebaf4e..e2becae 100644
--- a/plugins/eg-scope.lv2/uris.h
+++ b/plugins/eg-scope.lv2/uris.h
@@ -1,71 +1,57 @@
-/*
- Copyright 2013 Robin Gareus <robin@gareus.org>
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
+// 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/atom/forge.h"
#include "lv2/parameters/parameters.h"
#include "lv2/urid/urid.h"
#define SCO_URI "http://lv2plug.in/plugins/eg-scope"
typedef struct {
- // URIs defined in LV2 specifications
- LV2_URID atom_Vector;
- LV2_URID atom_Float;
- LV2_URID atom_Int;
- LV2_URID atom_eventTransfer;
- LV2_URID param_sampleRate;
-
- /* URIs defined for this plugin. It is best to re-use existing URIs as
- much as possible, but plugins may need more vocabulary specific to their
- needs. These are used as types and properties for plugin:UI
- communication, as well as for saving state. */
- LV2_URID RawAudio;
- LV2_URID channelID;
- LV2_URID audioData;
- LV2_URID ui_On;
- LV2_URID ui_Off;
- LV2_URID ui_State;
- LV2_URID ui_spp;
- LV2_URID ui_amp;
+ // URIs defined in LV2 specifications
+ LV2_URID atom_Vector;
+ LV2_URID atom_Float;
+ LV2_URID atom_Int;
+ LV2_URID atom_eventTransfer;
+ LV2_URID param_sampleRate;
+
+ /* URIs defined for this plugin. It is best to re-use existing URIs as
+ much as possible, but plugins may need more vocabulary specific to their
+ needs. These are used as types and properties for plugin:UI
+ communication, as well as for saving state. */
+ LV2_URID RawAudio;
+ LV2_URID channelID;
+ LV2_URID audioData;
+ LV2_URID ui_On;
+ LV2_URID ui_Off;
+ LV2_URID ui_State;
+ LV2_URID ui_spp;
+ LV2_URID ui_amp;
} ScoLV2URIs;
static inline void
map_sco_uris(LV2_URID_Map* map, ScoLV2URIs* uris)
{
- uris->atom_Vector = map->map(map->handle, LV2_ATOM__Vector);
- uris->atom_Float = map->map(map->handle, LV2_ATOM__Float);
- uris->atom_Int = map->map(map->handle, LV2_ATOM__Int);
- uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer);
- uris->param_sampleRate = map->map(map->handle, LV2_PARAMETERS__sampleRate);
-
- /* Note the convention that URIs for types are capitalized, and URIs for
- everything else (mainly properties) are not, just as in LV2
- specifications. */
- uris->RawAudio = map->map(map->handle, SCO_URI "#RawAudio");
- uris->audioData = map->map(map->handle, SCO_URI "#audioData");
- uris->channelID = map->map(map->handle, SCO_URI "#channelID");
- uris->ui_On = map->map(map->handle, SCO_URI "#UIOn");
- uris->ui_Off = map->map(map->handle, SCO_URI "#UIOff");
- uris->ui_State = map->map(map->handle, SCO_URI "#UIState");
- uris->ui_spp = map->map(map->handle, SCO_URI "#ui-spp");
- uris->ui_amp = map->map(map->handle, SCO_URI "#ui-amp");
+ 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 */
+#endif /* SCO_URIS_H */
diff --git a/plugins/eg-scope.lv2/wscript b/plugins/eg-scope.lv2/wscript
deleted file mode 100644
index 4333502..0000000
--- a/plugins/eg-scope.lv2/wscript
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-scope.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
- opt.load('compiler_c')
- opt.load('lv2')
- autowaf.set_options(opt)
-
-def configure(conf):
- conf.load('compiler_c', cache=True)
- conf.load('lv2', cache=True)
- conf.load('autowaf', cache=True)
-
- conf.check_pkg('lv2 >= 1.2.1', uselib_store='LV2')
- conf.check_pkg('cairo >= 1.8.10', uselib_store='CAIRO')
- conf.check_pkg('gtk+-2.0 >= 2.18.0',
- uselib_store='GTK2',
- system=True,
- mandatory=False)
-
-def build(bld):
- bundle = 'eg-scope.lv2'
-
- # Build manifest.ttl by substitution (for portable lib extension)
- for i in ['manifest.ttl', 'examploscope.ttl']:
- bld(features = 'subst',
- source = i + '.in',
- target = 'lv2/%s/%s' % (bundle, i),
- install_path = '${LV2DIR}/%s' % bundle,
- LIB_EXT = bld.env.LV2_LIB_EXT)
-
- # Build plugin library
- obj = bld(features = 'c cshlib lv2lib',
- source = 'examploscope.c',
- name = 'examploscope',
- target = 'lv2/%s/examploscope' % bundle,
- install_path = '${LV2DIR}/%s' % bundle,
- use = 'LV2')
-
- # Build UI library
- if bld.env.HAVE_GTK2:
- obj = bld(features = 'c cshlib lv2lib',
- source = 'examploscope_ui.c',
- name = 'examploscope_ui',
- target = 'lv2/%s/examploscope_ui' % bundle,
- install_path = '${LV2DIR}/%s' % bundle,
- use = 'GTK2 CAIRO LV2')
diff --git a/plugins/literasc.py b/plugins/literasc.py
index 0bcd8f2..74b13a7 100755
--- a/plugins/literasc.py
+++ b/plugins/literasc.py
@@ -1,127 +1,143 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Literasc, a simple literate programming tool for C, C++, and Turtle.
-# Copyright 2012 David Robillard <d@drobilla.net>
-#
-# 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.
+#!/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'
+ "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):
- if code.strip() == '':
+ "Format a block of code and return it as a marked up string."
+
+ if code.strip() == "":
return code
- head = '[source,%s]' % lang
- sep = '-' * len(head) + '\n'
- return head + '\n' + sep + code.strip('\n') + '\n' + sep
+ head = f"[source,{lang}]"
+ code = code.strip("\n")
+ sep = "-" * len(head)
+ return "\n".join([head, sep, code, sep]) + "\n"
+
-def format_c_source(filename, file):
- output = '=== %s ===\n' % os.path.basename(filename)
- chunk = ''
- prev_c = 0
- in_comment = False
+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 = ''
- for line in file:
- code += line
+ n_stars = 0
+ code = "".join(in_file)
# Skip initial license comment
- if code[0:2] == '/*':
- code = code[code.find('*/') + 2:]
+ if code[0:2] == "/*":
+ end = code.find("*/") + 2
+ code = code[end:]
- for c in code:
- if prev_c == '/' and c == '*':
+ 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 c == '*':
+ if char == "*":
n_stars += 1
else:
if n_stars > 1:
- output += format_code('c', chunk[0:len(chunk) - 1])
- chunk = ''
+ output += format_code("c", last_chunk(chunk))
+ chunk = ""
in_comment = True
else:
- chunk += '*' + c
+ chunk += "*" + char
in_comment_start = False
- elif in_comment and prev_c == '*' and c == '/':
+ elif in_comment and prev_c == "*" and char == "/":
if n_stars > 1:
- output += format_text(chunk[0:len(chunk) - 1])
+ output += format_text(last_chunk(chunk))
else:
- output += format_code('c', '/* ' + chunk[0:len(chunk) - 1] + '*/')
+ output += format_code("c", "/* " + last_chunk(chunk) + "*/")
in_comment = False
in_comment_start = False
- chunk = ''
- elif in_comment_start and c == '*':
- n_stars += 1
+ chunk = ""
else:
- chunk += c
- prev_c = c
+ chunk += char
+
+ prev_c = char
+
+ return output + format_code("c", chunk)
- return output + format_code('c', chunk)
-def format_ttl_source(filename, file):
- output = '=== %s ===\n' % os.path.basename(filename)
+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 file:
- is_comment = line.strip().startswith('#')
+ chunk = ""
+ for line in in_file:
+ is_comment = line.strip().startswith("#")
if in_comment:
if is_comment:
- chunk += line.strip().lstrip('# ') + ' \n'
+ chunk += line.strip().lstrip("# ") + " \n"
else:
output += format_text(chunk)
in_comment = False
chunk = line
else:
if is_comment:
- output += format_code('turtle', chunk)
+ output += format_code("turtle", chunk)
in_comment = True
- chunk = line.strip().lstrip('# ') + ' \n'
+ chunk = line.strip().lstrip("# ") + " \n"
else:
chunk += line
if in_comment:
return output + format_text(chunk)
- else:
- return output + format_code('turtle', chunk)
+
+ return output + format_code("turtle", chunk)
+
def gen(out, filenames):
+ "Write markup generated from filenames to an output file."
+
for filename in filenames:
- file = open(filename)
- if not file:
- sys.stderr.write('Failed to open file %s\n' % filename)
- continue
-
- if filename.endswith('.c') or filename.endswith('.h'):
- out.write(format_c_source(filename, file))
- elif filename.endswith('.ttl') or filename.endswith('.ttl.in'):
- out.write(format_ttl_source(filename, file))
- elif filename.endswith('.txt'):
- for line in file:
- out.write(line)
- out.write('\n')
- else:
- sys.stderr.write("Unknown source format `%s'" % (
- filename[filename.find('.'):]))
+ 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"
+ )
- file.close()
if __name__ == "__main__":
if len(sys.argv) < 2:
- sys.stderr.write('Usage: %s FILENAME...\n' % sys.argv[1])
+ sys.stderr.write(f"Usage: {sys.argv[0]} OUT_FILE IN_FILE...\n")
sys.exit(1)
- gen(sys.argv[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
new file mode 100644
index 0000000..ff70af1
--- /dev/null
+++ b/plugins/meson.build
@@ -0,0 +1,82 @@
+# 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/plugins/wscript b/plugins/wscript
deleted file mode 100644
index f5f6571..0000000
--- a/plugins/wscript
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env python
-import os
-
-from waflib.extras import autowaf as autowaf
-import waflib.Logs as Logs
-
-import literasc
-
-def confgure(conf):
- pass
-
-def bld_book_src(task):
- filenames = []
- for i in task.inputs:
- filenames += [i.abspath()]
-
- literasc.gen(open(task.outputs[0].abspath(), 'w'), filenames)
-
-def build(bld):
- files = [bld.path.find_node('README.txt')]
- for i in ['eg-amp.lv2',
- 'eg-midigate.lv2',
- 'eg-fifths.lv2',
- 'eg-metro.lv2',
- 'eg-sampler.lv2',
- 'eg-scope.lv2',
- 'eg-params.lv2']:
- files += bld.path.ant_glob('%s/*.txt' % i)
- files += bld.path.ant_glob('%s/manifest.ttl*' % i)
- files += bld.path.ant_glob('%s/*.ttl' % i)
- files += bld.path.ant_glob('%s/*.c' % i)
- files += bld.path.ant_glob('%s/*.h' % i)
-
- # Compile book sources into book.txt asciidoc source
- bld(rule = bld_book_src,
- source = files,
- target = 'book.txt')
-
- # Run asciidoc to generate book.html
- stylesdir = bld.path.find_node('../doc/').abspath()
- pygments_style = bld.path.find_node('../doc/style.css').abspath()
- bld(rule = 'asciidoc -a stylesdir=%s -a source-highlighter=pygments -a pygments-style=%s -b html -o ${TGT} ${SRC}' % (
- stylesdir, pygments_style),
- source = 'book.txt',
- target = 'book.html')