diff options
Diffstat (limited to 'plugins')
41 files changed, 3516 insertions, 3531 deletions
diff --git a/plugins/.clang-tidy b/plugins/.clang-tidy new file mode 100644 index 0000000..b327b36 --- /dev/null +++ b/plugins/.clang-tidy @@ -0,0 +1,16 @@ +# Copyright 2020-2025 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +Checks: > +  -*-narrowing-conversions, +  -bugprone-assignment-in-if-condition, +  -bugprone-easily-swappable-parameters, +  -bugprone-multi-level-implicit-pointer-conversion, +  -bugprone-suspicious-realloc-usage, +  -cert-err33-c, +  -clang-analyzer-core.NullDereference, +  -hicpp-signed-bitwise, +  -llvm-header-guard, +  -misc-unused-parameters, +  -readability-function-cognitive-complexity, +InheritParentConfig: true diff --git a/plugins/eg-amp.lv2/amp.c b/plugins/eg-amp.lv2/amp.c index c3ba279..c85d6ab 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 @@ -22,7 +9,7 @@     replacing `http:/` with `lv2` any header in the specification bundle can be     included, in this case `lv2.h`.  */ -#include "lv2/core/lv2.h" +#include <lv2/core/lv2.h>  /** Include standard C headers */  #include <math.h> @@ -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,8 +102,7 @@ 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) @@ -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,33 +177,27 @@ 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     or methods in this plugin library will be called concurrently with it.  */ -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-amp.lv2/meson.build b/plugins/eg-amp.lv2/meson.build new file mode 100644 index 0000000..2912e3f --- /dev/null +++ b/plugins/eg-amp.lv2/meson.build @@ -0,0 +1,42 @@ +# Copyright 2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +plugin_sources = files('amp.c') +bundle_name = 'eg-amp.lv2' +data_filenames = ['manifest.ttl.in', 'amp.ttl'] + +module = shared_library( +  'amp', +  plugin_sources, +  c_args: c_suppressions, +  dependencies: [lv2_dep, m_dep], +  gnu_symbol_visibility: 'hidden', +  implicit_include_directories: false, +  install: true, +  install_dir: lv2dir / bundle_name, +  name_prefix: '', +) + +config = configuration_data( +  { +    'LIB_EXT': '.' + module.full_path().split('.')[-1], +  }, +) + +foreach filename : data_filenames +  if filename.endswith('.in') +    configure_file( +      configuration: config, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename.substring(0, -3), +    ) +  else +    configure_file( +      copy: true, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename, +    ) +  endif +endforeach diff --git a/plugins/eg-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..22d5f8a 100644 --- a/plugins/eg-fifths.lv2/fifths.c +++ b/plugins/eg-fifths.lv2/fifths.c @@ -1,70 +1,51 @@ -/* -  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. -*/ - -#include "./uris.h" - -#include "lv2/atom/atom.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/midi/midi.h" -#include "lv2/urid/urid.h" +// Copyright 2014-2016 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include "uris.h" + +#include <lv2/atom/atom.h> +#include <lv2/atom/util.h> +#include <lv2/core/lv2.h> +#include <lv2/core/lv2_util.h> +#include <lv2/log/log.h> +#include <lv2/log/logger.h> +#include <lv2/midi/midi.h> +#include <lv2/urid/urid.h>  #include <stdbool.h>  #include <stdint.h>  #include <stdio.h>  #include <stdlib.h> -enum { -	FIFTHS_IN  = 0, -	FIFTHS_OUT = 1 -}; +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,114 @@ 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; +  const FifthsURIs* uris = &self->uris; + +  // Struct for a 3 byte MIDI event, used for writing notes +  typedef struct { +    LV2_Atom_Event event; +    uint8_t        msg[3]; +  } MIDINoteEvent; + +  // Initially self->out_port contains a Chunk with size set to capacity + +  // Get the capacity +  const uint32_t out_capacity = self->out_port->atom.size; + +  // Write an empty Sequence header to the output +  lv2_atom_sequence_clear(self->out_port); +  self->out_port->atom.type = self->in_port->atom.type; + +  // Read incoming events +  LV2_ATOM_SEQUENCE_FOREACH (self->in_port, ev) { +    if (ev->body.type == uris->midi_Event) { +      const uint8_t* const msg = (const uint8_t*)(ev + 1); +      switch (lv2_midi_message_type(msg)) { +      case LV2_MIDI_MSG_NOTE_ON: +      case LV2_MIDI_MSG_NOTE_OFF: +        // Forward note to output +        lv2_atom_sequence_append_event(self->out_port, out_capacity, ev); + +        if (msg[1] <= 127 - 7) { +          // Make a note one 5th (7 semitones) higher than input +          MIDINoteEvent fifth; + +          // Could simply do fifth.event = *ev here instead... +          fifth.event.time.frames = ev->time.frames; // Same time +          fifth.event.body.type   = ev->body.type;   // Same type +          fifth.event.body.size   = ev->body.size;   // Same size + +          fifth.msg[0] = msg[0];     // Same status +          fifth.msg[1] = msg[1] + 7; // Pitch up 7 semitones +          fifth.msg[2] = msg[2];     // Same velocity + +          // Write 5th event +          lv2_atom_sequence_append_event( +            self->out_port, out_capacity, &fifth.event); +        } +        break; +      default: +        // Forward all other MIDI events directly +        lv2_atom_sequence_append_event(self->out_port, out_capacity, ev); +        break; +      } +    } +  }  }  static const void*  extension_data(const char* uri)  { -	return NULL; +  return NULL;  } -static const LV2_Descriptor descriptor = { -	EG_FIFTHS_URI, -	instantiate, -	connect_port, -	NULL,  // activate, -	run, -	NULL,  // deactivate, -	cleanup, -	extension_data -}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* lv2_descriptor(uint32_t index) +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)  { -	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..d0cbc6a --- /dev/null +++ b/plugins/eg-fifths.lv2/meson.build @@ -0,0 +1,42 @@ +# Copyright 2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +plugin_sources = files('fifths.c') +bundle_name = 'eg-fifths.lv2' +data_filenames = ['manifest.ttl.in', 'fifths.ttl'] + +module = shared_library( +  'fifths', +  plugin_sources, +  c_args: c_suppressions, +  dependencies: [lv2_dep, m_dep], +  gnu_symbol_visibility: 'hidden', +  implicit_include_directories: false, +  install: true, +  install_dir: lv2dir / bundle_name, +  name_prefix: '', +) + +config = configuration_data( +  { +    'LIB_EXT': '.' + module.full_path().split('.')[-1], +  }, +) + +foreach filename : data_filenames +  if filename.endswith('.in') +    configure_file( +      configuration: config, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename.substring(0, -3), +    ) +  else +    configure_file( +      copy: true, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename, +    ) +  endif +endforeach diff --git a/plugins/eg-fifths.lv2/uris.h b/plugins/eg-fifths.lv2/uris.h index 04b09f6..f32a6bd 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/atom/atom.h> +#include <lv2/midi/midi.h> +#include <lv2/patch/patch.h> +#include <lv2/urid/urid.h>  #define EG_FIFTHS_URI "http://lv2plug.in/plugins/eg-fifths"  typedef struct { -	LV2_URID atom_Path; -	LV2_URID atom_Resource; -	LV2_URID atom_Sequence; -	LV2_URID atom_URID; -	LV2_URID atom_eventTransfer; -	LV2_URID midi_Event; -	LV2_URID patch_Set; -	LV2_URID patch_property; -	LV2_URID patch_value; +  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..ee4016e --- /dev/null +++ b/plugins/eg-metro.lv2/meson.build @@ -0,0 +1,42 @@ +# Copyright 2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +plugin_sources = files('metro.c') +bundle_name = 'eg-metro.lv2' +data_filenames = ['manifest.ttl.in', 'metro.ttl'] + +module = shared_library( +  'metro', +  plugin_sources, +  c_args: c_suppressions, +  dependencies: [lv2_dep, m_dep], +  gnu_symbol_visibility: 'hidden', +  implicit_include_directories: false, +  install: true, +  install_dir: lv2dir / bundle_name, +  name_prefix: '', +) + +config = configuration_data( +  { +    'LIB_EXT': '.' + module.full_path().split('.')[-1], +  }, +) + +foreach filename : data_filenames +  if filename.endswith('.in') +    configure_file( +      configuration: config, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename.substring(0, -3), +    ) +  else +    configure_file( +      copy: true, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename, +    ) +  endif +endforeach diff --git a/plugins/eg-metro.lv2/metro.c b/plugins/eg-metro.lv2/metro.c index a7231d2..8e4c738 100644 --- a/plugins/eg-metro.lv2/metro.c +++ b/plugins/eg-metro.lv2/metro.c @@ -1,28 +1,14 @@ -/* -  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. -*/ - -#include "lv2/atom/atom.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/time/time.h" -#include "lv2/urid/urid.h" +// Copyright 2012-2016 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include <lv2/atom/atom.h> +#include <lv2/atom/util.h> +#include <lv2/core/lv2.h> +#include <lv2/core/lv2_util.h> +#include <lv2/log/log.h> +#include <lv2/log/logger.h> +#include <lv2/time/time.h> +#include <lv2/urid/urid.h>  #include <math.h>  #include <stdbool.h> @@ -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,66 @@ 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); +  Metro* self = (Metro*)instance; +  free(self->wave); +  free(self);  }  /** @@ -213,47 +199,48 @@ 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); +  const float    attack_den = self->attack_len ? (float)self->attack_len : 1.0f; + +  if (self->speed == 0.0f) { +    memset(output, 0, (end - begin) * sizeof(float)); +    return; +  } + +  for (uint32_t i = begin; i < end; ++i) { +    switch (self->state) { +    case STATE_ATTACK: +      // Amplitude increases from 0..1 until attack_len +      output[i] = +        self->wave[self->wave_offset] * (float)self->elapsed_len / attack_den; +      if (self->elapsed_len >= self->attack_len) { +        self->state = STATE_DECAY; +      } +      break; +    case STATE_DECAY: +      // Amplitude decreases from 1..0 until attack_len + decay_len +      output[i] = 0.0f; +      output[i] = self->wave[self->wave_offset] * +                  (1 - ((float)(self->elapsed_len - self->attack_len) / +                        (float)self->decay_len)); +      if (self->elapsed_len >= self->attack_len + self->decay_len) { +        self->state = STATE_OFF; +      } +      break; +    case STATE_OFF: +      output[i] = 0.0f; +    } + +    // We continuously play the sine wave regardless of envelope +    self->wave_offset = (self->wave_offset + 1) % self->wave_len; + +    // Update elapsed time and start attack if necessary +    if (++self->elapsed_len == frames_per_beat) { +      self->state       = STATE_ATTACK; +      self->elapsed_len = 0; +    } +  }  }  /** @@ -263,93 +250,92 @@ 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_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..fc6010b --- /dev/null +++ b/plugins/eg-midigate.lv2/meson.build @@ -0,0 +1,42 @@ +# Copyright 2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +plugin_sources = files('midigate.c') +bundle_name = 'eg-midigate.lv2' +data_filenames = ['manifest.ttl.in', 'midigate.ttl'] + +module = shared_library( +  'midigate', +  plugin_sources, +  c_args: c_suppressions, +  dependencies: [lv2_dep, m_dep], +  gnu_symbol_visibility: 'hidden', +  implicit_include_directories: false, +  install: true, +  install_dir: lv2dir / bundle_name, +  name_prefix: '', +) + +config = configuration_data( +  { +    'LIB_EXT': '.' + module.full_path().split('.')[-1], +  }, +) + +foreach filename : data_filenames +  if filename.endswith('.in') +    configure_file( +      configuration: config, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename.substring(0, -3), +    ) +  else +    configure_file( +      copy: true, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename, +    ) +  endif +endforeach diff --git a/plugins/eg-midigate.lv2/midigate.c b/plugins/eg-midigate.lv2/midigate.c index 6c77828..b4861db 100644 --- a/plugins/eg-midigate.lv2/midigate.c +++ b/plugins/eg-midigate.lv2/midigate.c @@ -1,27 +1,14 @@ -/* -  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. -*/ - -#include "lv2/atom/atom.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/midi/midi.h" -#include "lv2/urid/urid.h" +// Copyright 2013-2016 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include <lv2/atom/atom.h> +#include <lv2/atom/util.h> +#include <lv2/core/lv2.h> +#include <lv2/core/lv2_util.h> +#include <lv2/log/log.h> +#include <lv2/log/logger.h> +#include <lv2/midi/midi.h> +#include <lv2/urid/urid.h>  #include <stdbool.h>  #include <stdint.h> @@ -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,20 @@ 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 -}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* +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..83c6ef9 --- /dev/null +++ b/plugins/eg-params.lv2/meson.build @@ -0,0 +1,42 @@ +# Copyright 2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +plugin_sources = files('params.c') +bundle_name = 'eg-params.lv2' +data_filenames = ['manifest.ttl.in', 'params.ttl'] + +module = shared_library( +  'params', +  plugin_sources, +  c_args: c_suppressions, +  dependencies: [lv2_dep, m_dep], +  gnu_symbol_visibility: 'hidden', +  implicit_include_directories: false, +  install: true, +  install_dir: lv2dir / bundle_name, +  name_prefix: '', +) + +config = configuration_data( +  { +    'LIB_EXT': '.' + module.full_path().split('.')[-1], +  }, +) + +foreach filename : data_filenames +  if filename.endswith('.in') +    configure_file( +      configuration: config, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename.substring(0, -3), +    ) +  else +    configure_file( +      copy: true, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename, +    ) +  endif +endforeach diff --git a/plugins/eg-params.lv2/params.c b/plugins/eg-params.lv2/params.c index 94d34a5..9fbaa46 100644 --- a/plugins/eg-params.lv2/params.c +++ b/plugins/eg-params.lv2/params.c @@ -1,33 +1,19 @@ -/* -  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" -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/midi/midi.h" -#include "lv2/patch/patch.h" -#include "lv2/state/state.h" -#include "lv2/urid/urid.h" +#include <lv2/atom/atom.h> +#include <lv2/atom/forge.h> +#include <lv2/atom/util.h> +#include <lv2/core/lv2.h> +#include <lv2/core/lv2_util.h> +#include <lv2/log/log.h> +#include <lv2/log/logger.h> +#include <lv2/midi/midi.h> +#include <lv2/patch/patch.h> +#include <lv2/state/state.h> +#include <lv2/urid/urid.h>  #include <stdbool.h>  #include <stdint.h> @@ -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) { +    const 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,162 @@ 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 -}; +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..c81ea29 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> +// Copyright 2016 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC -  Permission to use, copy, modify, and/or distribute this software for any -  purpose with or without fee is hereby granted, provided that the above -  copyright notice and this permission notice appear in all copies. - -  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#include "lv2/atom/atom.h" -#include "lv2/urid/urid.h" +#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..3319767 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> +// Copyright 2016 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC -  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. +#include <lv2/atom/atom.h> +#include <lv2/atom/forge.h> +#include <lv2/atom/util.h> -  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#include "lv2/atom/forge.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..a0f8799 --- /dev/null +++ b/plugins/eg-sampler.lv2/meson.build @@ -0,0 +1,82 @@ +# Copyright 2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +plugin_sources = files('sampler.c') +ui_sources = files('sampler_ui.c') +bundle_name = 'eg-sampler.lv2' +data_filenames = ['manifest.ttl.in', 'sampler.ttl', 'click.wav'] + +samplerate_dep = dependency( +  'samplerate', +  include_type: 'system', +  required: get_option('plugins'), +  version: '>= 0.1.0', +) + +sndfile_dep = dependency( +  'sndfile', +  include_type: 'system', +  required: get_option('plugins'), +  version: '>= 1.0.0', +) + +gtk2_dep = dependency( +  'gtk+-2.0', +  include_type: 'system', +  required: get_option('plugins'), +  version: '>= 2.18.0', +) + +if samplerate_dep.found() and sndfile_dep.found() +  module = shared_library( +    'sampler', +    plugin_sources, +    c_args: c_suppressions, +    dependencies: [lv2_dep, m_dep, samplerate_dep, sndfile_dep], +    gnu_symbol_visibility: 'hidden', +    implicit_include_directories: false, +    install: true, +    install_dir: lv2dir / bundle_name, +    name_prefix: '', +  ) + +  extension = '.' + module.full_path().split('.')[-1] +  config = configuration_data({'LIB_EXT': extension}) + +  foreach filename : data_filenames +    if filename.endswith('.in') +      configure_file( +        configuration: config, +        input: files(filename), +        install_dir: lv2dir / bundle_name, +        output: filename.substring(0, -3), +      ) +    else +      configure_file( +        copy: true, +        input: files(filename), +        install_dir: lv2dir / bundle_name, +        output: filename, +      ) +    endif +  endforeach + +  if gtk2_dep.found() +    ui_suppressions = c_suppressions +    if cc.get_id() == 'gcc' +      ui_suppressions += ['-Wno-strict-overflow'] +    endif + +    shared_library( +      'sampler_ui', +      ui_sources, +      c_args: ui_suppressions, +      dependencies: [lv2_dep, gtk2_dep], +      gnu_symbol_visibility: 'hidden', +      implicit_include_directories: false, +      install: true, +      install_dir: lv2dir / bundle_name, +      name_prefix: '', +    ) +  endif +endif diff --git a/plugins/eg-sampler.lv2/peaks.h b/plugins/eg-sampler.lv2/peaks.h index 45d3465..ff91546 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/forge.h" +#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..6fc04c5 100644 --- a/plugins/eg-sampler.lv2/sampler.c +++ b/plugins/eg-sampler.lv2/sampler.c @@ -1,38 +1,25 @@ -/* -  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"  #include "uris.h" -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/midi/midi.h" -#include "lv2/state/state.h" -#include "lv2/urid/urid.h" -#include "lv2/worker/worker.h" - +#include <lv2/atom/atom.h> +#include <lv2/atom/forge.h> +#include <lv2/atom/util.h> +#include <lv2/core/lv2.h> +#include <lv2/core/lv2_util.h> +#include <lv2/log/log.h> +#include <lv2/log/logger.h> +#include <lv2/midi/midi.h> +#include <lv2/state/state.h> +#include <lv2/urid/urid.h> +#include <lv2/worker/worker.h> + +#include <samplerate.h>  #include <sndfile.h>  #include <math.h> @@ -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; +  const 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,54 +297,59 @@ 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. */ @@ -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,121 @@ 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 -}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* lv2_descriptor(uint32_t index) +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)  { -	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..769dde0 100644 --- a/plugins/eg-sampler.lv2/sampler_ui.c +++ b/plugins/eg-sampler.lv2/sampler_ui.c @@ -1,33 +1,19 @@ -/* -  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" -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/midi/midi.h" -#include "lv2/ui/ui.h" -#include "lv2/urid/urid.h" +#include <lv2/atom/atom.h> +#include <lv2/atom/forge.h> +#include <lv2/atom/util.h> +#include <lv2/core/lv2.h> +#include <lv2/core/lv2_util.h> +#include <lv2/log/log.h> +#include <lv2/log/logger.h> +#include <lv2/midi/midi.h> +#include <lv2/ui/ui.h> +#include <lv2/urid/urid.h>  #include <cairo.h>  #include <gdk/gdk.h> @@ -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, -	          ¬e_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, +            ¬e_on);  }  static void  request_peaks(SamplerUI* ui, uint32_t n_peaks)  { -	if (n_peaks == ui->requested_n_peaks) { -		return; -	} - -	lv2_atom_forge_set_buffer(&ui->forge, ui->forge_buf, sizeof(ui->forge_buf)); - -	LV2_Atom_Forge_Frame frame; -	lv2_atom_forge_object(&ui->forge, &frame, 0, ui->uris.patch_Get); -	lv2_atom_forge_key(&ui->forge, ui->uris.patch_accept); -	lv2_atom_forge_urid(&ui->forge, ui->precv.uris.peaks_PeakUpdate); -	lv2_atom_forge_key(&ui->forge, ui->precv.uris.peaks_total); -	lv2_atom_forge_int(&ui->forge, n_peaks); -	lv2_atom_forge_pop(&ui->forge, &frame); - -	LV2_Atom* msg = lv2_atom_forge_deref(&ui->forge, frame.ref); -	ui->write(ui->controller, 0, lv2_atom_total_size(msg), -	          ui->uris.atom_eventTransfer, -	          msg); - -	ui->requested_n_peaks = n_peaks; +  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,115 @@ 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; +  const SamplerUI* ui = (const SamplerUI*)handle; +  if (ui->window) { +    gtk_main_iteration_do(false); +  } +  return 0;  }  static const void*  extension_data(const char* uri)  { -	static const LV2UI_Show_Interface show = { ui_show, ui_hide }; -	static const LV2UI_Idle_Interface idle = { ui_idle }; -	if (!strcmp(uri, LV2_UI__showInterface)) { -		return &show; -	} 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* +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..9922f51 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/midi/midi.h" -#include "lv2/parameters/parameters.h" -#include "lv2/patch/patch.h" -#include "lv2/state/state.h" +#include <lv2/atom/atom.h> +#include <lv2/atom/forge.h> +#include <lv2/atom/util.h> +#include <lv2/midi/midi.h> +#include <lv2/parameters/parameters.h> +#include <lv2/patch/patch.h> +#include <lv2/urid/urid.h> +#include <stdint.h>  #include <stdio.h> -#define EG_SAMPLER_URI          "http://lv2plug.in/plugins/eg-sampler" +#define EG_SAMPLER_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*)&value[1];  } -#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..5cdb610 100644 --- a/plugins/eg-scope.lv2/examploscope.c +++ b/plugins/eg-scope.lv2/examploscope.c @@ -1,31 +1,18 @@ -/* -  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. -*/ - -#include "./uris.h" - -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/core/lv2_util.h" -#include "lv2/log/log.h" -#include "lv2/log/logger.h" -#include "lv2/state/state.h" -#include "lv2/urid/urid.h" +// Copyright 2016 David Robillard <d@drobilla.net> +// Copyright 2013 Robin Gareus <robin@gareus.org> +// SPDX-License-Identifier: ISC + +#include "uris.h" + +#include <lv2/atom/atom.h> +#include <lv2/atom/forge.h> +#include <lv2/atom/util.h> +#include <lv2/core/lv2.h> +#include <lv2/core/lv2_util.h> +#include <lv2/log/log.h> +#include <lv2/log/logger.h> +#include <lv2/state/state.h> +#include <lv2/urid/urid.h>  #include <stdbool.h>  #include <stdint.h> @@ -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 @@ -181,140 +169,138 @@ connect_port(LV2_Handle handle,     http://lv2plug.in/ns/ext/atom#Float[Float].  */  static void -tx_rawaudio(LV2_Atom_Forge* forge, -            ScoLV2URIs*     uris, -            const int32_t   channel, -            const size_t    n_samples, -            const float*    data) +tx_rawaudio(LV2_Atom_Forge*   forge, +            const ScoLV2URIs* uris, +            const int32_t     channel, +            const size_t      n_samples, +            const float*      data)  { -	LV2_Atom_Forge_Frame frame; +  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, &, -					                    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, &, 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,67 @@ 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 -}; - -LV2_SYMBOL_EXPORT -const LV2_Descriptor* +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..4f366aa 100644 --- a/plugins/eg-scope.lv2/examploscope_ui.c +++ b/plugins/eg-scope.lv2/examploscope_ui.c @@ -1,27 +1,14 @@ -/* -  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" +#include "uris.h" -#include "lv2/atom/atom.h" -#include "lv2/atom/forge.h" -#include "lv2/atom/util.h" -#include "lv2/core/lv2.h" -#include "lv2/ui/ui.h" -#include "lv2/urid/urid.h" +#include <lv2/atom/atom.h> +#include <lv2/atom/forge.h> +#include <lv2/atom/util.h> +#include <lv2/core/lv2.h> +#include <lv2/ui/ui.h> +#include <lv2/urid/urid.h>  #include <cairo.h>  #include <gdk/gdk.h> @@ -38,7 +25,7 @@  #include <string.h>  // Drawing area size -#define DAWIDTH  (640) +#define DAWIDTH (640)  #define DAHEIGHT (200)  /** @@ -53,96 +40,104 @@     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); + +  lv2_atom_forge_set_buffer(&ui->forge, NULL, 0);  }  /** Notify backend that UI is closed. */  static void  send_ui_disable(LV2UI_Handle handle)  { -	EgScopeUI* ui = (EgScopeUI*)handle; -	send_ui_state(handle); - -	uint8_t obj_buf[64]; -	lv2_atom_forge_set_buffer(&ui->forge, obj_buf, sizeof(obj_buf)); - -	LV2_Atom_Forge_Frame frame; -	LV2_Atom*            msg = (LV2_Atom*)lv2_atom_forge_object( -		&ui->forge, &frame, 0, ui->uris.ui_Off); -	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); + +  lv2_atom_forge_set_buffer(&ui->forge, NULL, 0);  }  /** @@ -153,32 +148,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; +  const EgScopeUI* ui = (const EgScopeUI*)data; +  if (!ui->updating) { +    // Only send UI state if the change is from user interaction +    send_ui_state(data); +  } +  return TRUE;  }  /** @@ -189,128 +187,130 @@ 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); +  assert(ui->n_channels <= 2U); + +  for (uint32_t c = 0; c < ui->n_channels; ++c) { +    ScoChan* chn = &ui->chn[c]; + +    /* Drawing area Y-position of given sample-value. +     * Note: cairo-pixel at 0 spans -0.5 .. +0.5, hence (DAHEIGHT / 2.0 -0.5) +     * also the cairo Y-axis points upwards (hence 'minus value') +     * +     * == (   DAHEIGHT * (CHN)        // channel offset +     *      + (DAHEIGHT / 2) - 0.5    // vertical center -- '0' +     *      - (DAHEIGHT / 2) * (VAL) * (GAIN) +     *    ) +     */ +    const float chn_y_offset = (DAHEIGHT * c) + (DAHEIGHT * 0.5f) - 0.5f; +    const float chn_y_scale  = (DAHEIGHT * 0.5f) * gain; + +#define CYPOS(VAL) (chn_y_offset - ((VAL) * chn_y_scale)) + +    cairo_save(cr); + +    /* Restrict drawing to current channel area, don't bleed drawing into +       neighboring channels. */ +    cairo_rectangle(cr, 0, DAHEIGHT * c, DAWIDTH, DAHEIGHT); +    cairo_clip(cr); + +    // Set color of wave-form +    cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0); + +    /* This is a somewhat 'smart' mechanism to plot audio data using +       alternating up/down line-directions.  It works well for both cases: +       1 pixel <= 1 sample and 1 pixel represents more than 1 sample, but +       is not ideal for either. */ +    if (start == chn->idx) { +      cairo_move_to(cr, start - 0.5, CYPOS(0)); +    } else { +      cairo_move_to(cr, start - 0.5, CYPOS(chn->data_max[start])); +    } + +    uint32_t pathlength = 0; +    for (uint32_t i = start; i < end; ++i) { +      if (i == chn->idx) { +        continue; +      } + +      if (i % 2) { +        cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i])); +        cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i])); +        ++pathlength; +      } else { +        cairo_line_to(cr, i - .5, CYPOS(chn->data_max[i])); +        cairo_line_to(cr, i - .5, CYPOS(chn->data_min[i])); +        ++pathlength; +      } + +      /** Limit the max cairo path length.  This is an optimization trade +          off: too short path: high load CPU/GPU load.  too-long path: +          bad anti-aliasing, or possibly lost points */ +      if (pathlength > MAX_CAIRO_PATH) { +        pathlength = 0; +        cairo_stroke(cr); +        if (i % 2) { +          cairo_move_to(cr, i - .5, CYPOS(chn->data_max[i])); +        } else { +          cairo_move_to(cr, i - .5, CYPOS(chn->data_min[i])); +        } +      } +    } + +    if (pathlength > 0) { +      cairo_stroke(cr); +    } + +    // Draw current position vertical line if display is slow +    if (ui->stride >= ui->rate / 4800.0f || ui->paused) { +      cairo_set_source_rgba(cr, .9, .2, .2, .6); +      cairo_move_to(cr, chn->idx - .5, DAHEIGHT * c); +      cairo_line_to(cr, chn->idx - .5, DAHEIGHT * (c + 1)); +      cairo_stroke(cr); +    } + +    // Undo the 'clipping' restriction +    cairo_restore(cr); + +    // Channel separator +    if (c > 0) { +      cairo_set_source_rgba(cr, .5, .5, .5, 1.0); +      cairo_move_to(cr, 0, (DAHEIGHT * c) - .5); +      cairo_line_to(cr, DAWIDTH, (DAHEIGHT * c) - .5); +      cairo_stroke(cr); +    } + +    // Zero scale line +    cairo_set_source_rgba(cr, .3, .3, .7, .5); +    cairo_move_to(cr, 0, (DAHEIGHT * (c + .5)) - .5); +    cairo_line_to(cr, DAWIDTH, (DAHEIGHT * (c + .5)) - .5); +    cairo_stroke(cr); +  } + +  cairo_destroy(cr); +  return TRUE;  }  /** @@ -332,34 +332,34 @@ on_expose_event(GtkWidget* widget, GdkEventExpose* ev, gpointer data)     and https://github.com/x42/sisco.lv2  */  static int -process_channel(EgScopeUI*   ui, -                ScoChan*     chn, -                const size_t n_elem, -                float const* data, -                uint32_t*    idx_start, -                uint32_t*    idx_end) +process_channel(const EgScopeUI* ui, +                ScoChan*         chn, +                const size_t     n_elem, +                float const*     data, +                uint32_t*        idx_start, +                uint32_t*        idx_end)  { -	int overflow = 0; -	*idx_start = chn->idx; -	for (size_t i = 0; i < n_elem; ++i) { -		if (data[i] < chn->data_min[chn->idx]) { -			chn->data_min[chn->idx] = data[i]; -		} -		if (data[i] > chn->data_max[chn->idx]) { -			chn->data_max[chn->idx] = data[i]; -		} -		if (++chn->sub >= ui->stride) { -			chn->sub = 0; -			chn->idx = (chn->idx + 1) % DAWIDTH; -			if (chn->idx == 0) { -				++overflow; -			} -			chn->data_min[chn->idx] = 1.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 +372,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 +434,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, &_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, +                                          &_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 +636,32 @@ 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* +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..84e17ba --- /dev/null +++ b/plugins/eg-scope.lv2/meson.build @@ -0,0 +1,64 @@ +# Copyright 2022 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +plugin_sources = files('examploscope.c') +ui_sources = files('examploscope_ui.c') +bundle_name = 'eg-scope.lv2' +data_filenames = ['manifest.ttl.in', 'examploscope.ttl.in'] + +gtk2_dep = dependency( +  'gtk+-2.0', +  include_type: 'system', +  required: get_option('plugins'), +  version: '>= 2.18.0', +) + +module = shared_library( +  'examploscope', +  plugin_sources, +  c_args: c_suppressions, +  dependencies: [lv2_dep, m_dep], +  gnu_symbol_visibility: 'hidden', +  implicit_include_directories: false, +  install: true, +  install_dir: lv2dir / bundle_name, +  name_prefix: '', +) + +config = configuration_data( +  { +    'LIB_EXT': '.' + module.full_path().split('.')[-1], +  }, +) + +foreach filename : data_filenames +  if filename.endswith('.in') +    configure_file( +      configuration: config, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename.substring(0, -3), +    ) +  else +    configure_file( +      copy: true, +      input: files(filename), +      install_dir: lv2dir / bundle_name, +      output: filename, +    ) +  endif +endforeach + +if gtk2_dep.found() +  shared_library( +    'examploscope_ui', +    ui_sources, +    c_args: c_suppressions, +    dependencies: [lv2_dep, gtk2_dep], +    gnu_symbol_visibility: 'hidden', +    implicit_include_directories: false, +    install: true, +    install_dir: lv2dir / bundle_name, +    name_prefix: '', +  ) +endif diff --git a/plugins/eg-scope.lv2/uris.h b/plugins/eg-scope.lv2/uris.h index 2ebaf4e..d9d94be 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" +#include <lv2/atom/atom.h> +#include <lv2/parameters/parameters.h> +#include <lv2/urid/urid.h>  #define SCO_URI "http://lv2plug.in/plugins/eg-scope"  typedef struct { -	// URIs defined in LV2 specifications -	LV2_URID atom_Vector; -	LV2_URID atom_Float; -	LV2_URID atom_Int; -	LV2_URID atom_eventTransfer; -	LV2_URID param_sampleRate; - -	/* URIs defined for this plugin.  It is best to re-use existing URIs as -	   much as possible, but plugins may need more vocabulary specific to their -	   needs.  These are used as types and properties for plugin:UI -	   communication, as well as for saving state. */ -	LV2_URID RawAudio; -	LV2_URID channelID; -	LV2_URID audioData; -	LV2_URID ui_On; -	LV2_URID ui_Off; -	LV2_URID ui_State; -	LV2_URID ui_spp; -	LV2_URID ui_amp; +  // URIs defined in LV2 specifications +  LV2_URID atom_Vector; +  LV2_URID atom_Float; +  LV2_URID atom_Int; +  LV2_URID atom_eventTransfer; +  LV2_URID param_sampleRate; + +  /* URIs defined for this plugin.  It is best to reuse existing URIs as +     much as possible, but plugins may need more vocabulary specific to their +     needs.  These are used as types and properties for plugin:UI +     communication, as well as for saving state. */ +  LV2_URID RawAudio; +  LV2_URID channelID; +  LV2_URID audioData; +  LV2_URID ui_On; +  LV2_URID ui_Off; +  LV2_URID ui_State; +  LV2_URID ui_spp; +  LV2_URID ui_amp;  } ScoLV2URIs;  static inline void  map_sco_uris(LV2_URID_Map* map, ScoLV2URIs* uris)  { -	uris->atom_Vector        = map->map(map->handle, LV2_ATOM__Vector); -	uris->atom_Float         = map->map(map->handle, LV2_ATOM__Float); -	uris->atom_Int           = map->map(map->handle, LV2_ATOM__Int); -	uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); -	uris->param_sampleRate   = map->map(map->handle, LV2_PARAMETERS__sampleRate); - -	/* Note the convention that URIs for types are capitalized, and URIs for -	   everything else (mainly properties) are not, just as in LV2 -	   specifications. */ -	uris->RawAudio  = map->map(map->handle, SCO_URI "#RawAudio"); -	uris->audioData = map->map(map->handle, SCO_URI "#audioData"); -	uris->channelID = map->map(map->handle, SCO_URI "#channelID"); -	uris->ui_On     = map->map(map->handle, SCO_URI "#UIOn"); -	uris->ui_Off    = map->map(map->handle, SCO_URI "#UIOff"); -	uris->ui_State  = map->map(map->handle, SCO_URI "#UIState"); -	uris->ui_spp    = map->map(map->handle, SCO_URI "#ui-spp"); -	uris->ui_amp    = map->map(map->handle, SCO_URI "#ui-amp"); +  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')  |