Compare commits
	
		
			1 Commits
		
	
	
		
			master
			...
			feat/dynsm
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b377e9e8be | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -2,4 +2,3 @@ nimble.paths
 | 
			
		||||
*.so
 | 
			
		||||
*.dll
 | 
			
		||||
/.lv2/
 | 
			
		||||
*.code-workspace
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										95
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										95
									
								
								README.md
									
									
									
									
									
								
							@ -1,7 +1,6 @@
 | 
			
		||||
# nymph
 | 
			
		||||
 | 
			
		||||
A [Nim] library for writing audio and MIDI plugins conforming to the [LV2]
 | 
			
		||||
standard
 | 
			
		||||
A [Nim] library for writing audio and MIDI plugins conforming to the [LV2] standard
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Examples
 | 
			
		||||
@ -23,68 +22,53 @@ Install the `amp.lv2` example plugin:
 | 
			
		||||
    cp -a examples/amp.lv2 ~/.lv2
 | 
			
		||||
 | 
			
		||||
Other example plugins can be found in the [examples](./examples) directory and
 | 
			
		||||
can be built and tested with similar commands. Just change the example name to
 | 
			
		||||
the basename of the plugin's LV2 bundle dir.
 | 
			
		||||
can be built and tested with similar commands, just changing the example name
 | 
			
		||||
to the basename of the plugin's LV2 bundle dir.
 | 
			
		||||
 | 
			
		||||
Currently, there are just a few other example plugins:
 | 
			
		||||
 | 
			
		||||
* [`miditranspose`](./examples/miditranspose_plugin.nim): shows how to handle
 | 
			
		||||
  receiving and sending MIDI events.
 | 
			
		||||
* [`multimodefilter`](./examples/multimodefilter_plugin.nim): shows a
 | 
			
		||||
  multimode state-variable filter implementation ported from C++ to Nim.
 | 
			
		||||
* [`tiltfilter`](./examples/titltfilter_plugin.nim): shows a multimode tilt
 | 
			
		||||
  equalizer filter implementation ported from Rust to Nim.
 | 
			
		||||
* [`faustlpf`](./examples/faustlpf_plugin.nim): shows how to integrate DSP C
 | 
			
		||||
  code generated from a [FAUST] source file in Nim (in this example the FAUST
 | 
			
		||||
  code implements a simple non-resonant low-pass filter from the FAUST
 | 
			
		||||
  standard library).
 | 
			
		||||
Currently, there is only one other example plugin, `multimode_filter`, but
 | 
			
		||||
more will be added soon.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## How To
 | 
			
		||||
 | 
			
		||||
**Note:** I'll use `mydsp` as the base name for the new plugin in the
 | 
			
		||||
examples below. Substitute your own plugin basename wherever you see it used
 | 
			
		||||
below.
 | 
			
		||||
* Install this library:
 | 
			
		||||
 | 
			
		||||
1. Install this library:
 | 
			
		||||
        nimble install https://git.0x20.eu/chris/nymph
 | 
			
		||||
 | 
			
		||||
      nimble install https://github.com/SpotlightKid/nymph.git
 | 
			
		||||
* Copy `examples/amp.lv2` and `examples/amp.nim` into your project and rename
 | 
			
		||||
  them as you like (also rename `amp.lv2/amp.ttl`). I'll use `myplugin` as the
 | 
			
		||||
  base name in the examples below.
 | 
			
		||||
 | 
			
		||||
1. Make a directory named `mydsp.lv2` and copy `examples/amp.lv2/manifest.ttl`
 | 
			
		||||
    into it. Also copy `examples/amp.lv2/amp.ttl` to `mydsp.lv2/mydsp.ttl`.
 | 
			
		||||
* Edit `myplugin.lv2/manifest.ttl`:
 | 
			
		||||
    * Change the plugin URI.
 | 
			
		||||
    * Change the plugin's shared library name defined via `lv2:binary` to
 | 
			
		||||
      `libmyplugin.so`.
 | 
			
		||||
    * Change file name referenced via `rdfs:seeAlso` to `myplugin.ttl`.
 | 
			
		||||
 | 
			
		||||
1. Copy `examples/amp_plugin.nim` into your project as `mydsp_plugin.nim`.
 | 
			
		||||
* Edit `myplugin.lv2/myplugin.ttl`:
 | 
			
		||||
    * Change the plugin URI.
 | 
			
		||||
    * Define audio, control and atom ports as needed.
 | 
			
		||||
 | 
			
		||||
1. Edit `mydsp.lv2/manifest.ttl`:
 | 
			
		||||
   * Change the plugin URI.
 | 
			
		||||
   * Change the plugin's shared library name defined via `lv2:binary` to
 | 
			
		||||
     `libmydsp.so`.
 | 
			
		||||
   * Change file name referenced via `rdfs:seeAlso` to `mydsp.ttl`.
 | 
			
		||||
* Edit `myplugin.nim`:
 | 
			
		||||
    * Change the `PluginUri` constant at the top.
 | 
			
		||||
    * Change the `PluginPort` enum and list the ports in the order defined in
 | 
			
		||||
      `myplugin.ttl`.
 | 
			
		||||
    * Rename and update the definition of the `AmpPlugin` object type and
 | 
			
		||||
      define its members with the appropriate data type for the plugin port
 | 
			
		||||
      they will be connected to. Update the type name in the `instantiate`,
 | 
			
		||||
      `deactivate`, `connectPorts` and `run` procs.
 | 
			
		||||
    * Update and extend the `case` statement in `connectPort` to connect ports
 | 
			
		||||
      to your plugin object instance members.
 | 
			
		||||
    * Implement your DSP code in the `run` proc.
 | 
			
		||||
 | 
			
		||||
1. Edit `mydsp.lv2/mydsp.ttl`:
 | 
			
		||||
   * Change the plugin URI.
 | 
			
		||||
   * Define audio, control and atom ports as needed.
 | 
			
		||||
 | 
			
		||||
1. Edit `mydsp_plugin.nim`:
 | 
			
		||||
   * Change the `PluginUri` constant at the top.
 | 
			
		||||
   * Change the `PluginPort` enum and list the ports in the order defined in
 | 
			
		||||
     `mydsp.ttl`.
 | 
			
		||||
   * Rename and update the definition of the `AmpPlugin` object type and
 | 
			
		||||
     define its members with the appropriate data types for the plugin ports
 | 
			
		||||
     they will be connected to. Update the type name in the `instantiate`,
 | 
			
		||||
     `deactivate`, `connectPorts` and `run` procs.
 | 
			
		||||
   * Update and extend the `case` statement in `connectPort` to connect ports
 | 
			
		||||
     to your plugin object instance members.
 | 
			
		||||
   * Implement your DSP code in the `run` proc.
 | 
			
		||||
 | 
			
		||||
1. Compile the plugin shared library object with:
 | 
			
		||||
* Compile the plugin shared library object with:
 | 
			
		||||
 | 
			
		||||
        nim c --app:lib --noMain:on --mm:arc \
 | 
			
		||||
            --out:mydsp.lv2/libmydsp.so mydsp_plugin.nim
 | 
			
		||||
            --out:myplugin.lv2/libmyplugin.so myplugin.nim
 | 
			
		||||
 | 
			
		||||
   See the definition of the `build_ex` task in the
 | 
			
		||||
   [nymph.nimble](./nymph.nimble#L67) file on how to create a nimble task
 | 
			
		||||
   to simplify compilation and additional compiler args you might want to use.
 | 
			
		||||
    See the definition of the `build_ex` task in the
 | 
			
		||||
    [nymph.nimble](./nymph.nimble#L43) file on how to create a nimble task
 | 
			
		||||
    to simplify compilation.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Dependencies
 | 
			
		||||
@ -99,17 +83,8 @@ Optional:
 | 
			
		||||
* [lv2lint] - For checking conformity of plugin bundles
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## See Also
 | 
			
		||||
 | 
			
		||||
* [lv2-nim](https://gitlab.com/lpirl/lv2-nim) - Older third-party Nim bindings
 | 
			
		||||
  for the LV2 audio plugin specification. Last update in 2021.
 | 
			
		||||
* [offbeat](https://github.com/NimAudio/offbeat) - A [CLAP] based plugin
 | 
			
		||||
  framework in Nim
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[CLAP]: https://cleveraudio.org/
 | 
			
		||||
[FAUST]: https://faust.grame.fr/
 | 
			
		||||
[LV2]: https://lv2plug.in/
 | 
			
		||||
[lv2bm]: https://github.com/moddevices/lv2bm
 | 
			
		||||
[lv2lint]: https://git.open-music-kontrollers.ch/~hp/lv2lint
 | 
			
		||||
[Nim]: https://nim-lang.org/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,14 @@
 | 
			
		||||
@prefix bufs: <http://lv2plug.in/ns/ext/buf-size#> .
 | 
			
		||||
@prefix doap: <http://usefulinc.com/ns/doap#> .
 | 
			
		||||
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
 | 
			
		||||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix opts: <http://lv2plug.in/ns/ext/options#> .
 | 
			
		||||
@prefix params: <http://lv2plug.in/ns/ext/parameters#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
 | 
			
		||||
@prefix unit: <http://lv2plug.in/ns/extensions/units#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:amp>
 | 
			
		||||
    a lv2:Plugin , lv2:AmplifierPlugin , doap:Project ;
 | 
			
		||||
    a lv2:Plugin, lv2:AmplifierPlugin , doap:Project ;
 | 
			
		||||
 | 
			
		||||
    lv2:optionalFeature lv2:hardRTCapable , bufs:boundedBlockLength , opts:options ;
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@
 | 
			
		||||
        lv2:default 0.0 ;
 | 
			
		||||
        lv2:minimum -90.0 ;
 | 
			
		||||
        lv2:maximum 20.0 ;
 | 
			
		||||
        units:unit units:db ;
 | 
			
		||||
        unit:unit unit:db ;
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    rdfs:comment """
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:amp>
 | 
			
		||||
 | 
			
		||||
@ -22,12 +22,9 @@ template db2coeff(db: cfloat): cfloat =
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
 | 
			
		||||
                 bundlePath: cstring; features: ptr UncheckedArray[ptr Lv2Feature]):
 | 
			
		||||
                 bundlePath: cstring; features: ptr ptr Lv2Feature):
 | 
			
		||||
                 Lv2Handle {.cdecl.} =
 | 
			
		||||
    try:
 | 
			
		||||
        return createShared(AmpPlugin)
 | 
			
		||||
    except OutOfMemDefect:
 | 
			
		||||
        return nil
 | 
			
		||||
    return createShared(AmpPlugin)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc connectPort(instance: Lv2Handle; port: cuint;
 | 
			
		||||
@ -81,3 +78,4 @@ proc lv2Descriptor(index: cuint): ptr Lv2Descriptor {.
 | 
			
		||||
        result.deactivate = deactivate
 | 
			
		||||
        result.cleanup = cleanup
 | 
			
		||||
        result.extensionData = extensionData
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										48
									
								
								examples/dynparamsmooth.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								examples/dynparamsmooth.nim
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
## Dynamic Smoothing Using Self Modulating Filter
 | 
			
		||||
## Andrew Simper, Cytomic, 2014, andy@cytomic.com
 | 
			
		||||
## public release: 6th Dec 2016
 | 
			
		||||
## https://cytomic.com/files/dsp/DynamicSmoothing.pdf
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
    DynParamSmooth* = object
 | 
			
		||||
        baseFreq: float
 | 
			
		||||
        sensitivity: float
 | 
			
		||||
        wc: float
 | 
			
		||||
        low1: float
 | 
			
		||||
        low2: float
 | 
			
		||||
        inz: float
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc reset*(self: var DynParamSmooth) =
 | 
			
		||||
    self.low1 = 0.0
 | 
			
		||||
    self.low2 = 0.0
 | 
			
		||||
    self.inz = 0.0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setSampleRate*(self: var DynParamSmooth, sampleRate: float) =
 | 
			
		||||
    self.wc = self.baseFreq / sampleRate
 | 
			
		||||
    self.reset()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc process*(self: var DynParamSmooth, sample: float): float =
 | 
			
		||||
    let low1z = self.low1
 | 
			
		||||
    let low2z = self.low2
 | 
			
		||||
    let bandz = low1z - low2z
 | 
			
		||||
    let wd = self.wc + self.sensitivity * abs(bandz)
 | 
			
		||||
    let g = min(wd * (5.9948827 + wd * (-11.969296 + wd * 15.959062)), 1.0)
 | 
			
		||||
    self.low1 = low1z + g * (0.5 * (sample + self.inz) - low1z)
 | 
			
		||||
    self.low2 = low2z + g * (0.5 * (self.low1 + low1z) - low2z)
 | 
			
		||||
    self.inz = sample
 | 
			
		||||
    return self.low2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc initDynParamSmooth*(
 | 
			
		||||
    baseFreq: float = 2.0,
 | 
			
		||||
    sensitivity: float = 2.0,
 | 
			
		||||
    sampleRate: float = 48_000.0
 | 
			
		||||
): DynParamSmooth =
 | 
			
		||||
    result.baseFreq = baseFreq
 | 
			
		||||
    result.sensitivity = sensitivity
 | 
			
		||||
    result.wc = result.baseFreq / sampleRate
 | 
			
		||||
    result.reset()
 | 
			
		||||
 | 
			
		||||
@ -1,386 +0,0 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// This file was generated using the Faust compiler (https://faust.grame.fr),
 | 
			
		||||
// and the Faust post-processor (https://github.com/SpotlightKid/faustdoctor).
 | 
			
		||||
//
 | 
			
		||||
// Source: faustlpf.dsp
 | 
			
		||||
// Name: FaustLPF
 | 
			
		||||
// Author: Christopher Arndt
 | 
			
		||||
// Copyright: Christopher Arndt, 2024
 | 
			
		||||
// License: MIT
 | 
			
		||||
// Version: 0.1.0
 | 
			
		||||
// FAUST version: 2.76.0
 | 
			
		||||
// FAUST compilation options: -a /home/chris/tmp/tmpnf8hapuk.c -lang c -rui -ct 1 -fm def -cn faustlpf -es 1 -mcd 16 -mdd 1024 -mdy 33 -single -ftz 0 -vec -lv 0 -vs 32
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#include "faustlpf.h"
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Begin the Faust code section
 | 
			
		||||
 | 
			
		||||
#if defined(__GNUC__)
 | 
			
		||||
#   pragma GCC diagnostic push
 | 
			
		||||
#   pragma GCC diagnostic ignored "-Wunused-parameter"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// START INTRINSICS
 | 
			
		||||
// END INTRINSICS
 | 
			
		||||
// START CLASS CODE
 | 
			
		||||
#ifndef FAUSTFLOAT
 | 
			
		||||
#define FAUSTFLOAT float
 | 
			
		||||
#endif 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
#define RESTRICT __restrict
 | 
			
		||||
#else
 | 
			
		||||
#define RESTRICT __restrict__
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "faust/dsp/fastmath.cpp"
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
static float faustlpf_faustpower2_f(float value) {
 | 
			
		||||
    return value * value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef FAUSTCLASS 
 | 
			
		||||
#define FAUSTCLASS faustlpf
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef __APPLE__ 
 | 
			
		||||
#define exp10f __exp10f
 | 
			
		||||
#define exp10 __exp10
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
faustlpf* newfaustlpf() { 
 | 
			
		||||
    faustlpf* dsp = (faustlpf*)calloc(1, sizeof(faustlpf));
 | 
			
		||||
    return dsp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void deletefaustlpf(faustlpf* dsp) { 
 | 
			
		||||
    free(dsp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void metadatafaustlpf(MetaGlue* m) { 
 | 
			
		||||
    m->declare(m->metaInterface, "author", "Christopher Arndt");
 | 
			
		||||
    m->declare(m->metaInterface, "compile_options", "-a /home/chris/tmp/tmpnf8hapuk.c -lang c -rui -ct 1 -fm def -cn faustlpf -es 1 -mcd 16 -mdd 1024 -mdy 33 -single -ftz 0 -vec -lv 0 -vs 32");
 | 
			
		||||
    m->declare(m->metaInterface, "copyright", "Christopher Arndt, 2024");
 | 
			
		||||
    m->declare(m->metaInterface, "filename", "faustlpf.dsp");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/fir:author", "Julius O. Smith III");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/fir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/fir:license", "MIT-style STK-4.3 license");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/iir:author", "Julius O. Smith III");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/iir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/iir:license", "MIT-style STK-4.3 license");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/lowpass0_highpass1", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/lowpass0_highpass1:author", "Julius O. Smith III");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/lowpass:author", "Julius O. Smith III");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/lowpass:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/lowpass:license", "MIT-style STK-4.3 license");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/name", "Faust Filters Library");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/tf2:author", "Julius O. Smith III");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/tf2:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/tf2:license", "MIT-style STK-4.3 license");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/tf2s:author", "Julius O. Smith III");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/tf2s:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/tf2s:license", "MIT-style STK-4.3 license");
 | 
			
		||||
    m->declare(m->metaInterface, "filters.lib/version", "1.3.0");
 | 
			
		||||
    m->declare(m->metaInterface, "license", "MIT");
 | 
			
		||||
    m->declare(m->metaInterface, "maths.lib/author", "GRAME");
 | 
			
		||||
    m->declare(m->metaInterface, "maths.lib/copyright", "GRAME");
 | 
			
		||||
    m->declare(m->metaInterface, "maths.lib/license", "LGPL with exception");
 | 
			
		||||
    m->declare(m->metaInterface, "maths.lib/name", "Faust Math Library");
 | 
			
		||||
    m->declare(m->metaInterface, "maths.lib/version", "2.8.0");
 | 
			
		||||
    m->declare(m->metaInterface, "name", "FaustLPF");
 | 
			
		||||
    m->declare(m->metaInterface, "platform.lib/name", "Generic Platform Library");
 | 
			
		||||
    m->declare(m->metaInterface, "platform.lib/version", "1.3.0");
 | 
			
		||||
    m->declare(m->metaInterface, "signals.lib/name", "Faust Signal Routing Library");
 | 
			
		||||
    m->declare(m->metaInterface, "signals.lib/version", "1.6.0");
 | 
			
		||||
    m->declare(m->metaInterface, "version", "0.1.0");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int getSampleRatefaustlpf(faustlpf* RESTRICT dsp) {
 | 
			
		||||
    return dsp->fSampleRate;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int getNumInputsfaustlpf(faustlpf* RESTRICT dsp) {
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
int getNumOutputsfaustlpf(faustlpf* RESTRICT dsp) {
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void classInitfaustlpf(int sample_rate) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void instanceResetUserInterfacefaustlpf(faustlpf* dsp) {
 | 
			
		||||
    dsp->fHslider0 = (FAUSTFLOAT)(1.5e+04f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void instanceClearfaustlpf(faustlpf* dsp) {
 | 
			
		||||
    /* C99 loop */
 | 
			
		||||
    {
 | 
			
		||||
        int l0;
 | 
			
		||||
        for (l0 = 0; l0 < 4; l0 = l0 + 1) {
 | 
			
		||||
            dsp->fRec1_perm[l0] = 0.0f;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /* C99 loop */
 | 
			
		||||
    {
 | 
			
		||||
        int l1;
 | 
			
		||||
        for (l1 = 0; l1 < 4; l1 = l1 + 1) {
 | 
			
		||||
            dsp->fRec0_perm[l1] = 0.0f;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void instanceConstantsfaustlpf(faustlpf* dsp, int sample_rate) {
 | 
			
		||||
    dsp->fSampleRate = sample_rate;
 | 
			
		||||
    dsp->fConst0 = fminf(1.92e+05f, fmaxf(1.0f, (float)(dsp->fSampleRate)));
 | 
			
		||||
    dsp->fConst1 = 44.1f / dsp->fConst0;
 | 
			
		||||
    dsp->fConst2 = 1.0f - dsp->fConst1;
 | 
			
		||||
    dsp->fConst3 = 3.1415927f / dsp->fConst0;
 | 
			
		||||
}
 | 
			
		||||
    
 | 
			
		||||
void instanceInitfaustlpf(faustlpf* dsp, int sample_rate) {
 | 
			
		||||
    instanceConstantsfaustlpf(dsp, sample_rate);
 | 
			
		||||
    instanceResetUserInterfacefaustlpf(dsp);
 | 
			
		||||
    instanceClearfaustlpf(dsp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void initfaustlpf(faustlpf* dsp, int sample_rate) {
 | 
			
		||||
    classInitfaustlpf(sample_rate);
 | 
			
		||||
    instanceInitfaustlpf(dsp, sample_rate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void buildUserInterfacefaustlpf(faustlpf* dsp, UIGlue* ui_interface) {
 | 
			
		||||
    ui_interface->openVerticalBox(ui_interface->uiInterface, "FaustLPF");
 | 
			
		||||
    ui_interface->declare(ui_interface->uiInterface, &dsp->fHslider0, "1", "");
 | 
			
		||||
    ui_interface->declare(ui_interface->uiInterface, &dsp->fHslider0, "scale", "log");
 | 
			
		||||
    ui_interface->declare(ui_interface->uiInterface, &dsp->fHslider0, "style", "knob");
 | 
			
		||||
    ui_interface->declare(ui_interface->uiInterface, &dsp->fHslider0, "symbol", "cutoff");
 | 
			
		||||
    ui_interface->declare(ui_interface->uiInterface, &dsp->fHslider0, "tooltip", "Low-pass filter cutoff frequency");
 | 
			
		||||
    ui_interface->declare(ui_interface->uiInterface, &dsp->fHslider0, "unit", "Hz");
 | 
			
		||||
    ui_interface->addHorizontalSlider(ui_interface->uiInterface, "Cutoff", &dsp->fHslider0, (FAUSTFLOAT)1.5e+04f, (FAUSTFLOAT)16.0f, (FAUSTFLOAT)1.5e+04f, (FAUSTFLOAT)0.1f);
 | 
			
		||||
    ui_interface->closeBox(ui_interface->uiInterface);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void computefaustlpf(faustlpf* dsp, int count, FAUSTFLOAT** RESTRICT inputs, FAUSTFLOAT** RESTRICT outputs) {
 | 
			
		||||
    FAUSTFLOAT* input0_ptr = inputs[0];
 | 
			
		||||
    FAUSTFLOAT* output0_ptr = outputs[0];
 | 
			
		||||
    float fSlow0 = dsp->fConst1 * fmaxf(16.0f, fminf(1.5e+04f, (float)(dsp->fHslider0)));
 | 
			
		||||
    float fRec1_tmp[36];
 | 
			
		||||
    float* fRec1 = &fRec1_tmp[4];
 | 
			
		||||
    float fZec0[32];
 | 
			
		||||
    float fZec1[32];
 | 
			
		||||
    float fZec2[32];
 | 
			
		||||
    float fRec0_tmp[36];
 | 
			
		||||
    float* fRec0 = &fRec0_tmp[4];
 | 
			
		||||
    int vindex = 0;
 | 
			
		||||
    /* Main loop */
 | 
			
		||||
    for (vindex = 0; vindex <= (count - 32); vindex = vindex + 32) {
 | 
			
		||||
        FAUSTFLOAT* input0 = &input0_ptr[vindex];
 | 
			
		||||
        FAUSTFLOAT* output0 = &output0_ptr[vindex];
 | 
			
		||||
        int vsize = 32;
 | 
			
		||||
        /* Recursive loop 0 */
 | 
			
		||||
        /* Pre code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int j0;
 | 
			
		||||
            for (j0 = 0; j0 < 4; j0 = j0 + 1) {
 | 
			
		||||
                fRec1_tmp[j0] = dsp->fRec1_perm[j0];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                fRec1[i] = fSlow0 + dsp->fConst2 * fRec1[i - 1];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Post code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int j1;
 | 
			
		||||
            for (j1 = 0; j1 < 4; j1 = j1 + 1) {
 | 
			
		||||
                dsp->fRec1_perm[j1] = fRec1_tmp[vsize + j1];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Vectorizable loop 1 */
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                fZec0[i] = fast_tanf(dsp->fConst3 * fRec1[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Vectorizable loop 2 */
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                fZec1[i] = 1.0f / fZec0[i];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Vectorizable loop 3 */
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                fZec2[i] = (fZec1[i] + 1.4142135f) / fZec0[i] + 1.0f;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Recursive loop 4 */
 | 
			
		||||
        /* Pre code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int j2;
 | 
			
		||||
            for (j2 = 0; j2 < 4; j2 = j2 + 1) {
 | 
			
		||||
                fRec0_tmp[j2] = dsp->fRec0_perm[j2];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                fRec0[i] = (float)(input0[i]) - (fRec0[i - 2] * ((fZec1[i] + -1.4142135f) / fZec0[i] + 1.0f) + 2.0f * fRec0[i - 1] * (1.0f - 1.0f / faustlpf_faustpower2_f(fZec0[i]))) / fZec2[i];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Post code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int j3;
 | 
			
		||||
            for (j3 = 0; j3 < 4; j3 = j3 + 1) {
 | 
			
		||||
                dsp->fRec0_perm[j3] = fRec0_tmp[vsize + j3];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Vectorizable loop 5 */
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                output0[i] = (FAUSTFLOAT)((fRec0[i - 2] + fRec0[i] + 2.0f * fRec0[i - 1]) / fZec2[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /* Remaining frames */
 | 
			
		||||
    if (vindex < count) {
 | 
			
		||||
        FAUSTFLOAT* input0 = &input0_ptr[vindex];
 | 
			
		||||
        FAUSTFLOAT* output0 = &output0_ptr[vindex];
 | 
			
		||||
        int vsize = count - vindex;
 | 
			
		||||
        /* Recursive loop 0 */
 | 
			
		||||
        /* Pre code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int j0;
 | 
			
		||||
            for (j0 = 0; j0 < 4; j0 = j0 + 1) {
 | 
			
		||||
                fRec1_tmp[j0] = dsp->fRec1_perm[j0];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                fRec1[i] = fSlow0 + dsp->fConst2 * fRec1[i - 1];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Post code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int j1;
 | 
			
		||||
            for (j1 = 0; j1 < 4; j1 = j1 + 1) {
 | 
			
		||||
                dsp->fRec1_perm[j1] = fRec1_tmp[vsize + j1];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Vectorizable loop 1 */
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                fZec0[i] = fast_tanf(dsp->fConst3 * fRec1[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Vectorizable loop 2 */
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                fZec1[i] = 1.0f / fZec0[i];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Vectorizable loop 3 */
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                fZec2[i] = (fZec1[i] + 1.4142135f) / fZec0[i] + 1.0f;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Recursive loop 4 */
 | 
			
		||||
        /* Pre code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int j2;
 | 
			
		||||
            for (j2 = 0; j2 < 4; j2 = j2 + 1) {
 | 
			
		||||
                fRec0_tmp[j2] = dsp->fRec0_perm[j2];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                fRec0[i] = (float)(input0[i]) - (fRec0[i - 2] * ((fZec1[i] + -1.4142135f) / fZec0[i] + 1.0f) + 2.0f * fRec0[i - 1] * (1.0f - 1.0f / faustlpf_faustpower2_f(fZec0[i]))) / fZec2[i];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Post code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int j3;
 | 
			
		||||
            for (j3 = 0; j3 < 4; j3 = j3 + 1) {
 | 
			
		||||
                dsp->fRec0_perm[j3] = fRec0_tmp[vsize + j3];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Vectorizable loop 5 */
 | 
			
		||||
        /* Compute code */
 | 
			
		||||
        /* C99 loop */
 | 
			
		||||
        {
 | 
			
		||||
            int i;
 | 
			
		||||
            for (i = 0; i < vsize; i = i + 1) {
 | 
			
		||||
                output0[i] = (FAUSTFLOAT)((fRec0[i - 2] + fRec0[i] + 2.0f * fRec0[i - 1]) / fZec2[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
// END CLASS CODE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if defined(__GNUC__)
 | 
			
		||||
#   pragma GCC diagnostic pop
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// End the Faust code section
 | 
			
		||||
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
declare name "FaustLPF";
 | 
			
		||||
declare author "Christopher Arndt";
 | 
			
		||||
declare copyright "Christopher Arndt, 2024";
 | 
			
		||||
declare license "MIT"; 
 | 
			
		||||
declare version "0.1.0";
 | 
			
		||||
 | 
			
		||||
import("stdfaust.lib");
 | 
			
		||||
 | 
			
		||||
cutoff = hslider("[1] Cutoff [symbol:cutoff] [unit:Hz] [scale:log] [style:knob] [tooltip:Low-pass filter cutoff frequency]",
 | 
			
		||||
    15000, 16, 15000, 0.1) : si.smoo;
 | 
			
		||||
 | 
			
		||||
process = fi.lowpass(2, cutoff);
 | 
			
		||||
@ -1,200 +0,0 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// This file was generated using the Faust compiler (https://faust.grame.fr),
 | 
			
		||||
// and the Faust post-processor (https://github.com/SpotlightKid/faustdoctor).
 | 
			
		||||
//
 | 
			
		||||
// Source: faustlpf.dsp
 | 
			
		||||
// Name: FaustLPF
 | 
			
		||||
// Author: Christopher Arndt
 | 
			
		||||
// Copyright: Christopher Arndt, 2024
 | 
			
		||||
// License: MIT
 | 
			
		||||
// Version: 0.1.0
 | 
			
		||||
// FAUST version: 2.76.0
 | 
			
		||||
// FAUST compilation options: -a /home/chris/tmp/tmpkjrmmie8.c -lang c -rui -ct 1 -fm def -cn faustlpf -es 1 -mcd 16 -mdd 1024 -mdy 33 -single -ftz 0 -vec -lv 0 -vs 32
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#ifndef  __faustlpf_H__
 | 
			
		||||
#define  __faustlpf_H__
 | 
			
		||||
 | 
			
		||||
#ifndef FAUSTFLOAT
 | 
			
		||||
#define FAUSTFLOAT float
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef FAUSTCLASS
 | 
			
		||||
#define FAUSTCLASS faustlpf
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
#define RESTRICT __restrict
 | 
			
		||||
#else
 | 
			
		||||
#define RESTRICT __restrict__
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include "faust/gui/CInterface.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int fSampleRate;
 | 
			
		||||
    float fConst0;
 | 
			
		||||
    float fConst1;
 | 
			
		||||
    float fConst2;
 | 
			
		||||
    FAUSTFLOAT fHslider0;
 | 
			
		||||
    float fRec1_perm[4];
 | 
			
		||||
    float fConst3;
 | 
			
		||||
    float fRec0_perm[4];
 | 
			
		||||
} faustlpf;
 | 
			
		||||
 | 
			
		||||
faustlpf* newfaustlpf();
 | 
			
		||||
void deletefaustlpf(faustlpf* dsp);
 | 
			
		||||
void metadatafaustlpf(MetaGlue* m);
 | 
			
		||||
int getSampleRatefaustlpf(faustlpf* RESTRICT dsp);
 | 
			
		||||
int getNumInputsfaustlpf(faustlpf* RESTRICT dsp);
 | 
			
		||||
int getNumOutputsfaustlpf(faustlpf* RESTRICT dsp);
 | 
			
		||||
void classInitfaustlpf(int sample_rate);
 | 
			
		||||
void instanceResetUserInterfacefaustlpf(faustlpf* dsp);
 | 
			
		||||
void instanceClearfaustlpf(faustlpf* dsp);
 | 
			
		||||
void instanceConstantsfaustlpf(faustlpf* dsp, int sample_rate);
 | 
			
		||||
void instanceInitfaustlpf(faustlpf* dsp, int sample_rate);
 | 
			
		||||
void initfaustlpf(faustlpf* dsp, int sample_rate);
 | 
			
		||||
void buildUserInterfacefaustlpf(faustlpf* dsp, UIGlue* ui_interface);
 | 
			
		||||
void computefaustlpf(faustlpf* dsp, int count, FAUSTFLOAT** RESTRICT inputs, FAUSTFLOAT** RESTRICT outputs);
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    FAUSTFLOAT init;
 | 
			
		||||
    FAUSTFLOAT min;
 | 
			
		||||
    FAUSTFLOAT max;
 | 
			
		||||
} ParameterRange;
 | 
			
		||||
 | 
			
		||||
int parameter_group(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    default:
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *parameter_label(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        return "Cutoff";
 | 
			
		||||
    default:
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *parameter_short_label(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        return "Cutoff";
 | 
			
		||||
    default:
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *parameter_style(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    case 0: {
 | 
			
		||||
        return "knob";
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        return "";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *parameter_symbol(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        return "cutoff";
 | 
			
		||||
    default:
 | 
			
		||||
        return "";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *parameter_unit(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        return "Hz";
 | 
			
		||||
    default:
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ParameterRange *parameter_range(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    case 0: {
 | 
			
		||||
        static const ParameterRange range = { 15000.0, 16.0, 15000.0 };
 | 
			
		||||
        return ⦥
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool parameter_is_trigger(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    default:
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool parameter_is_boolean(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    default:
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool parameter_is_enum(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    default:
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool parameter_is_integer(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    default:
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool parameter_is_logarithmic(unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        return true;
 | 
			
		||||
    default:
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FAUSTFLOAT get_parameter(faustlpf* dsp, unsigned index) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        return dsp->fHslider0;
 | 
			
		||||
    default:
 | 
			
		||||
        (void)dsp;
 | 
			
		||||
        return 0.0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_parameter(faustlpf* dsp, unsigned index, FAUSTFLOAT value) {
 | 
			
		||||
    switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        dsp->fHslider0 = value;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        (void)dsp;
 | 
			
		||||
        (void)value;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FAUSTFLOAT get_cutoff(faustlpf* dsp) {
 | 
			
		||||
    return dsp->fHslider0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_cutoff(faustlpf* dsp, FAUSTFLOAT value) {
 | 
			
		||||
    dsp->fHslider0 = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif  /* __faustlpf_H__ */
 | 
			
		||||
@ -1,58 +0,0 @@
 | 
			
		||||
@prefix bufs: <http://lv2plug.in/ns/ext/buf-size#> .
 | 
			
		||||
@prefix doap: <http://usefulinc.com/ns/doap#> .
 | 
			
		||||
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
 | 
			
		||||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix opts: <http://lv2plug.in/ns/ext/options#> .
 | 
			
		||||
@prefix params: <http://lv2plug.in/ns/ext/parameters#> .
 | 
			
		||||
@prefix props: <http://lv2plug.in/ns/ext/port-props#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:faustlpf>
 | 
			
		||||
    a lv2:Plugin , lv2:FilterPlugin , doap:Project ;
 | 
			
		||||
 | 
			
		||||
    lv2:optionalFeature lv2:hardRTCapable , bufs:boundedBlockLength , opts:options ;
 | 
			
		||||
 | 
			
		||||
    opts:supportedOption bufs:nominalBlockLength ,
 | 
			
		||||
                         bufs:maxBlockLength ,
 | 
			
		||||
                         params:sampleRate ;
 | 
			
		||||
 | 
			
		||||
    lv2:port [
 | 
			
		||||
        a lv2:InputPort, lv2:AudioPort ;
 | 
			
		||||
        lv2:index 0 ;
 | 
			
		||||
        lv2:name "Audio In" ;
 | 
			
		||||
        lv2:symbol "in" ;
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:OutputPort, lv2:AudioPort ;
 | 
			
		||||
        lv2:index 1 ;
 | 
			
		||||
        lv2:name "Audio Out" ;
 | 
			
		||||
        lv2:symbol "out" ;
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:InputPort, lv2:ControlPort ;
 | 
			
		||||
        lv2:index 2 ;
 | 
			
		||||
        lv2:name "Cutoff" ;
 | 
			
		||||
        lv2:symbol "cutoff" ;
 | 
			
		||||
        lv2:default 15000.0 ;
 | 
			
		||||
        lv2:minimum 16.0 ;
 | 
			
		||||
        lv2:maximum 15000.0 ;
 | 
			
		||||
        lv2:portProperty props:logarithmic ;
 | 
			
		||||
        units:unit units:hz ;
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    rdfs:comment """
 | 
			
		||||
A 2-pole lowpass filter from the FAUST standard library.
 | 
			
		||||
""" ;
 | 
			
		||||
 | 
			
		||||
    doap:name "nymph faust LPF" ;
 | 
			
		||||
    doap:license <https://spdx.org/licenses/MIT> ;
 | 
			
		||||
 | 
			
		||||
    doap:maintainer [
 | 
			
		||||
        foaf:name "Christopher Arndt" ;
 | 
			
		||||
        foaf:mbox <mailto:info@chrisarndt.de> ;
 | 
			
		||||
        foaf:homepage <https://gitlab.com/SpotlightKid/nymph> ;
 | 
			
		||||
    ] ;
 | 
			
		||||
 | 
			
		||||
    lv2:microVersion 0 ;
 | 
			
		||||
    lv2:minorVersion 1 .
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:faustlpf>
 | 
			
		||||
    a lv2:Plugin ;
 | 
			
		||||
    lv2:binary <libfaustlpf.so> ;
 | 
			
		||||
    rdfs:seeAlso <faustlpf.ttl> .
 | 
			
		||||
 | 
			
		||||
@ -1,35 +0,0 @@
 | 
			
		||||
{.compile: "faustlpf.c".}
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
    faustlpf* = object
 | 
			
		||||
 | 
			
		||||
    ParameterRange* = object
 | 
			
		||||
        init*, min*, max*: cfloat
 | 
			
		||||
 | 
			
		||||
    SampleBuffer* = UncheckedArray[cfloat]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc newfaustlpf*(): ptr faustlpf {.importc.}
 | 
			
		||||
proc deletefaustlpf*(dsp: ptr faustlpf) {.importc.}
 | 
			
		||||
proc initfaustlpf*(dsp: ptr faustlpf, sample_rate: cint) {.importc.}
 | 
			
		||||
proc instanceClearfaustlpf*(dsp: ptr faustlpf) {.importc.}
 | 
			
		||||
proc computefaustlpf*(dsp: ptr faustlpf, count: cint, inputs, outputs: ptr ptr SampleBuffer) {.importc.}
 | 
			
		||||
 | 
			
		||||
proc parameter_range*(index: cuint): ptr ParameterRange {.importc.}
 | 
			
		||||
proc parameter_group*(index: cuint): cint {.importc}
 | 
			
		||||
proc parameter_is_boolean*(index: cuint): bool {.importc}
 | 
			
		||||
proc parameter_is_enum*(index: cuint): bool {.importc}
 | 
			
		||||
proc parameter_is_integer*(index: cuint): bool {.importc}
 | 
			
		||||
proc parameter_is_logarithmic*(index: cuint): bool {.importc}
 | 
			
		||||
proc parameter_is_trigger*(index: cuint): bool {.importc}
 | 
			
		||||
proc parameter_label*(index: cuint): cstring {.importc}
 | 
			
		||||
proc parameter_short_label*(index: cuint): cstring {.importc}
 | 
			
		||||
proc parameter_style*(index: cuint): cstring {.importc}
 | 
			
		||||
proc parameter_symbol*(index: cuint): cstring {.importc}
 | 
			
		||||
proc parameter_unit*(index: cuint): cstring {.importc}
 | 
			
		||||
 | 
			
		||||
proc get_parameter*(dsp: ptr faustlpf, index: cuint): cfloat {.importc}
 | 
			
		||||
proc set_parameter*(dsp: ptr faustlpf, index: cuint, value: cfloat) {.importc}
 | 
			
		||||
 | 
			
		||||
proc get_cutoff*(dsp: ptr faustlpf): cfloat {.importc}
 | 
			
		||||
proc set_cutoff*(dsp: ptr faustlpf, value: cfloat) {.importc}
 | 
			
		||||
@ -1,84 +0,0 @@
 | 
			
		||||
## A FAUST standard library 2-pole lowpass filter LV2 plugin
 | 
			
		||||
 | 
			
		||||
import nymph
 | 
			
		||||
import faustlpf
 | 
			
		||||
 | 
			
		||||
const
 | 
			
		||||
    PluginUri = "urn:nymph:examples:faustlpf"
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
    PluginPort {.pure.} = enum
 | 
			
		||||
        Input, Output, Frequency
 | 
			
		||||
 | 
			
		||||
    FaustLPFPlugin = object
 | 
			
		||||
        input: ptr SampleBuffer
 | 
			
		||||
        output: ptr SampleBuffer
 | 
			
		||||
        freq: ptr cfloat
 | 
			
		||||
        flt: ptr faustlpf
 | 
			
		||||
 | 
			
		||||
proc NimMain() {.cdecl, importc.}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
 | 
			
		||||
                 bundlePath: cstring; features: ptr UncheckedArray[ptr Lv2Feature]):
 | 
			
		||||
                 Lv2Handle {.cdecl.} =
 | 
			
		||||
    try:
 | 
			
		||||
        let plug = cast[ptr FaustLPFPlugin](createShared(FaustLPFPlugin))
 | 
			
		||||
        plug.flt = newfaustlpf()
 | 
			
		||||
        initfaustlpf(plug.flt, sampleRate.cint)
 | 
			
		||||
        return cast[Lv2Handle](plug)
 | 
			
		||||
    except OutOfMemDefect:
 | 
			
		||||
        return nil
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc connectPort(instance: Lv2Handle; port: cuint;
 | 
			
		||||
                 dataLocation: pointer) {.cdecl.} =
 | 
			
		||||
    let plug = cast[ptr FaustLPFPlugin](instance)
 | 
			
		||||
    case cast[PluginPort](port)
 | 
			
		||||
    of PluginPort.Input:
 | 
			
		||||
        plug.input = cast[ptr SampleBuffer](dataLocation)
 | 
			
		||||
    of PluginPort.Output:
 | 
			
		||||
        plug.output = cast[ptr SampleBuffer](dataLocation)
 | 
			
		||||
    of PluginPort.Frequency:
 | 
			
		||||
        plug.freq = cast[ptr cfloat](dataLocation)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc activate(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    let plug = cast[ptr FaustLPFPlugin](instance)
 | 
			
		||||
    instanceClearfaustlpf(plug.flt)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc run(instance: Lv2Handle; nSamples: cuint) {.cdecl.} =
 | 
			
		||||
    let plug = cast[ptr FaustLPFPlugin](instance)
 | 
			
		||||
    plug.flt.set_cutoff(plug.freq[])
 | 
			
		||||
    computefaustlpf(plug.flt, nSamples.cint, addr plug.input, addr plug.output)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc deactivate(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    discard
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc cleanup(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    let plug = cast[ptr FaustLPFPlugin](instance)
 | 
			
		||||
    deletefaustlpf(plug.flt)
 | 
			
		||||
    freeShared(plug)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc extensionData(uri: cstring): pointer {.cdecl.} =
 | 
			
		||||
    return nil
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc lv2Descriptor(index: cuint): ptr Lv2Descriptor {.
 | 
			
		||||
                   cdecl, exportc, dynlib, extern: "lv2_descriptor".} =
 | 
			
		||||
    NimMain()
 | 
			
		||||
 | 
			
		||||
    if index == 0:
 | 
			
		||||
        result = createShared(Lv2Descriptor)
 | 
			
		||||
        result.uri = cstring(PluginUri)
 | 
			
		||||
        result.instantiate = instantiate
 | 
			
		||||
        result.connectPort = connectPort
 | 
			
		||||
        result.activate = activate
 | 
			
		||||
        result.run = run
 | 
			
		||||
        result.deactivate = deactivate
 | 
			
		||||
        result.cleanup = cleanup
 | 
			
		||||
        result.extensionData = extensionData
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:miditranspose>
 | 
			
		||||
    a lv2:Plugin ;
 | 
			
		||||
    lv2:binary <libmiditranspose.so> ;
 | 
			
		||||
    rdfs:seeAlso <miditranspose.ttl> .
 | 
			
		||||
 | 
			
		||||
@ -1,66 +0,0 @@
 | 
			
		||||
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
 | 
			
		||||
@prefix bufs: <http://lv2plug.in/ns/ext/buf-size#> .
 | 
			
		||||
@prefix doap: <http://usefulinc.com/ns/doap#> .
 | 
			
		||||
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
 | 
			
		||||
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
 | 
			
		||||
@prefix opts: <http://lv2plug.in/ns/ext/options#> .
 | 
			
		||||
@prefix params: <http://lv2plug.in/ns/ext/parameters#> .
 | 
			
		||||
@prefix props: <http://lv2plug.in/ns/ext/port-props#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
 | 
			
		||||
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:miditranspose>
 | 
			
		||||
    a lv2:Plugin , lv2:MIDIPlugin , doap:Project ;
 | 
			
		||||
 | 
			
		||||
    lv2:optionalFeature lv2:hardRTCapable , bufs:boundedBlockLength , opts:options ;
 | 
			
		||||
 | 
			
		||||
    lv2:requiredFeature urid:map ;
 | 
			
		||||
 | 
			
		||||
    opts:supportedOption bufs:nominalBlockLength ,
 | 
			
		||||
                         bufs:maxBlockLength ,
 | 
			
		||||
                         params:sampleRate ;
 | 
			
		||||
 | 
			
		||||
    lv2:port [
 | 
			
		||||
        a lv2:InputPort , atom:AtomPort ;
 | 
			
		||||
        lv2:index 0 ;
 | 
			
		||||
        atom:bufferType atom:Sequence ;
 | 
			
		||||
        atom:supports midi:MidiEvent ;
 | 
			
		||||
        lv2:designation lv2:control ;
 | 
			
		||||
        lv2:symbol "midi_in" ;
 | 
			
		||||
        lv2:name "MIDI In"
 | 
			
		||||
    ] ,
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:OutputPort , atom:AtomPort ;
 | 
			
		||||
        lv2:index 1 ;
 | 
			
		||||
        atom:bufferType atom:Sequence ;
 | 
			
		||||
        atom:supports midi:MidiEvent ;
 | 
			
		||||
        lv2:symbol "midi_out" ;
 | 
			
		||||
        lv2:name "MIDI Out"
 | 
			
		||||
    ] ,
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:InputPort, lv2:ControlPort ;
 | 
			
		||||
        lv2:index 2 ;
 | 
			
		||||
        lv2:name "Transposition" ;
 | 
			
		||||
        lv2:symbol "transposition" ;
 | 
			
		||||
        lv2:portProperty lv2:integer ;
 | 
			
		||||
        lv2:default 0 ;
 | 
			
		||||
        lv2:minimum -12 ;
 | 
			
		||||
        lv2:maximum 12 ;
 | 
			
		||||
        units:unit units:semitone12TET
 | 
			
		||||
    ] ; 
 | 
			
		||||
 | 
			
		||||
    rdfs:comment "A simple MIDI transposition LV2 plugin." ;
 | 
			
		||||
 | 
			
		||||
    doap:name "nymph miditranspose" ;
 | 
			
		||||
    doap:license <https://spdx.org/licenses/MIT> ;
 | 
			
		||||
 | 
			
		||||
    doap:maintainer [
 | 
			
		||||
        foaf:name "Christopher Arndt" ;
 | 
			
		||||
        foaf:mbox <mailto:info@chrisarndt.de> ;
 | 
			
		||||
        foaf:homepage <https://gitlab.com/SpotlightKid/nymph> ;
 | 
			
		||||
    ] ;
 | 
			
		||||
 | 
			
		||||
    lv2:microVersion 0 ;
 | 
			
		||||
    lv2:minorVersion 1 .
 | 
			
		||||
@ -1,111 +0,0 @@
 | 
			
		||||
## A simple MIDI transpose LV2 plugin
 | 
			
		||||
 | 
			
		||||
import std/math
 | 
			
		||||
#import std/strformat
 | 
			
		||||
#import std/strutils
 | 
			
		||||
import nymph/[atom, core, midi, util, urid]
 | 
			
		||||
import nymph/atom/util
 | 
			
		||||
 | 
			
		||||
const PluginUri = "urn:nymph:examples:miditranspose"
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
    PluginPort {.pure.} = enum
 | 
			
		||||
        Input, Output, Transposition
 | 
			
		||||
 | 
			
		||||
    MidiTransposePlugin = object
 | 
			
		||||
        input: ptr AtomSequence
 | 
			
		||||
        output: ptr AtomSequence
 | 
			
		||||
        transposition: ptr cfloat
 | 
			
		||||
        map: ptr UridMap
 | 
			
		||||
        midi_urid: Urid
 | 
			
		||||
 | 
			
		||||
    MidiEvent = object
 | 
			
		||||
        size: uint32
 | 
			
		||||
        frames: int64
 | 
			
		||||
        data: ptr UncheckedArray[byte]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
 | 
			
		||||
                 bundlePath: cstring; features: ptr UncheckedArray[ptr Lv2Feature]):
 | 
			
		||||
                 Lv2Handle {.cdecl.} =
 | 
			
		||||
    let amp: ptr MidiTransposePlugin = createShared(MidiTransposePlugin)
 | 
			
		||||
    amp.map = cast[ptr UridMap](lv2FeaturesData(features, lv2UridMap))
 | 
			
		||||
 | 
			
		||||
    if amp.map.isNil:
 | 
			
		||||
        freeShared(amp)
 | 
			
		||||
        return nil
 | 
			
		||||
 | 
			
		||||
    amp.midi_urid = amp.map.map(amp.map.handle, lv2MidiMidiEvent)
 | 
			
		||||
 | 
			
		||||
    return cast[Lv2Handle](amp)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc connectPort(instance: Lv2Handle; port: cuint;
 | 
			
		||||
                 dataLocation: pointer) {.cdecl.} =
 | 
			
		||||
    let amp = cast[ptr MidiTransposePlugin](instance)
 | 
			
		||||
    case cast[PluginPort](port)
 | 
			
		||||
    of PluginPort.Input:
 | 
			
		||||
        amp.input = cast[ptr AtomSequence](dataLocation)
 | 
			
		||||
    of PluginPort.Output:
 | 
			
		||||
        amp.output = cast[ptr AtomSequence](dataLocation)
 | 
			
		||||
    of PluginPort.Transposition:
 | 
			
		||||
        amp.transposition = cast[ptr cfloat](dataLocation)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc activate(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    discard
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc run(instance: Lv2Handle; nSamples: cuint) {.cdecl.} =
 | 
			
		||||
    let amp = cast[ptr MidiTransposePlugin](instance)
 | 
			
		||||
    let outCapacity = amp.output.atom.size
 | 
			
		||||
    atomSequenceClear(amp.output)
 | 
			
		||||
    amp.output.atom.type = amp.input.atom.type
 | 
			
		||||
 | 
			
		||||
    if not atomSequenceIsEmpty(amp.input):
 | 
			
		||||
        #echo &"Event sequence size: {amp.input.atom.size}"
 | 
			
		||||
        let noteOffset = clamp(floor(amp.transposition[] + 0.5), -12, 12).uint8
 | 
			
		||||
 | 
			
		||||
        for ev in amp.input:
 | 
			
		||||
            if ev.body.`type` == amp.midi_urid:
 | 
			
		||||
                var msg = cast[ptr UncheckedArray[uint8]](atomContents(AtomEvent, ev))
 | 
			
		||||
                #echo &"0x{toHex(msg[0], 2)} 0x{toHex(msg[1], 2)} 0x{toHex(msg[2], 2)}"
 | 
			
		||||
 | 
			
		||||
                case midiGetMessageType(msg[]):
 | 
			
		||||
                of midiMsgNoteOff, midiMsgNoteOn, midiMsgNotePressure:
 | 
			
		||||
                    msg[1] = clamp(msg[1] + noteOffset, 0, 127).uint8
 | 
			
		||||
                else:
 | 
			
		||||
                    discard
 | 
			
		||||
 | 
			
		||||
                discard atomSequenceAppendEvent(amp.output, outCapacity, ev)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc deactivate(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    discard
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc cleanup(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    freeShared(cast[ptr MidiTransposePlugin](instance))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc extensionData(uri: cstring): pointer {.cdecl.} =
 | 
			
		||||
    return nil
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc NimMain() {.cdecl, importc.}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc lv2Descriptor(index: cuint): ptr Lv2Descriptor {.
 | 
			
		||||
                   cdecl, exportc, dynlib, extern: "lv2_descriptor".} =
 | 
			
		||||
    NimMain()
 | 
			
		||||
 | 
			
		||||
    if index == 0:
 | 
			
		||||
        result = createShared(Lv2Descriptor)
 | 
			
		||||
        result.uri = cstring(PluginUri)
 | 
			
		||||
        result.instantiate = instantiate
 | 
			
		||||
        result.connectPort = connectPort
 | 
			
		||||
        result.activate = activate
 | 
			
		||||
        result.run = run
 | 
			
		||||
        result.deactivate = deactivate
 | 
			
		||||
        result.cleanup = cleanup
 | 
			
		||||
        result.extensionData = extensionData
 | 
			
		||||
@ -1,19 +0,0 @@
 | 
			
		||||
/******************* BEGIN minarch.h ****************/
 | 
			
		||||
 | 
			
		||||
/************************************************************************
 | 
			
		||||
 FAUST Architecture File for generating a very minimal C interface
 | 
			
		||||
 ************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "faust/gui/CInterface.h"
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 VECTOR INTRINSICS
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
<<includeIntrinsic>>
 | 
			
		||||
 | 
			
		||||
/**************************BEGIN USER SECTION **************************/
 | 
			
		||||
 | 
			
		||||
<<includeclass>>
 | 
			
		||||
 | 
			
		||||
/***************************END USER SECTION ***************************/
 | 
			
		||||
							
								
								
									
										8
									
								
								examples/multimode_filter.lv2/manifest.ttl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								examples/multimode_filter.lv2/manifest.ttl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:multimode-filter>
 | 
			
		||||
    a lv2:Plugin ;
 | 
			
		||||
    lv2:binary <libmultimode_filter.so> ;
 | 
			
		||||
    rdfs:seeAlso <multimode_filter.ttl> .
 | 
			
		||||
 | 
			
		||||
@ -1,16 +1,16 @@
 | 
			
		||||
@prefix bufs: <http://lv2plug.in/ns/ext/buf-size#> .
 | 
			
		||||
@prefix doap: <http://usefulinc.com/ns/doap#> .
 | 
			
		||||
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
 | 
			
		||||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix opts: <http://lv2plug.in/ns/ext/options#> .
 | 
			
		||||
@prefix params: <http://lv2plug.in/ns/ext/parameters#> .
 | 
			
		||||
@prefix props: <http://lv2plug.in/ns/ext/port-props#> .
 | 
			
		||||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
 | 
			
		||||
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
 | 
			
		||||
@prefix unit: <http://lv2plug.in/ns/extensions/units#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:multimodefilter>
 | 
			
		||||
    a lv2:Plugin , lv2:FilterPlugin , doap:Project ;
 | 
			
		||||
<urn:nymph:examples:multimode-filter>
 | 
			
		||||
    a lv2:Plugin, lv2:AmplifierPlugin , doap:Project ;
 | 
			
		||||
 | 
			
		||||
    lv2:optionalFeature lv2:hardRTCapable , bufs:boundedBlockLength , opts:options ;
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@
 | 
			
		||||
        lv2:minimum 16.0 ;
 | 
			
		||||
        lv2:maximum 7000.0 ;
 | 
			
		||||
        lv2:portProperty props:logarithmic;
 | 
			
		||||
        units:unit units:hz ;
 | 
			
		||||
        unit:unit unit:hz ;
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:InputPort, lv2:ControlPort ;
 | 
			
		||||
@ -2,10 +2,10 @@
 | 
			
		||||
 | 
			
		||||
import nymph
 | 
			
		||||
 | 
			
		||||
import paramsmooth
 | 
			
		||||
import dynparamsmooth
 | 
			
		||||
import svf
 | 
			
		||||
 | 
			
		||||
const PluginUri = "urn:nymph:examples:multimodefilter"
 | 
			
		||||
const PluginUri = "urn:nymph:examples:multimode-filter"
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
    SampleBuffer = UncheckedArray[cfloat]
 | 
			
		||||
@ -19,20 +19,17 @@ type
 | 
			
		||||
        cutoff: ptr cfloat
 | 
			
		||||
        q: ptr cfloat
 | 
			
		||||
        mode: ptr cfloat
 | 
			
		||||
        svf: SVFilter
 | 
			
		||||
        smoothCutoff: ParamSmooth
 | 
			
		||||
        svf: FilterSV
 | 
			
		||||
        smoothCutoff: DynParamSmooth
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
 | 
			
		||||
                 bundlePath: cstring; features: ptr UncheckedArray[ptr Lv2Feature]):
 | 
			
		||||
                 bundlePath: cstring; features: ptr ptr Lv2Feature):
 | 
			
		||||
                 Lv2Handle {.cdecl.} =
 | 
			
		||||
    try:
 | 
			
		||||
        let plug = createShared(SVFPlugin)
 | 
			
		||||
        plug.svf = initSVFilter(fmLowPass, sampleRate)
 | 
			
		||||
        plug.smoothCutoff = initParamSmooth(20.0, sampleRate)
 | 
			
		||||
        return cast[Lv2Handle](plug)
 | 
			
		||||
    except OutOfMemDefect:
 | 
			
		||||
        return nil
 | 
			
		||||
     let plug = createShared(SVFPlugin)
 | 
			
		||||
     plug.svf = initFilterSV(fmLowPass, sampleRate)
 | 
			
		||||
     plug.smoothCutoff = initDynParamSmooth(sampleRate=sampleRate)
 | 
			
		||||
     return plug
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc connectPort(instance: Lv2Handle; port: cuint;
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:multimodefilter>
 | 
			
		||||
    a lv2:Plugin ;
 | 
			
		||||
    lv2:binary <libmultimodefilter.so> ;
 | 
			
		||||
    rdfs:seeAlso <multimodefilter.ttl> .
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ proc setSampleRate*(self: var ParamSmooth, sampleRate: float64) =
 | 
			
		||||
        self.z = 0.0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc process*(self: var ParamSmooth, sample: float): float {.inline.} =
 | 
			
		||||
proc process*(self: var ParamSmooth, sample: float): float =
 | 
			
		||||
    self.z = (sample * self.b) + (self.z * self.a)
 | 
			
		||||
    return self.z
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,32 +9,32 @@ type
 | 
			
		||||
    FilterMode* = enum
 | 
			
		||||
        fmLowPass, fmHighPass, fmBandPass, fmBandReject
 | 
			
		||||
 | 
			
		||||
    SVFilter* = object
 | 
			
		||||
    FilterSV* = object
 | 
			
		||||
        mode: FilterMode
 | 
			
		||||
        cutoff, q, lowPass, hiPass, bandPass, bandReject, a, b, maxCutoff: float
 | 
			
		||||
        sampleRate: float64
 | 
			
		||||
        needsUpdate: bool
 | 
			
		||||
        needs_update: bool
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc reset*(self: var SVFilter) =
 | 
			
		||||
proc reset*(self: var FilterSV) =
 | 
			
		||||
    self.lowPass = 0.0
 | 
			
		||||
    self.hiPass = 0.0
 | 
			
		||||
    self.bandPass = 0.0
 | 
			
		||||
    self.bandReject = 0.0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc initSVFilter*(mode: FilterMode = fmLowPass, sampleRate: float64 = 48_000.0): SVFilter =
 | 
			
		||||
proc initFilterSV*(mode: FilterMode = fmLowPass, sampleRate: float64 = 48_000.0): FilterSV =
 | 
			
		||||
    result.mode = mode
 | 
			
		||||
    result.sampleRate = sampleRate
 | 
			
		||||
    result.reset()
 | 
			
		||||
    result.a = 0.0
 | 
			
		||||
    result.b = 0.0
 | 
			
		||||
    result.maxCutoff = sampleRate / 6.0
 | 
			
		||||
    result.needsUpdate = true
 | 
			
		||||
    result.needs_update = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc calcCoef*(self: var SVFilter) =
 | 
			
		||||
    if self.needsUpdate:
 | 
			
		||||
proc calcCoef*(self: var FilterSV) =
 | 
			
		||||
    if self.needs_update:
 | 
			
		||||
        self.a = 2.0 * sin(PI * self.cutoff / self.sampleRate)
 | 
			
		||||
 | 
			
		||||
        if self.q > 0.0:
 | 
			
		||||
@ -42,36 +42,36 @@ proc calcCoef*(self: var SVFilter) =
 | 
			
		||||
        else:
 | 
			
		||||
            self.b = 0.0
 | 
			
		||||
 | 
			
		||||
        self.needsUpdate = false
 | 
			
		||||
        self.needs_update = false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setCutoff*(self: var SVFilter, cutoff: float) =
 | 
			
		||||
proc setCutoff*(self: var FilterSV, cutoff: float) =
 | 
			
		||||
    let fc = min(self.maxCutoff, cutoff)
 | 
			
		||||
 | 
			
		||||
    if fc != self.cutoff:
 | 
			
		||||
        self.cutoff = fc
 | 
			
		||||
        self.needsUpdate = true
 | 
			
		||||
        self.needs_update = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setQ*(self: var SVFilter, q: float) =
 | 
			
		||||
proc setQ*(self: var FilterSV, q: float) =
 | 
			
		||||
    if q != self.q:
 | 
			
		||||
        self.q = q
 | 
			
		||||
        self.needsUpdate = true
 | 
			
		||||
        self.needs_update = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setMode*(self: var SVFilter, mode: FilterMode) =
 | 
			
		||||
proc setMode*(self: var FilterSV, mode: FilterMode) =
 | 
			
		||||
    self.mode = mode
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setSampleRate*(self: var SVFilter, sampleRate: float) =
 | 
			
		||||
proc setSampleRate*(self: var FilterSV, sampleRate: float) =
 | 
			
		||||
    if sampleRate != self.sampleRate:
 | 
			
		||||
        self.sampleRate = sampleRate
 | 
			
		||||
        self.needsUpdate = true
 | 
			
		||||
        self.needs_update = true
 | 
			
		||||
        self.reset()
 | 
			
		||||
        self.calcCoef()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc process*(self: var SVFilter, sample: float): float =
 | 
			
		||||
proc process*(self: var FilterSV, sample: float): float =
 | 
			
		||||
    self.lowPass += self.a * self.bandPass
 | 
			
		||||
    self.hiPass = sample - (self.lowPass + (self.b * self.bandPass))
 | 
			
		||||
    self.bandPass += self.a * self.hiPass
 | 
			
		||||
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:tiltfilter>
 | 
			
		||||
    a lv2:Plugin ;
 | 
			
		||||
    lv2:binary <libtiltfilter.so> ;
 | 
			
		||||
    rdfs:seeAlso <tiltfilter.ttl> .
 | 
			
		||||
 | 
			
		||||
@ -1,90 +0,0 @@
 | 
			
		||||
@prefix bufs: <http://lv2plug.in/ns/ext/buf-size#> .
 | 
			
		||||
@prefix doap: <http://usefulinc.com/ns/doap#> .
 | 
			
		||||
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
 | 
			
		||||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
 | 
			
		||||
@prefix opts: <http://lv2plug.in/ns/ext/options#> .
 | 
			
		||||
@prefix params: <http://lv2plug.in/ns/ext/parameters#> .
 | 
			
		||||
@prefix props: <http://lv2plug.in/ns/ext/port-props#> .
 | 
			
		||||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
 | 
			
		||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 | 
			
		||||
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
 | 
			
		||||
 | 
			
		||||
<urn:nymph:examples:tiltfilter>
 | 
			
		||||
    a lv2:Plugin , lv2:FilterPlugin , doap:Project ;
 | 
			
		||||
 | 
			
		||||
    lv2:optionalFeature lv2:hardRTCapable , bufs:boundedBlockLength , opts:options ;
 | 
			
		||||
 | 
			
		||||
    opts:supportedOption bufs:nominalBlockLength ,
 | 
			
		||||
                         bufs:maxBlockLength ,
 | 
			
		||||
                         params:sampleRate ;
 | 
			
		||||
 | 
			
		||||
    lv2:port [
 | 
			
		||||
        a lv2:InputPort, lv2:AudioPort ;
 | 
			
		||||
        lv2:index 0 ;
 | 
			
		||||
        lv2:name "Audio In" ;
 | 
			
		||||
        lv2:symbol "in" ;
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:OutputPort, lv2:AudioPort ;
 | 
			
		||||
        lv2:index 1 ;
 | 
			
		||||
        lv2:name "Audio Out" ;
 | 
			
		||||
        lv2:symbol "out" ;
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:InputPort, lv2:ControlPort ;
 | 
			
		||||
        lv2:index 2 ;
 | 
			
		||||
        lv2:name "Cutoff" ;
 | 
			
		||||
        lv2:symbol "cutoff" ;
 | 
			
		||||
        lv2:default 10000.0 ;
 | 
			
		||||
        lv2:minimum 20.0 ;
 | 
			
		||||
        lv2:maximum 20000.0 ;
 | 
			
		||||
        units:unit units:hz ;
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:InputPort, lv2:ControlPort ;
 | 
			
		||||
        lv2:index 3 ;
 | 
			
		||||
        lv2:name "Steepness" ;
 | 
			
		||||
        lv2:symbol "steepness" ;
 | 
			
		||||
        lv2:default 1.0 ;
 | 
			
		||||
        lv2:minimum 0.0 ;
 | 
			
		||||
        lv2:maximum 1.0 ;
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
        a lv2:InputPort, lv2:ControlPort ;
 | 
			
		||||
        lv2:index 4 ;
 | 
			
		||||
        lv2:name "Filter mode" ;
 | 
			
		||||
        lv2:symbol "mode" ;
 | 
			
		||||
        lv2:default 0 ;
 | 
			
		||||
        lv2:minimum 0 ;
 | 
			
		||||
        lv2:maximum 2 ;
 | 
			
		||||
        lv2:portProperty lv2:enumeration, lv2:integer ;
 | 
			
		||||
        lv2:scalePoint [
 | 
			
		||||
            rdfs:label "Lowpass" ;
 | 
			
		||||
            rdf:value 0 ;
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            rdfs:label "Highpass" ;
 | 
			
		||||
            rdf:value 1 ;
 | 
			
		||||
        ],
 | 
			
		||||
        [
 | 
			
		||||
            rdfs:label "Bandpass" ;
 | 
			
		||||
            rdf:value 2;
 | 
			
		||||
        ] ;
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    rdfs:comment """
 | 
			
		||||
A tilt EQ audio filter.
 | 
			
		||||
""" ;
 | 
			
		||||
 | 
			
		||||
    doap:name "nymph tilt filter" ;
 | 
			
		||||
    doap:license <https://spdx.org/licenses/MIT> ;
 | 
			
		||||
 | 
			
		||||
    doap:maintainer [
 | 
			
		||||
        foaf:name "Christopher Arndt" ;
 | 
			
		||||
        foaf:mbox <mailto:info@chrisarndt.de> ;
 | 
			
		||||
        foaf:homepage <https://gitlab.com/SpotlightKid/nymph> ;
 | 
			
		||||
    ] ;
 | 
			
		||||
 | 
			
		||||
    lv2:microVersion 0 ;
 | 
			
		||||
    lv2:minorVersion 1 .
 | 
			
		||||
 | 
			
		||||
@ -1,165 +0,0 @@
 | 
			
		||||
##
 | 
			
		||||
## Tilt Filter
 | 
			
		||||
##
 | 
			
		||||
## Nim translation of this Rust implementation:
 | 
			
		||||
##
 | 
			
		||||
## https://github.com/ardura/Actuate/blob/main/src/fx/ArduraFilter.rs
 | 
			
		||||
##
 | 
			
		||||
## Inspired by https://www.musicdsp.org/en/latest/Filters/267-simple-tilt-equalizer.html
 | 
			
		||||
## Lowpass, Bandpass, Highpass based off tilt filter code
 | 
			
		||||
 | 
			
		||||
import math
 | 
			
		||||
 | 
			
		||||
const
 | 
			
		||||
    slopeNeg = -60.0
 | 
			
		||||
    amp = 6.0 / ln(2.0)
 | 
			
		||||
    denorm = pow(10.0, -30.0)
 | 
			
		||||
    minFreq = 20.0
 | 
			
		||||
    maxFreq = 20_000.0
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
    FilterMode* = enum
 | 
			
		||||
        fmLowPass, fmBandPass, fmHighPass
 | 
			
		||||
 | 
			
		||||
    TiltFilter* = object
 | 
			
		||||
        # Filter parameters
 | 
			
		||||
        sampleRate: float64
 | 
			
		||||
        centerFreq: float
 | 
			
		||||
        steepness: float
 | 
			
		||||
        mode: FilterMode
 | 
			
		||||
        # Filter tracking / internal
 | 
			
		||||
        needsUpdate: bool
 | 
			
		||||
        sampleRateX3: float64
 | 
			
		||||
        lowGain: float
 | 
			
		||||
        highGain: float
 | 
			
		||||
        a: float
 | 
			
		||||
        b: float
 | 
			
		||||
        lpOut: float
 | 
			
		||||
        # Band pass separate vars
 | 
			
		||||
        bandALow: float
 | 
			
		||||
        bandBLow: float
 | 
			
		||||
        bandOutLow: float
 | 
			
		||||
        bandAHigh: float
 | 
			
		||||
        bandBHigh: float
 | 
			
		||||
        bandOutHigh: float
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Super useful function to scale an sample 0-1 into other ranges
 | 
			
		||||
proc scaleRange(sample, minOutput, maxOutput: float): float =
 | 
			
		||||
    result = clamp(sample * (maxOutput - minOutput) + minOutput, minOutput, maxOutput)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc initTiltFilter*(centerFreq, steepness: float, mode: FilterMode = fmLowPass, sampleRate: float64 = 48_000.0): TiltFilter =
 | 
			
		||||
    let sampleRateX3 = 3.0 * sampleRate
 | 
			
		||||
 | 
			
		||||
    case mode:
 | 
			
		||||
        # These are the gains for the slopes when math happens later
 | 
			
		||||
        of fmLowPass:
 | 
			
		||||
            result.lowGain = exp(0.0 / amp) - 1.0
 | 
			
		||||
            result.highGain = exp(slopeNeg / amp) - 1.0
 | 
			
		||||
        of fmBandPass:
 | 
			
		||||
            result.lowGain = exp(0.0 / amp) - 1.0
 | 
			
		||||
            result.highGain = exp(slopeNeg / amp) - 1.0
 | 
			
		||||
        of fmHighPass:
 | 
			
		||||
            result.lowGain = exp(slopeNeg / amp) - 1.0
 | 
			
		||||
            result.highGain = exp(0.0 / amp) - 1.0
 | 
			
		||||
 | 
			
		||||
    let omega = 2.0 * PI * centerFreq
 | 
			
		||||
    let n = 1.0 / (scaleRange(steepness, 0.98, 1.2) * (sampleRateX3 + omega))
 | 
			
		||||
    result.a = 2.0 * omega * n
 | 
			
		||||
    result.bandALow = result.a
 | 
			
		||||
    result.bandAHigh = result.a
 | 
			
		||||
    result.b = (sampleRateX3 - omega) * n
 | 
			
		||||
    result.bandBLow = result.b
 | 
			
		||||
    result.bandBHigh = result.b
 | 
			
		||||
    result.lpOut = 0.0
 | 
			
		||||
    result.bandOutLow = 0.0
 | 
			
		||||
    result.bandOutHigh = 0.0
 | 
			
		||||
    result.centerFreq = centerFreq
 | 
			
		||||
    result.sampleRateX3 = sampleRateX3
 | 
			
		||||
    result.steepness = steepness
 | 
			
		||||
    result.sampleRate = sampleRate
 | 
			
		||||
    result.mode = mode
 | 
			
		||||
    result.needsUpdate = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setMode*(self: var TiltFilter, mode: FilterMode) =
 | 
			
		||||
    if mode != self.mode:
 | 
			
		||||
        self.mode = mode
 | 
			
		||||
        self.needsUpdate = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setCenterFreq*(self: var TiltFilter, value: float) =
 | 
			
		||||
    let freq = value.clamp(minFreq, maxFreq)
 | 
			
		||||
 | 
			
		||||
    if freq != self.centerFreq:
 | 
			
		||||
        self.centerFreq = freq
 | 
			
		||||
        self.needsUpdate = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setSteepness*(self: var TiltFilter, value: float) =
 | 
			
		||||
    let steepness = value.clamp(0.0, 1.0)
 | 
			
		||||
 | 
			
		||||
    if steepness != self.steepness:
 | 
			
		||||
        self.steepness = steepness
 | 
			
		||||
        self.needsUpdate = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc setSampleRate*(self: var TiltFilter, sampleRate: float) =
 | 
			
		||||
    if sampleRate != self.sampleRate:
 | 
			
		||||
        self.sampleRate = sampleRate
 | 
			
		||||
        self.sampleRateX3 = self.sampleRate * 3.0
 | 
			
		||||
        self.needsUpdate = true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc reset*(self: var TiltFilter) =
 | 
			
		||||
    discard
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc update*(self: var TiltFilter) =
 | 
			
		||||
    if self.needsUpdate:
 | 
			
		||||
        case self.mode:
 | 
			
		||||
            of fmLowPass:
 | 
			
		||||
                let omega = 2.0 * PI * self.centerFreq
 | 
			
		||||
                let n = 1.0 / (scaleRange(self.steepness, 0.98, 1.2) * (self.sample_rate_x3 + omega))
 | 
			
		||||
                self.b = (self.sampleRateX3 - omega) * n
 | 
			
		||||
                self.lowGain = exp(0.0 / amp) - 1.0
 | 
			
		||||
                self.highGain = exp(slopeNeg / amp) - 1.0
 | 
			
		||||
            of fmBandPass:
 | 
			
		||||
                let width = self.steepness * self.steepness * 500.0
 | 
			
		||||
 | 
			
		||||
                let lowOmega = 2.0 * PI * (self.centerFreq - width).clamp(20.0, 16_000.0)
 | 
			
		||||
                let lowN = 1.0 / (scaleRange(self.steepness, 0.98, 1.2) * (self.sampleRateX3 + lowOmega))
 | 
			
		||||
                self.bandALow = 2.0 * lowOmega * lowN
 | 
			
		||||
                self.bandBLow = (self.sampleRateX3 - lowOmega) * lowN
 | 
			
		||||
 | 
			
		||||
                let highOmega = 2.0 * PI * (self.centerFreq + width).clamp(20.0, 16_000.0);
 | 
			
		||||
                let highN = 1.0 / (scaleRange(self.steepness, 0.98, 1.2) * (self.sampleRateX3 + highOmega))
 | 
			
		||||
                self.bandAHigh = 2.0 * highOmega * highN
 | 
			
		||||
                self.bandBHigh = (self.sampleRateX3 - highOmega) * highN
 | 
			
		||||
 | 
			
		||||
                self.lowGain = exp(0.0 / amp) - 1.0
 | 
			
		||||
                self.highGain = exp(slopeNeg / amp) - 1.0
 | 
			
		||||
            of fmHighPass:
 | 
			
		||||
                let omega = 2.0 * PI * self.centerFreq
 | 
			
		||||
                let n = 1.0 / (scaleRange(self.steepness, 0.98, 1.2) * (self.sampleRateX3 + omega))
 | 
			
		||||
                self.a = 2.0 * omega * n
 | 
			
		||||
                self.b = (self.sampleRateX3 - omega) * n
 | 
			
		||||
                self.lowGain = exp(slopeNeg / amp) - 1.0
 | 
			
		||||
                self.highGain = exp(0.0 / amp) - 1.0
 | 
			
		||||
 | 
			
		||||
        self.needsUpdate = false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Process the input sample using the tilt filter
 | 
			
		||||
##
 | 
			
		||||
proc process*(self: var TiltFilter, sample: float): float =
 | 
			
		||||
    if self.mode == fmBandPass:
 | 
			
		||||
        self.bandOutLow = self.bandALow * sample + self.bandBLow * self.bandOutLow
 | 
			
		||||
        let temp = sample + self.highGain * self.bandOutLow + self.lowGain * (sample - self.bandOutLow)
 | 
			
		||||
        self.bandOutHigh = self.bandAHigh * temp + self.bandBHigh * self.bandOutHigh
 | 
			
		||||
        result = temp + self.lowGain * self.bandOutHigh + self.highGain * (temp - self.bandOutHigh) + denorm
 | 
			
		||||
    else:
 | 
			
		||||
        self.lpOut = self.a * sample + self.b * self.lpOut;
 | 
			
		||||
        result = sample + self.lowGain * self.lpOut + self.highGain * (sample - self.lpOut) + denorm
 | 
			
		||||
@ -1,104 +0,0 @@
 | 
			
		||||
## A tilt EQ / filter LV2 plugin
 | 
			
		||||
 | 
			
		||||
import nymph
 | 
			
		||||
 | 
			
		||||
import paramsmooth
 | 
			
		||||
import tiltfilter
 | 
			
		||||
 | 
			
		||||
const
 | 
			
		||||
    PluginUri = "urn:nymph:examples:tiltfilter"
 | 
			
		||||
    minFreq = 20.0
 | 
			
		||||
    maxFreq = 20_000.0
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
    SampleBuffer = UncheckedArray[cfloat]
 | 
			
		||||
 | 
			
		||||
    PluginPort {.pure.} = enum
 | 
			
		||||
        Input, Output, Frequency, Steepness, Mode
 | 
			
		||||
 | 
			
		||||
    TiltFilterPlugin = object
 | 
			
		||||
        input: ptr SampleBuffer
 | 
			
		||||
        output: ptr SampleBuffer
 | 
			
		||||
        freq: ptr cfloat
 | 
			
		||||
        steepness: ptr cfloat
 | 
			
		||||
        mode: ptr cfloat
 | 
			
		||||
        flt: TiltFilter
 | 
			
		||||
        smoothFreq: ParamSmooth
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
 | 
			
		||||
                 bundlePath: cstring; features: ptr UncheckedArray[ptr Lv2Feature]):
 | 
			
		||||
                 Lv2Handle {.cdecl.} =
 | 
			
		||||
    try:
 | 
			
		||||
        let plug = createShared(TiltFilterPlugin)
 | 
			
		||||
        plug.flt = initTiltFilter(10_000.0, 1.0, fmLowPass, sampleRate)
 | 
			
		||||
        plug.smoothFreq = initParamSmooth(20.0, sampleRate)
 | 
			
		||||
        return cast[Lv2Handle](plug)
 | 
			
		||||
    except OutOfMemDefect:
 | 
			
		||||
        return nil
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc connectPort(instance: Lv2Handle; port: cuint;
 | 
			
		||||
                 dataLocation: pointer) {.cdecl.} =
 | 
			
		||||
    let plug = cast[ptr TiltFilterPlugin](instance)
 | 
			
		||||
    case cast[PluginPort](port)
 | 
			
		||||
    of PluginPort.Input:
 | 
			
		||||
        plug.input = cast[ptr SampleBuffer](dataLocation)
 | 
			
		||||
    of PluginPort.Output:
 | 
			
		||||
        plug.output = cast[ptr SampleBuffer](dataLocation)
 | 
			
		||||
    of PluginPort.Frequency:
 | 
			
		||||
        plug.freq = cast[ptr cfloat](dataLocation)
 | 
			
		||||
    of PluginPort.Steepness:
 | 
			
		||||
        plug.steepness = cast[ptr cfloat](dataLocation)
 | 
			
		||||
    of PluginPort.Mode:
 | 
			
		||||
        plug.mode = cast[ptr cfloat](dataLocation)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc activate(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    let plug = cast[ptr TiltFilterPlugin](instance)
 | 
			
		||||
    plug.flt.reset()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc run(instance: Lv2Handle; nSamples: cuint) {.cdecl.} =
 | 
			
		||||
    let plug = cast[ptr TiltFilterPlugin](instance)
 | 
			
		||||
    let freq = plug.freq[].clamp(minFreq, maxFreq)
 | 
			
		||||
 | 
			
		||||
    plug.flt.setMode(plug.mode[].clamp(0.0, 2.0).FilterMode)
 | 
			
		||||
    plug.flt.setSteepness(plug.steepness[])
 | 
			
		||||
 | 
			
		||||
    for pos in 0 ..< nSamples:
 | 
			
		||||
        plug.flt.setCenterFreq(plug.smoothFreq.process(freq))
 | 
			
		||||
        plug.flt.update()
 | 
			
		||||
        plug.output[pos] = plug.flt.process(plug.input[pos])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc deactivate(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    discard
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc cleanup(instance: Lv2Handle) {.cdecl.} =
 | 
			
		||||
    freeShared(cast[ptr TiltFilterPlugin](instance))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc extensionData(uri: cstring): pointer {.cdecl.} =
 | 
			
		||||
    return nil
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc NimMain() {.cdecl, importc.}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
proc lv2Descriptor(index: cuint): ptr Lv2Descriptor {.
 | 
			
		||||
                   cdecl, exportc, dynlib, extern: "lv2_descriptor".} =
 | 
			
		||||
    NimMain()
 | 
			
		||||
 | 
			
		||||
    if index == 0:
 | 
			
		||||
        result = createShared(Lv2Descriptor)
 | 
			
		||||
        result.uri = cstring(PluginUri)
 | 
			
		||||
        result.instantiate = instantiate
 | 
			
		||||
        result.connectPort = connectPort
 | 
			
		||||
        result.activate = activate
 | 
			
		||||
        result.run = run
 | 
			
		||||
        result.deactivate = deactivate
 | 
			
		||||
        result.cleanup = cleanup
 | 
			
		||||
        result.extensionData = extensionData
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								nymph.nimble
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								nymph.nimble
									
									
									
									
									
								
							@ -25,10 +25,7 @@ type Example = tuple
 | 
			
		||||
 | 
			
		||||
const examples = to_table({
 | 
			
		||||
    "amp": "urn:nymph:examples:amp",
 | 
			
		||||
    "faustlpf": "urn:nymph:examples:faustlpf",
 | 
			
		||||
    "miditranspose": "urn:nymph:examples:miditranspose",
 | 
			
		||||
    "multimodefilter": "urn:nymph:examples:multimodefilter",
 | 
			
		||||
    "tiltfilter": "urn:nymph:examples:tiltfilter",
 | 
			
		||||
    "multimode_filter": "urn:nymph:examples:multimode-filter",
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -50,7 +47,7 @@ proc getExample(task_name: string): Example =
 | 
			
		||||
    result.name = changeFileExt(args[^1], "")
 | 
			
		||||
 | 
			
		||||
    let examplesDir = thisDir() / "examples"
 | 
			
		||||
    result.source = examplesDir / result.name & "_plugin.nim"
 | 
			
		||||
    result.source = examplesDir / changeFileExt(result.name, "nim")
 | 
			
		||||
 | 
			
		||||
    if not fileExists(result.source):
 | 
			
		||||
        quit(&"Example '{result.name}' not found.")
 | 
			
		||||
@ -72,9 +69,6 @@ task build_ex, "Build given example plugin":
 | 
			
		||||
    switch("mm", "arc")
 | 
			
		||||
    switch("out", ex.dll)
 | 
			
		||||
 | 
			
		||||
    when defined(gcc):
 | 
			
		||||
        switch("passC", "-fvisibility=hidden")
 | 
			
		||||
 | 
			
		||||
    when not defined(release) and not defined(debug):
 | 
			
		||||
        echo &"Compiling plugin {ex.name} in release mode."
 | 
			
		||||
        switch("define", "release")
 | 
			
		||||
@ -89,7 +83,7 @@ task lv2lint, "Run lv2lint check on given example plugin":
 | 
			
		||||
    let ex = getExample("lv2lint")
 | 
			
		||||
 | 
			
		||||
    if fileExists(ex.dll):
 | 
			
		||||
        exec(&"lv2lint -s NimMain -s NimDestroyGlobals -I \"{ex.bundle}\" \"{ex.uri}\"")
 | 
			
		||||
        exec(&"lv2lint -s NimMain -I \"{ex.bundle}\" \"{ex.uri}\"")
 | 
			
		||||
    else:
 | 
			
		||||
        echo &"Example '{ex.name}' shared library not found. Use task 'build_ex' to build it."
 | 
			
		||||
 | 
			
		||||
@ -118,3 +112,4 @@ task lv2bm, "Run lv2bm benchmark on given example plugin":
 | 
			
		||||
        exec(&"lv2bm --full-test -i white \"{ex.uri}\"")
 | 
			
		||||
    else:
 | 
			
		||||
        echo &"Example '{ex.name}' shared library not found. Use task 'build_ex' to build it."
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,2 +1,32 @@
 | 
			
		||||
import nymph/core
 | 
			
		||||
export core
 | 
			
		||||
const LV2_CORE_URI* = "http://lv2plug.in/ns/lv2core"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Lv2Handle* = pointer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Lv2Feature* = object
 | 
			
		||||
    uri*: cstring
 | 
			
		||||
    data*: pointer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Lv2Descriptor* = object
 | 
			
		||||
    uri*: cstring
 | 
			
		||||
 | 
			
		||||
    instantiate*: proc(descriptor: ptr Lv2Descriptor, sampleRate: cdouble, bundlePath: cstring,
 | 
			
		||||
                       features: ptr ptr Lv2Feature): Lv2Handle {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    connectPort*: proc(instance: Lv2Handle, port: cuint, dataLocation: pointer) {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    activate*: proc(instance: Lv2Handle) {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    run*: proc(instance: Lv2Handle, sampleCount: cuint) {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    deactivate*: proc(instance: Lv2Handle) {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    cleanup*: proc(instance: Lv2Handle) {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    extensionData*: proc(uri: cstring): pointer {.cdecl.}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type lv2Descriptor* = proc(index: cuint): ptr Lv2Descriptor {.cdecl.}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,188 +0,0 @@
 | 
			
		||||
## Copyright 2008-2016 David Robillard <d@drobilla.net>
 | 
			
		||||
## SPDX-License-Identifier: ISC
 | 
			
		||||
##
 | 
			
		||||
## A generic value container and several data types.
 | 
			
		||||
##
 | 
			
		||||
## See <http://lv2plug.in/ns/ext/atom> for details.
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
import ptrmath
 | 
			
		||||
import urid
 | 
			
		||||
 | 
			
		||||
const
 | 
			
		||||
    lv2AtomBaseUri* ="http://lv2plug.in/ns/ext/atom"
 | 
			
		||||
    lv2AtomPrefix = lv2AtomBaseUri & "#"
 | 
			
		||||
 | 
			
		||||
    lv2AtomAtom* = lv2AtomPrefix & "Atom"
 | 
			
		||||
    lv2AtomAtomPort* = lv2AtomPrefix & "AtomPort"
 | 
			
		||||
    lv2AtomBlank* = lv2AtomPrefix & "Blank"
 | 
			
		||||
    lv2AtomBool* = lv2AtomPrefix & "Bool"
 | 
			
		||||
    lv2AtomChunk* = lv2AtomPrefix & "Chunk"
 | 
			
		||||
    lv2AtomDouble* = lv2AtomPrefix & "Double"
 | 
			
		||||
    lv2AtomEvent* = lv2AtomPrefix & "Event"
 | 
			
		||||
    lv2AtomFloat* = lv2AtomPrefix & "Float"
 | 
			
		||||
    lv2AtomInt* = lv2AtomPrefix & "Int"
 | 
			
		||||
    lv2AtomLiteral* = lv2AtomPrefix & "Literal"
 | 
			
		||||
    lv2AtomLong* = lv2AtomPrefix & "Long"
 | 
			
		||||
    lv2AtomNumber* = lv2AtomPrefix & "Number"
 | 
			
		||||
    lv2AtomObject* = lv2AtomPrefix & "Object"
 | 
			
		||||
    lv2AtomPath* = lv2AtomPrefix & "Path"
 | 
			
		||||
    lv2AtomProperty* = lv2AtomPrefix & "Property"
 | 
			
		||||
    lv2AtomResource* = lv2AtomPrefix & "Resource"
 | 
			
		||||
    lv2AtomSequence* = lv2AtomPrefix & "Sequence"
 | 
			
		||||
    lv2AtomSound* = lv2AtomPrefix & "Sound"
 | 
			
		||||
    lv2AtomString* = lv2AtomPrefix & "String"
 | 
			
		||||
    lv2AtomTuple* = lv2AtomPrefix & "Tuple"
 | 
			
		||||
    lv2AtomUri* = lv2AtomPrefix & "URI"
 | 
			
		||||
    lv2AtomUrid* = lv2AtomPrefix & "URID"
 | 
			
		||||
    lv2AtomVector* = lv2AtomPrefix & "Vector"
 | 
			
		||||
    lv2AtomAtomtransfer* = lv2AtomPrefix & "atomTransfer"
 | 
			
		||||
    lv2AtomBeattime* = lv2AtomPrefix & "beatTime"
 | 
			
		||||
    lv2AtomBuffer* = lv2AtomPrefix & "bufferType"
 | 
			
		||||
    lv2AtomChild* = lv2AtomPrefix & "childType"
 | 
			
		||||
    lv2AtomEventTransfer* = lv2AtomPrefix & "eventTransfer"
 | 
			
		||||
    lv2AtomFrameTime* = lv2AtomPrefix & "frameTime"
 | 
			
		||||
    lv2AtomSupports* = lv2AtomPrefix & "supports"
 | 
			
		||||
    lv2AtomTimeUnit* = lv2AtomPrefix & "timeUnit"
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return a pointer to the contents of an Atom.  The "contents" of an atom
 | 
			
		||||
## is the data past the complete type-specific header.
 | 
			
		||||
## @param type The type of the atom, for example AtomString.
 | 
			
		||||
## @param atom A variable-sized atom.
 | 
			
		||||
##
 | 
			
		||||
template atomContents*(`type`, atom: untyped): pointer =
 | 
			
		||||
    cast[ptr uint8](atom) + sizeof(`type`)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return a pointer to the body of an Atom.  The "body" of an atom is the
 | 
			
		||||
## data just past the Atom head (i.e. the same offset for all types).
 | 
			
		||||
##
 | 
			
		||||
template atomBody*(atom: untyped): pointer =
 | 
			
		||||
    atomContents(Atom, atom)
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
    ## The header of an atom:Atom.
 | 
			
		||||
    Atom* {.bycopy.} = object
 | 
			
		||||
        size*: uint32  ## Size in bytes, not including type and size.
 | 
			
		||||
        `type`*: Urid  ## Type of this atom (mapped URI).
 | 
			
		||||
 | 
			
		||||
    ## An atom:Int or atom:Bool.  May be cast to Atom.
 | 
			
		||||
    AtomInt* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        body*: int32  ## Integer value.
 | 
			
		||||
 | 
			
		||||
    ## An atom:Long.  May be cast to Atom.
 | 
			
		||||
    AtomLong* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        body*: int64  ## Integer value.
 | 
			
		||||
 | 
			
		||||
    ## An atom:Float.  May be cast to Atom.
 | 
			
		||||
    AtomFloat* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        body*: cfloat  ## Floating point value.
 | 
			
		||||
 | 
			
		||||
    ## An atom:Double.  May be cast to Atom.
 | 
			
		||||
    AtomDouble* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        body*: cdouble  ## Floating point value.
 | 
			
		||||
 | 
			
		||||
    ## An atom:Bool.  May be cast to Atom.
 | 
			
		||||
    AtomBool* = distinct AtomInt
 | 
			
		||||
 | 
			
		||||
    ## An atom:URID.  May be cast to Atom.
 | 
			
		||||
    AtomUrid* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        body*: Urid  ## Urid.
 | 
			
		||||
 | 
			
		||||
    ## An atom:String.  May be cast to Atom.
 | 
			
		||||
    AtomString* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        ## Contents (a null-terminated UTF-8 string) follow here.
 | 
			
		||||
 | 
			
		||||
    ## The body of an atom:Literal.
 | 
			
		||||
    AtomLiteralBody* {.bycopy.} = object
 | 
			
		||||
        datatype*: Urid  ## Datatype Urid.
 | 
			
		||||
        lang*: Urid  ## Language Urid.
 | 
			
		||||
        ## Contents (a null-terminated UTF-8 string) follow here.
 | 
			
		||||
 | 
			
		||||
    ## An atom:Literal.  May be cast to Atom.
 | 
			
		||||
    AtomLiteral* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        body*: AtomLiteralBody  ## Body.
 | 
			
		||||
 | 
			
		||||
    ## An atom:Tuple.  May be cast to Atom.
 | 
			
		||||
    AtomTuple* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        ## Contents (a series of complete atoms) follow here.
 | 
			
		||||
 | 
			
		||||
    ## The body of an atom:Vector.
 | 
			
		||||
    AtomVectorBody* {.bycopy.} = object
 | 
			
		||||
        childSize*: uint32  ## The size of each element in the vector.
 | 
			
		||||
        childType*: uint32  ## The of each element in the vector.
 | 
			
		||||
        ## Contents (a series of packed atom bodies) follow here.
 | 
			
		||||
 | 
			
		||||
    ## An atom:Vector.  May be cast to Atom.
 | 
			
		||||
    AtomVector* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        body*: AtomVectorBody  ## Body.
 | 
			
		||||
 | 
			
		||||
    ## The body of an atom:Property (typically in an atom:Object).
 | 
			
		||||
    AtomPropertyBody* {.bycopy.} = object
 | 
			
		||||
        key*: Urid  ## Key (predicate) (mapped URI).
 | 
			
		||||
        context*: Urid  ## Context URID (may be, and generally is, 0).
 | 
			
		||||
        value*: Atom  ## Value atom header.
 | 
			
		||||
        ## Value atom body follows here.
 | 
			
		||||
 | 
			
		||||
    ## An atom:Property.  May be cast to Atom.
 | 
			
		||||
    AtomProperty* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        body*: AtomPropertyBody  ## Body.
 | 
			
		||||
 | 
			
		||||
    ## The body of an atom:Object. May be cast to Atom.
 | 
			
		||||
    AtomObjectBody* {.bycopy.} = object
 | 
			
		||||
        id*: Urid  ## Urid, or 0 for blank.
 | 
			
		||||
        otype*: Urid  ## Urid (same as rdf:type, for fast dispatch).
 | 
			
		||||
        ## Contents (a series of property bodies) follow here.
 | 
			
		||||
 | 
			
		||||
    ## An atom:Object.  May be cast to Atom.
 | 
			
		||||
    AtomObject* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        body*: AtomObjectBody  ## Body.
 | 
			
		||||
 | 
			
		||||
    ## The header of an atom:Event.  Note this is NOT an Atom.
 | 
			
		||||
    AtomEventTime* {.bycopy, union.} = object
 | 
			
		||||
        frames*: int64  ## Time in audio frames.
 | 
			
		||||
        beats*: cdouble  ## Time in beats.
 | 
			
		||||
 | 
			
		||||
    AtomEvent* {.bycopy.} = object
 | 
			
		||||
        time*: AtomEventTime  ## Time stamp.  Which is valid is determined by context.
 | 
			
		||||
        body*: Atom  ## Event body atom header.
 | 
			
		||||
        ## Body atom contents follow here.
 | 
			
		||||
 | 
			
		||||
    ##
 | 
			
		||||
    ## The body of an AtomSequence (a sequence of events).
 | 
			
		||||
    ##
 | 
			
		||||
    ## The unit field is either a Urid that describes an appropriate time stamp
 | 
			
		||||
    ## type, or may be 0 where a default stamp type is known.  For
 | 
			
		||||
    ## lv2Descriptor.run(), the default stamp type is audio frames.
 | 
			
		||||
    ##
 | 
			
		||||
    ## The contents of a sequence is a series of AtomEvent, each aligned
 | 
			
		||||
    ## to 64-bits, for example:
 | 
			
		||||
    ## 
 | 
			
		||||
    ## ```
 | 
			
		||||
    ## | Event 1 (size 6)                              | Event 2
 | 
			
		||||
    ## |       |       |       |       |       |       |       |       |
 | 
			
		||||
    ## | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
 | 
			
		||||
    ## |FRAMES         |SIZE   |TYPE   |DATADATADATAPAD|FRAMES         |...
 | 
			
		||||
    ## ```
 | 
			
		||||
    ##
 | 
			
		||||
    AtomSequenceBody* {.bycopy.} = object
 | 
			
		||||
        unit*: Urid  ## Urid of unit of event time stamps.
 | 
			
		||||
        pad*: uint32  ## Currently unused.
 | 
			
		||||
        ## Contents (a series of events) follow here.
 | 
			
		||||
 | 
			
		||||
    ## An atom:Sequence.
 | 
			
		||||
    AtomSequence* {.bycopy.} = object
 | 
			
		||||
        atom*: Atom  ## Atom header.
 | 
			
		||||
        body*: AtomSequenceBody  ## Body.
 | 
			
		||||
@ -1,199 +0,0 @@
 | 
			
		||||
## Copyright 2008-2015 David Robillard <d@drobilla.net>
 | 
			
		||||
## SPDX-License-Identifier: ISC
 | 
			
		||||
##
 | 
			
		||||
## Helper functions for the LV2 Atom extension.
 | 
			
		||||
##
 | 
			
		||||
## Utilities for working with atoms.
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
import ../atom
 | 
			
		||||
import ../ptrmath
 | 
			
		||||
import ../urid
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Pad a size to 64 bits.
 | 
			
		||||
##
 | 
			
		||||
proc atomPadSize*(size: uint32): uint32 {.inline.} =
 | 
			
		||||
    let mask: uint32 = 7
 | 
			
		||||
    return (size + mask) and not mask
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return the total size of `atom`, including the header.
 | 
			
		||||
##
 | 
			
		||||
proc atomTotalSize*(atom: ptr Atom): uint32 {.inline.} =
 | 
			
		||||
    return cast[uint32](sizeof(atom)) + atom.size
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return true iff `atom` is null.
 | 
			
		||||
##
 | 
			
		||||
proc atomIsNull*(atom: ptr Atom): bool {.inline.} =
 | 
			
		||||
    return atom.isNil or (atom.`type` == Urid(0) and atom.size == 0)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return true iff `a` is equal to `b`.
 | 
			
		||||
##
 | 
			
		||||
proc atomEquals*(a: ptr Atom; b: ptr Atom): bool {.inline.} =
 | 
			
		||||
    return (a == b) or
 | 
			
		||||
        ((a.`type` == b.`type`) and (a.size == b.size) and
 | 
			
		||||
        cmpMem(a + 1, b + 1, a.size) == 0)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Sequence Iterator
 | 
			
		||||
##
 | 
			
		||||
## Get an iterator pointing to the first event in a Sequence body.
 | 
			
		||||
##
 | 
			
		||||
template atomSequenceBegin*(body: ptr AtomSequenceBody): ptr AtomEvent =
 | 
			
		||||
    cast[ptr AtomEvent](body + 1)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Get an iterator pointing to the end of a Sequence body.
 | 
			
		||||
##
 | 
			
		||||
template atomSequenceEnd*(body: ptr AtomSequenceBody; size: Natural): ptr AtomEvent =
 | 
			
		||||
    cast[ptr AtomEvent](cast[ptr uint8](body) + atomPadSize(size.uint32))
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return true iff `i` has reached the end of `body`.
 | 
			
		||||
##
 | 
			
		||||
template atomSequenceIsEnd*(body: ptr AtomSequenceBody; size: Natural; i: ptr AtomEvent): bool =
 | 
			
		||||
    cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.uint)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return an iterator to the element following `i`.
 | 
			
		||||
##
 | 
			
		||||
template atomSequenceNext*(i: ptr AtomEvent): ptr AtomEvent =
 | 
			
		||||
    cast[ptr AtomEvent](cast[ptr uint8](i) + sizeof(AtomEvent) + atomPadSize(i.body.size))
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## An iterator for looping over all events in an AtomSequence.
 | 
			
		||||
## @param seq  Pointer to the sequence to iterate over
 | 
			
		||||
##
 | 
			
		||||
iterator items*(seq: ptr AtomSequence): ptr AtomEvent {.inline.} =
 | 
			
		||||
    var event = atomSequenceBegin(seq.body.addr)
 | 
			
		||||
    while not atomSequenceIsEnd(seq.body.addr, seq.atom.size, event):
 | 
			
		||||
        yield event
 | 
			
		||||
        event = atomSequenceNext(event)
 | 
			
		||||
 | 
			
		||||
## 
 | 
			
		||||
## An iterator for looping over all events in an AtomSequenceBody.
 | 
			
		||||
## @param body  Pointer to the sequence body to iterate over
 | 
			
		||||
## @param size  Size in bytes of the sequence body (including the 8 bytes of AtomSequenceBody)
 | 
			
		||||
## 
 | 
			
		||||
iterator items*(body: ptr AtomSequenceBody, size: Natural): ptr AtomEvent {.inline.} =
 | 
			
		||||
    var event = atomSequenceBegin(body)
 | 
			
		||||
    while not atomSequenceIsEnd(body, size, event):
 | 
			
		||||
        yield event
 | 
			
		||||
        event = atomSequenceNext(event)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Sequence Utilities
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Test if AtomSequence is empty, i.e the body has no events
 | 
			
		||||
##
 | 
			
		||||
template atomSequenceIsEmpty*(seq: ptr AtomSequence): bool =
 | 
			
		||||
    seq.atom.size == sizeof(AtomSequenceBody).uint32
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Clear all events from `sequence`.
 | 
			
		||||
##
 | 
			
		||||
## This simply resets the size field, the other fields are left untouched.
 | 
			
		||||
##
 | 
			
		||||
template atomSequenceClear*(seq: ptr AtomSequence) =
 | 
			
		||||
    seq.atom.size = sizeof(AtomSequenceBody).uint32
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Append an event at the end of `sequence`.
 | 
			
		||||
##
 | 
			
		||||
## @param seq Sequence to append to.
 | 
			
		||||
## @param capacity Total capacity of the sequence atom
 | 
			
		||||
## (as set by the host for sequence output ports).
 | 
			
		||||
## @param event Event to write.
 | 
			
		||||
##
 | 
			
		||||
## @return A pointer to the newly written event in `seq`,
 | 
			
		||||
## or nil on failure (insufficient space).
 | 
			
		||||
##
 | 
			
		||||
proc atomSequenceAppendEvent*(seq: ptr AtomSequence; capacity: uint32;
 | 
			
		||||
                              event: ptr AtomEvent): ptr AtomEvent {.inline.} =
 | 
			
		||||
    let totalSize = sizeof(AtomEvent).uint32 + event.body.size
 | 
			
		||||
    
 | 
			
		||||
    if capacity - seq.atom.size < totalSize:
 | 
			
		||||
        return nil
 | 
			
		||||
    
 | 
			
		||||
    var e = atomSequenceEnd(seq.body.addr, seq.atom.size)
 | 
			
		||||
    copyMem(e, event, totalSize)
 | 
			
		||||
    seq.atom.size += atomPadSize(totalSize)
 | 
			
		||||
    return e
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Tuple Iterator
 | 
			
		||||
##
 | 
			
		||||
## Get an iterator pointing to the first element in `tup`.
 | 
			
		||||
##
 | 
			
		||||
template atomTupleBegin*(tup: ptr AtomTuple): ptr Atom =
 | 
			
		||||
    cast[ptr Atom](atomContents(Atom, tup))
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return true iff `i` has reached the end of `body`.
 | 
			
		||||
##
 | 
			
		||||
template atomTupleIsEnd*(body: pointer; size: Natural; i: ptr Atom): bool =
 | 
			
		||||
    cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.uint)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return an iterator to the element following `i`.
 | 
			
		||||
##
 | 
			
		||||
template atomTupleNext*(i: ptr Atom): ptr Atom =
 | 
			
		||||
    cast[ptr Atom](cast[ptr uint8](i) + sizeof(Atom) + atomPadSize(i.size))
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## An iterator for looping over all elements of an AtomTuple.
 | 
			
		||||
## @param tuple Pointer to the tuple to iterate over
 | 
			
		||||
##
 | 
			
		||||
iterator items*(tup: ptr AtomTuple): ptr Atom {.inline.} =
 | 
			
		||||
    var atom = atomTupleBegin(tup)
 | 
			
		||||
    while not atomTupleIsEnd(atomBody(tup), tup.atom.size, atom):
 | 
			
		||||
        yield atom
 | 
			
		||||
        atom = atomTupleNext(atom)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## An iterator for looping over all elements of a headerless tuple.
 | 
			
		||||
## @param tuple Pointer to the first Atom of the tuple to iterate over
 | 
			
		||||
## @param size  Size in bytes of the tuple body
 | 
			
		||||
##
 | 
			
		||||
iterator items*(tup: ptr Atom, size: Natural): ptr Atom {.inline.} =
 | 
			
		||||
    var atom = tup
 | 
			
		||||
    while not atomTupleIsEnd(tup, size, atom):
 | 
			
		||||
        yield atom
 | 
			
		||||
        atom = atomTupleNext(atom)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Object Iterator
 | 
			
		||||
##
 | 
			
		||||
## Return a pointer to the first property in `body`.
 | 
			
		||||
##
 | 
			
		||||
template atomObjectBegin*(body: ptr AtomObjectBody): ptr AtomPropertyBody =
 | 
			
		||||
    cast[ptr AtomPropertyBody](body + 1)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return true iff `i` has reached the end of `obj`.
 | 
			
		||||
##
 | 
			
		||||
template atomObjectIsEnd*(body: ptr AtomObjectBody; size: Natural; i: ptr AtomPropertyBody): bool =
 | 
			
		||||
    cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.uint)
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return an iterator to the property following `i`.
 | 
			
		||||
##
 | 
			
		||||
template atomObjectNext*(i: ptr AtomPropertyBody): ptr AtomPropertyBody =
 | 
			
		||||
    let value = cast[ptr Atom](cast[ptr uint8](i) + 2 * sizeof(uint32))
 | 
			
		||||
    return cast[ptr AtomPropertyBody](cast[ptr uint8](i) + atomPadSize(sizeof(AtomPropertyBody).uint32 + value.size))
 | 
			
		||||
 | 
			
		||||
## TODO: 
 | 
			
		||||
##
 | 
			
		||||
## A iterator for looping over all properties of an Object.
 | 
			
		||||
## @param obj The object to iterate over
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
## TODO: 
 | 
			
		||||
##
 | 
			
		||||
## Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body.
 | 
			
		||||
##
 | 
			
		||||
@ -1,120 +0,0 @@
 | 
			
		||||
const
 | 
			
		||||
    lv2CoreBaseUri* = "http://lv2plug.in/ns/lv2core"
 | 
			
		||||
    lv2CorePrefix = lv2CoreBaseUri & "#"
 | 
			
		||||
 | 
			
		||||
    # Classes (http://lv2plug.in/ns/lv2core#ref-classes):
 | 
			
		||||
    lv2CoreClassAllpassPlugin* = lv2CorePrefix & "AllpassPlugin"
 | 
			
		||||
    lv2CoreClassAmplifierPlugin* = lv2CorePrefix & "AmplifierPlugin"
 | 
			
		||||
    lv2CoreClassAnalyserPlugin* = lv2CorePrefix & "AnalyserPlugin"
 | 
			
		||||
    lv2CoreClassAudioPort* = lv2CorePrefix & "AudioPort"
 | 
			
		||||
    lv2CoreClassBandpassPlugin* = lv2CorePrefix & "BandpassPlugin"
 | 
			
		||||
    lv2CoreClassCVPort* = lv2CorePrefix & "CVPort"
 | 
			
		||||
    lv2CoreClassChorusPlugin* = lv2CorePrefix & "ChorusPlugin"
 | 
			
		||||
    lv2CoreClassCombPlugin* = lv2CorePrefix & "CombPlugin"
 | 
			
		||||
    lv2CoreClassCompressorPlugin* = lv2CorePrefix & "CompressorPlugin"
 | 
			
		||||
    lv2CoreClassConstantPlugin* = lv2CorePrefix & "ConstantPlugin"
 | 
			
		||||
    lv2CoreClassControlPort* = lv2CorePrefix & "ControlPort"
 | 
			
		||||
    lv2CoreClassConverterPlugin* = lv2CorePrefix & "ConverterPlugin"
 | 
			
		||||
    lv2CoreClassDelayPlugin* = lv2CorePrefix & "DelayPlugin"
 | 
			
		||||
    lv2CoreClassDistortionPlugin* = lv2CorePrefix & "DistortionPlugin"
 | 
			
		||||
    lv2CoreClassDynamicsPlugin* = lv2CorePrefix & "DynamicsPlugin"
 | 
			
		||||
    lv2CoreClassEQPlugin* = lv2CorePrefix & "EQPlugin"
 | 
			
		||||
    lv2CoreClassEnvelopePlugin* = lv2CorePrefix & "EnvelopePlugin"
 | 
			
		||||
    lv2CoreClassExpanderPlugin* = lv2CorePrefix & "ExpanderPlugin"
 | 
			
		||||
    lv2CoreClassExtensionData* = lv2CorePrefix & "ExtensionData"
 | 
			
		||||
    lv2CoreClassFeature* = lv2CorePrefix & "Feature"
 | 
			
		||||
    lv2CoreClassFilterPlugin* = lv2CorePrefix & "FilterPlugin"
 | 
			
		||||
    lv2CoreClassFlangerPlugin* = lv2CorePrefix & "FlangerPlugin"
 | 
			
		||||
    lv2CoreClassFunctionPlugin* = lv2CorePrefix & "FunctionPlugin"
 | 
			
		||||
    lv2CoreClassGatePlugin* = lv2CorePrefix & "GatePlugin"
 | 
			
		||||
    lv2CoreClassGeneratorPlugin* = lv2CorePrefix & "GeneratorPlugin"
 | 
			
		||||
    lv2CoreClassHighpassPlugin* = lv2CorePrefix & "HighpassPlugin"
 | 
			
		||||
    lv2CoreClassInputPort* = lv2CorePrefix & "InputPort"
 | 
			
		||||
    lv2CoreClassInstrumentPlugin* = lv2CorePrefix & "InstrumentPlugin"
 | 
			
		||||
    lv2CoreClassLimiterPlugin* = lv2CorePrefix & "LimiterPlugin"
 | 
			
		||||
    lv2CoreClassLowpassPlugin* = lv2CorePrefix & "LowpassPlugin"
 | 
			
		||||
    lv2CoreClassMixerPlugin* = lv2CorePrefix & "MixerPlugin"
 | 
			
		||||
    lv2CoreClassModulatorPlugin* = lv2CorePrefix & "ModulatorPlugin"
 | 
			
		||||
    lv2CoreClassMultiEQPlugin* = lv2CorePrefix & "MultiEQPlugin"
 | 
			
		||||
    lv2CoreClassOscillatorPlugin* = lv2CorePrefix & "OscillatorPlugin"
 | 
			
		||||
    lv2CoreClassOutputPort* = lv2CorePrefix & "OutputPort"
 | 
			
		||||
    lv2CoreClassParaEQPlugin* = lv2CorePrefix & "ParaEQPlugin"
 | 
			
		||||
    lv2CoreClassPhaserPlugin* = lv2CorePrefix & "PhaserPlugin"
 | 
			
		||||
    lv2CoreClassPitchPlugin* = lv2CorePrefix & "PitchPlugin"
 | 
			
		||||
    lv2CoreClassPlugin* = lv2CorePrefix & "Plugin"
 | 
			
		||||
    lv2CoreClassPluginBase* = lv2CorePrefix & "PluginBase"
 | 
			
		||||
    lv2CoreClassPoint* = lv2CorePrefix & "Point"
 | 
			
		||||
    lv2CoreClassPort* = lv2CorePrefix & "Port"
 | 
			
		||||
    lv2CoreClassPortProperty* = lv2CorePrefix & "Port"
 | 
			
		||||
    lv2CoreClassResource* = lv2CorePrefix & "Resource"
 | 
			
		||||
    lv2CoreClassReverbPlugin* = lv2CorePrefix & "ReverbPlugin"
 | 
			
		||||
    lv2CoreClassScalePoint* = lv2CorePrefix & "ScalePoint"
 | 
			
		||||
    lv2CoreClassSimulatorPlugin* = lv2CorePrefix & "SimulatorPlugin"
 | 
			
		||||
    lv2CoreClassSpatialPlugin* = lv2CorePrefix & "SpatialPlugin"
 | 
			
		||||
    lv2CoreClassSpecification* = lv2CorePrefix & "Specification"
 | 
			
		||||
    lv2CoreClassSpectralPlugin* = lv2CorePrefix & "SpectralPlugin"
 | 
			
		||||
    lv2CoreClassUtilityPlugin* = lv2CorePrefix & "UtilityPlugin"
 | 
			
		||||
    lv2CoreClassWaveshaperPlugin* = lv2CorePrefix & "WaveshaperPlugin"
 | 
			
		||||
 | 
			
		||||
    # Properties (http://lv2plug.in/ns/lv2core#ref-properties):
 | 
			
		||||
    lv2CorePropertyAppliesTo* = lv2CorePrefix & "appliesTo"
 | 
			
		||||
    lv2CorePropertyBinary* = lv2CorePrefix & "binary"
 | 
			
		||||
    lv2CorePropertyDefault* = lv2CorePrefix & "default"
 | 
			
		||||
    lv2CorePropertyDesignation* = lv2CorePrefix & "designation"
 | 
			
		||||
    lv2CorePropertyDocumentation* = lv2CorePrefix & "documentation"
 | 
			
		||||
    lv2CorePropertyExtensionData* = lv2CorePrefix & "extensionData"
 | 
			
		||||
    lv2CorePropertyIndex* = lv2CorePrefix & "index"
 | 
			
		||||
    lv2CorePropertyLatency* = lv2CorePrefix & "latency"
 | 
			
		||||
    lv2CorePropertyMaximum* = lv2CorePrefix & "maximum"
 | 
			
		||||
    lv2CorePropertyMicroVersion* = lv2CorePrefix & "microVersion"
 | 
			
		||||
    lv2CorePropertyMinimum* = lv2CorePrefix & "minimum"
 | 
			
		||||
    lv2CorePropertyMinorVersion* = lv2CorePrefix & "minorVersion"
 | 
			
		||||
    lv2CorePropertyName* = lv2CorePrefix & "name"
 | 
			
		||||
    lv2CorePropertyOptionalFeature* = lv2CorePrefix & "optionalFeature"
 | 
			
		||||
    lv2CorePropertyPort* = lv2CorePrefix & "port"
 | 
			
		||||
    lv2CorePropertyPortProperty* = lv2CorePrefix & "portProperty"
 | 
			
		||||
    lv2CorePropertyProject* = lv2CorePrefix & "project"
 | 
			
		||||
    lv2CorePropertyPrototype* = lv2CorePrefix & "prototype"
 | 
			
		||||
    lv2CorePropertyReportsLatency* = lv2CorePrefix & "reportsLatency"
 | 
			
		||||
    lv2CorePropertyRequiredFeature* = lv2CorePrefix & "requiredFeature"
 | 
			
		||||
    lv2CorePropertyScalePoint* = lv2CorePrefix & "scalePoint"
 | 
			
		||||
    lv2CorePropertySymbol* = lv2CorePrefix & "symbol"
 | 
			
		||||
 | 
			
		||||
    # Instances (http://lv2plug.in/ns/lv2core#ref-instances)
 | 
			
		||||
    lv2CoreInstanceConnectionOptional* = lv2CorePrefix & "connectionOptional"
 | 
			
		||||
    lv2CoreInstanceControl* = lv2CorePrefix & "control"
 | 
			
		||||
    lv2CoreInstanceEnumeration* = lv2CorePrefix & "enumeration"
 | 
			
		||||
    lv2CoreInstanceFreeWheeling* = lv2CorePrefix & "freeWheeling"
 | 
			
		||||
    lv2CoreInstanceHardRTCapable* = lv2CorePrefix & "hardRTCapable"
 | 
			
		||||
    lv2CoreInstanceInPlaceBroken* = lv2CorePrefix & "inPlaceBroken"
 | 
			
		||||
    lv2CoreInstanceInteger* = lv2CorePrefix & "integer"
 | 
			
		||||
    lv2CoreInstanceIsLive* = lv2CorePrefix & "isLive"
 | 
			
		||||
    lv2CoreInstanceSampleRate* = lv2CorePrefix & "sampleRate"
 | 
			
		||||
    lv2CoreInstanceToggled* = lv2CorePrefix & "toggled"
 | 
			
		||||
 | 
			
		||||
type Lv2Handle* = pointer
 | 
			
		||||
 | 
			
		||||
type Lv2Feature* = object
 | 
			
		||||
    uri*: cstring
 | 
			
		||||
    data*: pointer
 | 
			
		||||
 | 
			
		||||
type Lv2Descriptor* = object
 | 
			
		||||
    uri*: cstring
 | 
			
		||||
 | 
			
		||||
    instantiate*: proc(descriptor: ptr Lv2Descriptor, sampleRate: cdouble, bundlePath: cstring,
 | 
			
		||||
                       features: ptr UncheckedArray[ptr Lv2Feature]): Lv2Handle {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    connectPort*: proc(instance: Lv2Handle, port: cuint, dataLocation: pointer) {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    activate*: proc(instance: Lv2Handle) {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    run*: proc(instance: Lv2Handle, sampleCount: cuint) {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    deactivate*: proc(instance: Lv2Handle) {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    cleanup*: proc(instance: Lv2Handle) {.cdecl.}
 | 
			
		||||
 | 
			
		||||
    extensionData*: proc(uri: cstring): pointer {.cdecl.}
 | 
			
		||||
 | 
			
		||||
type lv2Descriptor* = proc(index: cuint): ptr Lv2Descriptor {.cdecl.}
 | 
			
		||||
 | 
			
		||||
@ -1,189 +0,0 @@
 | 
			
		||||
## Copyright 2012-2016 David Robillard <d@drobilla.net>
 | 
			
		||||
## SPDX-License-Identifier: ISC
 | 
			
		||||
##
 | 
			
		||||
## Definitions of standard MIDI messages.
 | 
			
		||||
##
 | 
			
		||||
## See <http://lv2plug.in/ns/ext/midi> for details.
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
const
 | 
			
		||||
    lv2MidiBaseUri = "http://lv2plug.in/ns/ext/midi"
 | 
			
		||||
    lv2MidiPrefix = lv2MidiBaseUri & "#" 
 | 
			
		||||
 | 
			
		||||
    lv2MidiActiveSense* = lv2MidiPrefix & "ActiveSense" 
 | 
			
		||||
    lv2MidiAftertouch* = lv2MidiPrefix & "Aftertouch" 
 | 
			
		||||
    lv2MidiBender* = lv2MidiPrefix & "Bender" 
 | 
			
		||||
    lv2MidiChannelPressure* = lv2MidiPrefix & "ChannelPressure" 
 | 
			
		||||
    lv2MidiChunk* = lv2MidiPrefix & "Chunk" 
 | 
			
		||||
    lv2MidiClock* = lv2MidiPrefix & "Clock" 
 | 
			
		||||
    lv2MidiContinue* = lv2MidiPrefix & "Continue" 
 | 
			
		||||
    lv2MidiController* = lv2MidiPrefix & "Controller" 
 | 
			
		||||
    lv2MidiMidiEvent* = lv2MidiPrefix & "MidiEvent" 
 | 
			
		||||
    lv2MidiNoteOff* = lv2MidiPrefix & "NoteOff" 
 | 
			
		||||
    lv2MidiNoteOn* = lv2MidiPrefix & "NoteOn" 
 | 
			
		||||
    lv2MidiProgramChange* = lv2MidiPrefix & "ProgramChange" 
 | 
			
		||||
    lv2MidiQuarterFrame* = lv2MidiPrefix & "QuarterFrame" 
 | 
			
		||||
    lv2MidiReset* = lv2MidiPrefix & "Reset" 
 | 
			
		||||
    lv2MidiSongPosition* = lv2MidiPrefix & "SongPosition" 
 | 
			
		||||
    lv2MidiSongSelect* = lv2MidiPrefix & "SongSelect" 
 | 
			
		||||
    lv2MidiStart* = lv2MidiPrefix & "Start" 
 | 
			
		||||
    lv2MidiStop* = lv2MidiPrefix & "Stop" 
 | 
			
		||||
    lv2MidiSystemCommon* = lv2MidiPrefix & "SystemCommon" 
 | 
			
		||||
    lv2MidiSystemExclusive* = lv2MidiPrefix & "SystemExclusive" 
 | 
			
		||||
    lv2MidiSystemMessage* = lv2MidiPrefix & "SystemMessage" 
 | 
			
		||||
    lv2MidiSystemRealtime* = lv2MidiPrefix & "SystemRealtime" 
 | 
			
		||||
    lv2MidiTick* = lv2MidiPrefix & "Tick" 
 | 
			
		||||
    lv2MidiTuneRequest* = lv2MidiPrefix & "TuneRequest" 
 | 
			
		||||
    lv2MidiVoiceMessage* = lv2MidiPrefix & "VoiceMessage" 
 | 
			
		||||
    lv2MidiPropBenderValue* = lv2MidiPrefix & "benderValue" 
 | 
			
		||||
    lv2MidiPropBinding* = lv2MidiPrefix & "binding" 
 | 
			
		||||
    lv2MidiPropByteNumber* = lv2MidiPrefix & "byteNumber" 
 | 
			
		||||
    lv2MidiPropChannel* = lv2MidiPrefix & "channel" 
 | 
			
		||||
    lv2MidiPropChunk* = lv2MidiPrefix & "chunk" 
 | 
			
		||||
    lv2MidiPropControllerNumber* = lv2MidiPrefix & "controllerNumber" 
 | 
			
		||||
    lv2MidiPropControllerValue* = lv2MidiPrefix & "controllerValue" 
 | 
			
		||||
    lv2MidiPropNoteNumber* = lv2MidiPrefix & "noteNumber" 
 | 
			
		||||
    lv2MidiPropPressure* = lv2MidiPrefix & "pressure" 
 | 
			
		||||
    lv2MidiPropProgramNumber* = lv2MidiPrefix & "programNumber" 
 | 
			
		||||
    lv2MidiPropProperty* = lv2MidiPrefix & "property" 
 | 
			
		||||
    lv2MidiPropSongNumber* = lv2MidiPrefix & "songNumber" 
 | 
			
		||||
    lv2MidiPropSongPosition* = lv2MidiPrefix & "songPosition" 
 | 
			
		||||
    lv2MidiPropStatus* = lv2MidiPrefix & "status" 
 | 
			
		||||
    lv2MidiPropStatusMask* = lv2MidiPrefix & "statusMask" 
 | 
			
		||||
    lv2MidiPropVelocity* = lv2MidiPrefix & "velocity" 
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## MIDI Message Type
 | 
			
		||||
##
 | 
			
		||||
## This includes both voice messages (which have a channel) and system messages
 | 
			
		||||
## (which do not), as well as a sentinel value for invalid messages. To get
 | 
			
		||||
## the type of a message suitable for use in a switch statement, use
 | 
			
		||||
## midiGetMessageType() on the status byte.
 | 
			
		||||
##
 | 
			
		||||
type
 | 
			
		||||
    MidiMessageType* = enum
 | 
			
		||||
        midiMsgInvalid = 0,  # Invalid Message
 | 
			
		||||
        midiMsgNoteOff = 0x80,  # Note Off
 | 
			
		||||
        midiMsgNoteOn = 0x90,  # Note On
 | 
			
		||||
        midiMsgNotePressure = 0xA0,  # Note Pressure
 | 
			
		||||
        midiMsgController = 0xB0,  # Controller
 | 
			
		||||
        midiMsgPgmChange = 0xC0,  # Program Change
 | 
			
		||||
        midiMsgChannelPressure = 0xD0,  # Channel Pressure
 | 
			
		||||
        midiMsgBender = 0xE0,  # Pitch Bender
 | 
			
		||||
        midiMsgSystemExclusive = 0xF0,  # System Exclusive Begin
 | 
			
		||||
        midiMsgMtcQuarter = 0xF1,  # MTC Quarter Frame
 | 
			
		||||
        midiMsgSongPos = 0xF2,  # Song Position
 | 
			
		||||
        midiMsgSongSelect = 0xF3,  # Song Select
 | 
			
		||||
        midiMsgTuneRequest = 0xF6,  # Tune Request
 | 
			
		||||
        midiMsgClock = 0xF8,  # Clock
 | 
			
		||||
        midiMsgStart = 0xFA,  # Start
 | 
			
		||||
        midiMsgContinue = 0xFB,  # Continue
 | 
			
		||||
        midiMsgStop = 0xFC,  # Stop
 | 
			
		||||
        midiMsgActiveSense = 0xFE,  # Active Sensing
 | 
			
		||||
        midiMsgReset = 0xFF  # Reset
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Standard MIDI Controller Numbers
 | 
			
		||||
##
 | 
			
		||||
type
 | 
			
		||||
    MidiController* = enum
 | 
			
		||||
        midiCtlMsbBank = 0x00,  ##  Bank Selection
 | 
			
		||||
        midiCtlMsbModwheel = 0x01,  # Modulation
 | 
			
		||||
        midiCtlMsbBreath = 0x02,  # Breath
 | 
			
		||||
        midiCtlMsbFoot = 0x04,  # Foot
 | 
			
		||||
        midiCtlMsbPortamentoTime = 0x05,  # Portamento Time
 | 
			
		||||
        midiCtlMsbDataEntry = 0x06,  # Data Entry
 | 
			
		||||
        midiCtlMsbMainVolume = 0x07,  # Main Volume
 | 
			
		||||
        midiCtlMsbBalance = 0x08,  # Balance
 | 
			
		||||
        midiCtlMsbPan = 0x0A,  # Panpot
 | 
			
		||||
        midiCtlMsbExpression = 0x0B,  # Expression
 | 
			
		||||
        midiCtlMsbEffect1 = 0x0C,  # Effect1
 | 
			
		||||
        midiCtlMsbEffect2 = 0x0D,  # Effect2
 | 
			
		||||
        midiCtlMsbGeneralPurpose1 = 0x10,  # General Purpose 1
 | 
			
		||||
        midiCtlMsbGeneralPurpose2 = 0x11,  # General Purpose 2
 | 
			
		||||
        midiCtlMsbGeneralPurpose3 = 0x12,  # General Purpose 3
 | 
			
		||||
        midiCtlMsbGeneralPurpose4 = 0x13,  # General Purpose 4
 | 
			
		||||
        midiCtlLsbBank = 0x20,  # Bank Selection
 | 
			
		||||
        midiCtlLsbModwheel = 0x21,  # Modulation
 | 
			
		||||
        midiCtlLsbBreath = 0x22,  # Breath
 | 
			
		||||
        midiCtlLsbFoot = 0x24,  # Foot
 | 
			
		||||
        midiCtlLsbPortamentoTime = 0x25,  # Portamento Time
 | 
			
		||||
        midiCtlLsbDataEntry = 0x26,  # Data Entry
 | 
			
		||||
        midiCtlLsbMainVolume = 0x27,  # Main Volume
 | 
			
		||||
        midiCtlLsbBalance = 0x28,  # Balance
 | 
			
		||||
        midiCtlLsbPan = 0x2A,  # Panpot
 | 
			
		||||
        midiCtlLsbExpression = 0x2B,  # Expression
 | 
			
		||||
        midiCtlLsbEffect1 = 0x2C,  # Effect1
 | 
			
		||||
        midiCtlLsbEffect2 = 0x2D,  # Effect2
 | 
			
		||||
        midiCtlLsbGeneralPurpose1 = 0x30,  # General Purpose 1
 | 
			
		||||
        midiCtlLsbGeneralPurpose2 = 0x31,  # General Purpose 2
 | 
			
		||||
        midiCtlLsbGeneralPurpose3 = 0x32,  # General Purpose 3
 | 
			
		||||
        midiCtlLsbGeneralPurpose4 = 0x33,  # General Purpose 4
 | 
			
		||||
        midiCtlSustain = 0x40,  # Sustain Pedal
 | 
			
		||||
        midiCtlPortamento = 0x41,  # Portamento
 | 
			
		||||
        midiCtlSostenuto = 0x42,  # Sostenuto
 | 
			
		||||
        midiCtlSoftPedal = 0x43,  # Soft Pedal
 | 
			
		||||
        midiCtlLegatoFootswitch = 0x44,  # Legato Foot Switch
 | 
			
		||||
        midiCtlHold2 = 0x45,  # Hold2
 | 
			
		||||
        midiCtlSc1SoundVariation = 0x46,  # SC1 Sound Variation
 | 
			
		||||
        midiCtlSc2Timbre = 0x47,  # SC2 Timbre
 | 
			
		||||
        midiCtlSc3ReleaseTime = 0x48,  # SC3 Release Time
 | 
			
		||||
        midiCtlSc4AttackTime = 0x49,  # SC4 Attack Time
 | 
			
		||||
        midiCtlSc5Brightness = 0x4A,  # SC5 Brightness
 | 
			
		||||
        midiCtlSc6 = 0x4B,  # SC6
 | 
			
		||||
        midiCtlSc7 = 0x4C,  # SC7
 | 
			
		||||
        midiCtlSc8 = 0x4D,  # SC8
 | 
			
		||||
        midiCtlSc9 = 0x4E,  # SC9
 | 
			
		||||
        midiCtlSc10 = 0x4F,  # SC10
 | 
			
		||||
        midiCtlGeneralPurpose5 = 0x50,  # General Purpose 5
 | 
			
		||||
        midiCtlGeneralPurpose6 = 0x51,  # General Purpose 6
 | 
			
		||||
        midiCtlGeneralPurpose7 = 0x52,  # General Purpose 7
 | 
			
		||||
        midiCtlGeneralPurpose8 = 0x53,  # General Purpose 8
 | 
			
		||||
        midiCtlPortamentoControl = 0x54,  # Portamento Control
 | 
			
		||||
        midiCtlE1ReverbDepth = 0x5B,  # E1 Reverb Depth
 | 
			
		||||
        midiCtlE2TremoloDepth = 0x5C,  # E2 Tremolo Depth
 | 
			
		||||
        midiCtlE3ChorusDepth = 0x5D,  # E3 Chorus Depth
 | 
			
		||||
        midiCtlE4DetuneDepth = 0x5E,  # E4 Detune Depth
 | 
			
		||||
        midiCtlE5PhaserDepth = 0x5F,  # E5 Phaser Depth
 | 
			
		||||
        midiCtlDataIncrement = 0x60,  # Data Increment
 | 
			
		||||
        midiCtlDataDecrement = 0x61,  # Data Decrement
 | 
			
		||||
        midiCtlNrpnLsb = 0x62,  # Non-registered Parameter Number
 | 
			
		||||
        midiCtlNrpnMsb = 0x63,  # Non-registered Parameter Number
 | 
			
		||||
        midiCtlRpnLsb = 0x64,  # Registered Parameter Number
 | 
			
		||||
        midiCtlRpnMsb = 0x65,  # Registered Parameter Number
 | 
			
		||||
        midiCtlAllSoundsOff = 0x78,  # All Sounds Off
 | 
			
		||||
        midiCtlResetControllers = 0x79,  # Reset Controllers
 | 
			
		||||
        midiCtlLocalControlSwitch = 0x7A,  # Local Control Switch
 | 
			
		||||
        midiCtlAllNotesOff = 0x7B,  # All Notes Off
 | 
			
		||||
        midiCtlOmniOff = 0x7C,  # Omni Off
 | 
			
		||||
        midiCtlOmniOn = 0x7D,  # Omni On
 | 
			
		||||
        midiCtlMono1 = 0x7E,  # Mono1
 | 
			
		||||
        midiCtlMono2 = 0x7F  # Mono2
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return true iff `msg` is a MIDI voice message (which has a channel).
 | 
			
		||||
##
 | 
			
		||||
proc midiIsVoiceMessage*(msg: UncheckedArray[uint8]): bool {.inline.} =
 | 
			
		||||
    return msg[0] >= 0x80 and msg[0] < 0xF0
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return true iff `msg` is a MIDI system message (which has no channel).
 | 
			
		||||
##
 | 
			
		||||
proc midiIsSystemMessage*(msg: UncheckedArray[uint8]): bool {.inline.} =
 | 
			
		||||
    case msg[0]
 | 
			
		||||
    of 0xF4, 0xF5, 0xF7, 0xF9, 0xFD:
 | 
			
		||||
        return false
 | 
			
		||||
    else:
 | 
			
		||||
        return (msg[0] and 0xF0) == 0xF0
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
## Return the type of a MIDI message.
 | 
			
		||||
## 
 | 
			
		||||
## @param msg Pointer to the start (status byte) of a MIDI message.
 | 
			
		||||
##
 | 
			
		||||
proc midiGetMessageType*(msg: UncheckedArray[uint8]): MidiMessageType {.inline.} =
 | 
			
		||||
    if midiIsVoiceMessage(msg):
 | 
			
		||||
        return cast[MidiMessageType](msg[0] and 0xF0)
 | 
			
		||||
    if midiIsSystemMessage(msg):
 | 
			
		||||
        return cast[MidiMessageType](msg[0])
 | 
			
		||||
    return midiMsgInvalid
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
plugin:
 | 
			
		||||
    urid: urn:foobar#v1
 | 
			
		||||
    port_group:
 | 
			
		||||
        name: "stereo"
 | 
			
		||||
        audio_input:
 | 
			
		||||
            "in_l"
 | 
			
		||||
        audio_input:
 | 
			
		||||
            "in_r"
 | 
			
		||||
@ -1,6 +0,0 @@
 | 
			
		||||
##
 | 
			
		||||
## Increment pointer `p` by `offset` that jumps memory in increments of
 | 
			
		||||
## the size of `T`.
 | 
			
		||||
##
 | 
			
		||||
proc `+`*[T](p: ptr T, offset: SomeInteger): ptr T =
 | 
			
		||||
    return cast[ptr T](cast[int](p) +% (offset.int * sizeof(T)))
 | 
			
		||||
@ -1,95 +0,0 @@
 | 
			
		||||
## Copyright 2008-2016 David Robillard <d@drobilla.net>
 | 
			
		||||
## Copyright 2011 Gabriel M. Beddingfield <gabrbedd@gmail.com>
 | 
			
		||||
## SPDX-License-Identifier: ISC
 | 
			
		||||
##
 | 
			
		||||
## Features for mapping URIs to and from integers.
 | 
			
		||||
##
 | 
			
		||||
## See <http://lv2plug.in/ns/ext/urid> for details.
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
const
 | 
			
		||||
    lv2UridBaseUri* = "http://lv2plug.in/ns/ext/urid"
 | 
			
		||||
    lv2UridPrefix* = lv2UridBaseUri & "#"
 | 
			
		||||
 | 
			
		||||
    lv2UridMap* = lv2UridPrefix & "map"
 | 
			
		||||
    lv2UridUnmap* = lv2UridPrefix & "unmap"
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
    ##
 | 
			
		||||
    ## URI mapped to an integer.
 | 
			
		||||
    ##
 | 
			
		||||
    Urid* = distinct uint32
 | 
			
		||||
 | 
			
		||||
    ##
 | 
			
		||||
    ## Opaque pointer to host data for UridMap.
 | 
			
		||||
    ##
 | 
			
		||||
    UridMapHandle* = pointer
 | 
			
		||||
 | 
			
		||||
    ##
 | 
			
		||||
    ## Opaque pointer to host data for uridUnmap.
 | 
			
		||||
    ##
 | 
			
		||||
    UridUnmapHandle* = pointer
 | 
			
		||||
 | 
			
		||||
    ##
 | 
			
		||||
    ## URID Map Feature (lv2UridMap)
 | 
			
		||||
    ##
 | 
			
		||||
    UridMap* {.bycopy.} = object
 | 
			
		||||
        ##
 | 
			
		||||
        ## Opaque pointer to host data.
 | 
			
		||||
        ##
 | 
			
		||||
        ## This MUST be passed to map_uri() whenever it is called.
 | 
			
		||||
        ## Otherwise, it must not be interpreted in any way.
 | 
			
		||||
        ##
 | 
			
		||||
        handle*: UridMapHandle
 | 
			
		||||
        ##
 | 
			
		||||
        ## Get the numeric ID of a URI.
 | 
			
		||||
        ##
 | 
			
		||||
        ## If the ID does not already exist, it will be created.
 | 
			
		||||
        ##
 | 
			
		||||
        ## This function is referentially transparent; any number of calls with the
 | 
			
		||||
        ## same arguments is guaranteed to return the same value over the life of a
 | 
			
		||||
        ## plugin instance.  Note, however, that several URIs MAY resolve to the
 | 
			
		||||
        ## same ID if the host considers those URIs equivalent.
 | 
			
		||||
        ##
 | 
			
		||||
        ## This function is not necessarily very fast or RT-safe: plugins SHOULD
 | 
			
		||||
        ## cache any IDs they might need in performance critical situations.
 | 
			
		||||
        ##
 | 
			
		||||
        ## The return value 0 is reserved and indicates that an ID for that URI
 | 
			
		||||
        ## could not be created for whatever reason.  However, hosts SHOULD NOT
 | 
			
		||||
        ## return 0 from this function in non-exceptional circumstances (i.e. the
 | 
			
		||||
        ## URI map SHOULD be dynamic).
 | 
			
		||||
        ##
 | 
			
		||||
        ## @param handle Must be the callback_data member of this struct.
 | 
			
		||||
        ## @param uri The URI to be mapped to an integer ID.
 | 
			
		||||
        ##
 | 
			
		||||
        map*: proc (handle: UridMapHandle; uri: cstring): Urid
 | 
			
		||||
 | 
			
		||||
    ##
 | 
			
		||||
    ## URI Unmap Feature (lv2UridUnmap)
 | 
			
		||||
    ##
 | 
			
		||||
    UridUnmap* {.bycopy.} = object
 | 
			
		||||
        ##
 | 
			
		||||
        ## Opaque pointer to host data.
 | 
			
		||||
        ##
 | 
			
		||||
        ## This MUST be passed to unmap() whenever it is called.
 | 
			
		||||
        ## Otherwise, it must not be interpreted in any way.
 | 
			
		||||
        ##
 | 
			
		||||
        handle*: UridUnmapHandle
 | 
			
		||||
        ##
 | 
			
		||||
        ## Get the URI for a previously mapped numeric ID.
 | 
			
		||||
        ##
 | 
			
		||||
        ## Returns NULL if `urid` is not yet mapped.  Otherwise, the corresponding
 | 
			
		||||
        ## URI is returned in a canonical form.  This MAY not be the exact same
 | 
			
		||||
        ## string that was originally passed to LV2_URID_Map::map(), but it MUST be
 | 
			
		||||
        ## an identical URI according to the URI syntax specification (RFC3986).  A
 | 
			
		||||
        ## non-NULL return for a given `urid` will always be the same for the life
 | 
			
		||||
        ## of the plugin.  Plugins that intend to perform string comparison on
 | 
			
		||||
        ## unmapped URIs SHOULD first canonicalise URI strings with a call to
 | 
			
		||||
        ## map_uri() followed by a call to unmap_uri().
 | 
			
		||||
        ##
 | 
			
		||||
        ## @param handle Must be the callback_data member of this struct.
 | 
			
		||||
        ## @param urid The ID to be mapped back to the URI string.
 | 
			
		||||
        ##
 | 
			
		||||
        unmap*: proc (handle: UridUnmapHandle; urid: Urid): cstring
 | 
			
		||||
 | 
			
		||||
proc `==`* (x: Urid, y: Urid): bool {.borrow.}
 | 
			
		||||
@ -1,42 +0,0 @@
 | 
			
		||||
import core
 | 
			
		||||
 | 
			
		||||
## Return the data for a feature in a features array.
 | 
			
		||||
 | 
			
		||||
## If the feature is not found, nil is returned.  Note that this function is
 | 
			
		||||
## only useful for features with data, and can not detect features that are
 | 
			
		||||
## present but have nil data.
 | 
			
		||||
 | 
			
		||||
proc lv2FeaturesData*(features: ptr UncheckedArray[ptr Lv2Feature], uri: string): pointer =
 | 
			
		||||
    if not features.isNil:
 | 
			
		||||
        var i = 0
 | 
			
		||||
        while true:
 | 
			
		||||
            let feature = features[i]
 | 
			
		||||
            if feature.isNil:
 | 
			
		||||
                break
 | 
			
		||||
            
 | 
			
		||||
            if feature[].uri == uri.cstring:
 | 
			
		||||
                return feature[].data
 | 
			
		||||
            
 | 
			
		||||
            inc i
 | 
			
		||||
    
 | 
			
		||||
    return nil
 | 
			
		||||
 | 
			
		||||
## Query a features array.
 | 
			
		||||
## 
 | 
			
		||||
## This function allows getting several features in one call, and detect
 | 
			
		||||
## missing required features, with the same caveat of lv2_features_data().
 | 
			
		||||
## 
 | 
			
		||||
## The arguments should be a series of const char* uri, void** data, bool
 | 
			
		||||
## required, terminated by a NULL URI.  The data pointers MUST be initialized
 | 
			
		||||
## to NULL.  For example:
 | 
			
		||||
## 
 | 
			
		||||
## LV2_URID_Log* log = NULL;
 | 
			
		||||
## LV2_URID_Map* map = NULL;
 | 
			
		||||
## const char* missing = lv2_features_query(
 | 
			
		||||
##      features,
 | 
			
		||||
##      LV2_LOG__log,  &log, false,
 | 
			
		||||
##      LV2_URID__map, &map, true,
 | 
			
		||||
##      NULL);
 | 
			
		||||
##
 | 
			
		||||
## 
 | 
			
		||||
## @return NULL on success, otherwise the URI of this missing feature.
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user