Compare commits
1 Commits
master
...
feat/dynsm
Author | SHA1 | Date |
---|---|---|
Christopher Arndt | b377e9e8be |
|
@ -2,4 +2,3 @@ nimble.paths
|
||||||
*.so
|
*.so
|
||||||
*.dll
|
*.dll
|
||||||
/.lv2/
|
/.lv2/
|
||||||
*.code-workspace
|
|
||||||
|
|
69
README.md
69
README.md
|
@ -1,7 +1,6 @@
|
||||||
# nymph
|
# nymph
|
||||||
|
|
||||||
A [Nim] library for writing audio and MIDI plugins conforming to the [LV2]
|
A [Nim] library for writing audio and MIDI plugins conforming to the [LV2] standard
|
||||||
standard
|
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
@ -23,68 +22,53 @@ Install the `amp.lv2` example plugin:
|
||||||
cp -a examples/amp.lv2 ~/.lv2
|
cp -a examples/amp.lv2 ~/.lv2
|
||||||
|
|
||||||
Other example plugins can be found in the [examples](./examples) directory and
|
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
|
can be built and tested with similar commands, just changing the example name
|
||||||
the basename of the plugin's LV2 bundle dir.
|
to the basename of the plugin's LV2 bundle dir.
|
||||||
|
|
||||||
Currently, there are just a few other example plugins:
|
Currently, there is only one other example plugin, `multimode_filter`, but
|
||||||
|
more will be added soon.
|
||||||
* [`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).
|
|
||||||
|
|
||||||
|
|
||||||
## How To
|
## How To
|
||||||
|
|
||||||
**Note:** I'll use `mydsp` as the base name for the new plugin in the
|
* Install this library:
|
||||||
examples below. Substitute your own plugin basename wherever you see it used
|
|
||||||
below.
|
|
||||||
|
|
||||||
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`
|
* Edit `myplugin.lv2/manifest.ttl`:
|
||||||
into it. Also copy `examples/amp.lv2/amp.ttl` to `mydsp.lv2/mydsp.ttl`.
|
|
||||||
|
|
||||||
1. Copy `examples/amp_plugin.nim` into your project as `mydsp_plugin.nim`.
|
|
||||||
|
|
||||||
1. Edit `mydsp.lv2/manifest.ttl`:
|
|
||||||
* Change the plugin URI.
|
* Change the plugin URI.
|
||||||
* Change the plugin's shared library name defined via `lv2:binary` to
|
* Change the plugin's shared library name defined via `lv2:binary` to
|
||||||
`libmydsp.so`.
|
`libmyplugin.so`.
|
||||||
* Change file name referenced via `rdfs:seeAlso` to `mydsp.ttl`.
|
* Change file name referenced via `rdfs:seeAlso` to `myplugin.ttl`.
|
||||||
|
|
||||||
1. Edit `mydsp.lv2/mydsp.ttl`:
|
* Edit `myplugin.lv2/myplugin.ttl`:
|
||||||
* Change the plugin URI.
|
* Change the plugin URI.
|
||||||
* Define audio, control and atom ports as needed.
|
* Define audio, control and atom ports as needed.
|
||||||
|
|
||||||
1. Edit `mydsp_plugin.nim`:
|
* Edit `myplugin.nim`:
|
||||||
* Change the `PluginUri` constant at the top.
|
* Change the `PluginUri` constant at the top.
|
||||||
* Change the `PluginPort` enum and list the ports in the order defined in
|
* Change the `PluginPort` enum and list the ports in the order defined in
|
||||||
`mydsp.ttl`.
|
`myplugin.ttl`.
|
||||||
* Rename and update the definition of the `AmpPlugin` object type and
|
* Rename and update the definition of the `AmpPlugin` object type and
|
||||||
define its members with the appropriate data types for the plugin ports
|
define its members with the appropriate data type for the plugin port
|
||||||
they will be connected to. Update the type name in the `instantiate`,
|
they will be connected to. Update the type name in the `instantiate`,
|
||||||
`deactivate`, `connectPorts` and `run` procs.
|
`deactivate`, `connectPorts` and `run` procs.
|
||||||
* Update and extend the `case` statement in `connectPort` to connect ports
|
* Update and extend the `case` statement in `connectPort` to connect ports
|
||||||
to your plugin object instance members.
|
to your plugin object instance members.
|
||||||
* Implement your DSP code in the `run` proc.
|
* 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 \
|
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
|
See the definition of the `build_ex` task in the
|
||||||
[nymph.nimble](./nymph.nimble#L67) file on how to create a nimble task
|
[nymph.nimble](./nymph.nimble#L43) file on how to create a nimble task
|
||||||
to simplify compilation and additional compiler args you might want to use.
|
to simplify compilation.
|
||||||
|
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
@ -99,17 +83,8 @@ Optional:
|
||||||
* [lv2lint] - For checking conformity of plugin bundles
|
* [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/
|
[LV2]: https://lv2plug.in/
|
||||||
[lv2bm]: https://github.com/moddevices/lv2bm
|
[lv2bm]: https://github.com/moddevices/lv2bm
|
||||||
[lv2lint]: https://git.open-music-kontrollers.ch/~hp/lv2lint
|
[lv2lint]: https://git.open-music-kontrollers.ch/~hp/lv2lint
|
||||||
[Nim]: https://nim-lang.org/
|
[Nim]: https://nim-lang.org/
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
@prefix opts: <http://lv2plug.in/ns/ext/options#> .
|
@prefix opts: <http://lv2plug.in/ns/ext/options#> .
|
||||||
@prefix params: <http://lv2plug.in/ns/ext/parameters#> .
|
@prefix params: <http://lv2plug.in/ns/ext/parameters#> .
|
||||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
@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>
|
<urn:nymph:examples:amp>
|
||||||
a lv2:Plugin, lv2:AmplifierPlugin , doap:Project ;
|
a lv2:Plugin, lv2:AmplifierPlugin , doap:Project ;
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
lv2:default 0.0 ;
|
lv2:default 0.0 ;
|
||||||
lv2:minimum -90.0 ;
|
lv2:minimum -90.0 ;
|
||||||
lv2:maximum 20.0 ;
|
lv2:maximum 20.0 ;
|
||||||
units:unit units:db ;
|
unit:unit unit:db ;
|
||||||
];
|
];
|
||||||
|
|
||||||
rdfs:comment """
|
rdfs:comment """
|
||||||
|
|
|
@ -22,12 +22,9 @@ template db2coeff(db: cfloat): cfloat =
|
||||||
|
|
||||||
|
|
||||||
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
|
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
|
||||||
bundlePath: cstring; features: ptr UncheckedArray[ptr Lv2Feature]):
|
bundlePath: cstring; features: ptr ptr Lv2Feature):
|
||||||
Lv2Handle {.cdecl.} =
|
Lv2Handle {.cdecl.} =
|
||||||
try:
|
|
||||||
return createShared(AmpPlugin)
|
return createShared(AmpPlugin)
|
||||||
except OutOfMemDefect:
|
|
||||||
return nil
|
|
||||||
|
|
||||||
|
|
||||||
proc connectPort(instance: Lv2Handle; port: cuint;
|
proc connectPort(instance: Lv2Handle; port: cuint;
|
||||||
|
@ -81,3 +78,4 @@ proc lv2Descriptor(index: cuint): ptr Lv2Descriptor {.
|
||||||
result.deactivate = deactivate
|
result.deactivate = deactivate
|
||||||
result.cleanup = cleanup
|
result.cleanup = cleanup
|
||||||
result.extensionData = extensionData
|
result.extensionData = extensionData
|
||||||
|
|
|
@ -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,396 +0,0 @@
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// This file was generated using the Faust compiler (https://faust.grame.fr),
|
|
||||||
// and the Faust post-processor (https://github.com/SpotlightKid/faustpp).
|
|
||||||
//
|
|
||||||
// Source: faustlpf.dsp
|
|
||||||
// Name: FaustLPF
|
|
||||||
// Author: Christopher Arndt
|
|
||||||
// Copyright: Christopher Arndt, 2024
|
|
||||||
// License: MIT
|
|
||||||
// Version: 0.1.0
|
|
||||||
// FAUST version: 2.75.10
|
|
||||||
// FAUST compilation options: -a /home/chris/tmp/tmpebxwoqng.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/tmpebxwoqng.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,216 +0,0 @@
|
||||||
#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) {
|
|
||||||
|
|
||||||
case 0:
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
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,36 +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 ***************************/
|
|
|
@ -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> .
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
@prefix props: <http://lv2plug.in/ns/ext/port-props#> .
|
@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 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>
|
<urn:nymph:examples:multimode-filter>
|
||||||
a lv2:Plugin , lv2:FilterPlugin , doap:Project ;
|
a lv2:Plugin, lv2:AmplifierPlugin , doap:Project ;
|
||||||
|
|
||||||
lv2:optionalFeature lv2:hardRTCapable , bufs:boundedBlockLength , opts:options ;
|
lv2:optionalFeature lv2:hardRTCapable , bufs:boundedBlockLength , opts:options ;
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
lv2:minimum 16.0 ;
|
lv2:minimum 16.0 ;
|
||||||
lv2:maximum 7000.0 ;
|
lv2:maximum 7000.0 ;
|
||||||
lv2:portProperty props:logarithmic;
|
lv2:portProperty props:logarithmic;
|
||||||
units:unit units:hz ;
|
unit:unit unit:hz ;
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
a lv2:InputPort, lv2:ControlPort ;
|
a lv2:InputPort, lv2:ControlPort ;
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
import nymph
|
import nymph
|
||||||
|
|
||||||
import paramsmooth
|
import dynparamsmooth
|
||||||
import svf
|
import svf
|
||||||
|
|
||||||
const PluginUri = "urn:nymph:examples:multimodefilter"
|
const PluginUri = "urn:nymph:examples:multimode-filter"
|
||||||
|
|
||||||
type
|
type
|
||||||
SampleBuffer = UncheckedArray[cfloat]
|
SampleBuffer = UncheckedArray[cfloat]
|
||||||
|
@ -19,20 +19,17 @@ type
|
||||||
cutoff: ptr cfloat
|
cutoff: ptr cfloat
|
||||||
q: ptr cfloat
|
q: ptr cfloat
|
||||||
mode: ptr cfloat
|
mode: ptr cfloat
|
||||||
svf: SVFilter
|
svf: FilterSV
|
||||||
smoothCutoff: ParamSmooth
|
smoothCutoff: DynParamSmooth
|
||||||
|
|
||||||
|
|
||||||
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
|
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
|
||||||
bundlePath: cstring; features: ptr UncheckedArray[ptr Lv2Feature]):
|
bundlePath: cstring; features: ptr ptr Lv2Feature):
|
||||||
Lv2Handle {.cdecl.} =
|
Lv2Handle {.cdecl.} =
|
||||||
try:
|
|
||||||
let plug = createShared(SVFPlugin)
|
let plug = createShared(SVFPlugin)
|
||||||
plug.svf = initSVFilter(fmLowPass, sampleRate)
|
plug.svf = initFilterSV(fmLowPass, sampleRate)
|
||||||
plug.smoothCutoff = initParamSmooth(20.0, sampleRate)
|
plug.smoothCutoff = initDynParamSmooth(sampleRate=sampleRate)
|
||||||
return cast[Lv2Handle](plug)
|
return plug
|
||||||
except OutOfMemDefect:
|
|
||||||
return nil
|
|
||||||
|
|
||||||
|
|
||||||
proc connectPort(instance: Lv2Handle; port: cuint;
|
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
|
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)
|
self.z = (sample * self.b) + (self.z * self.a)
|
||||||
return self.z
|
return self.z
|
||||||
|
|
||||||
|
|
|
@ -9,32 +9,32 @@ type
|
||||||
FilterMode* = enum
|
FilterMode* = enum
|
||||||
fmLowPass, fmHighPass, fmBandPass, fmBandReject
|
fmLowPass, fmHighPass, fmBandPass, fmBandReject
|
||||||
|
|
||||||
SVFilter* = object
|
FilterSV* = object
|
||||||
mode: FilterMode
|
mode: FilterMode
|
||||||
cutoff, q, lowPass, hiPass, bandPass, bandReject, a, b, maxCutoff: float
|
cutoff, q, lowPass, hiPass, bandPass, bandReject, a, b, maxCutoff: float
|
||||||
sampleRate: float64
|
sampleRate: float64
|
||||||
needsUpdate: bool
|
needs_update: bool
|
||||||
|
|
||||||
|
|
||||||
proc reset*(self: var SVFilter) =
|
proc reset*(self: var FilterSV) =
|
||||||
self.lowPass = 0.0
|
self.lowPass = 0.0
|
||||||
self.hiPass = 0.0
|
self.hiPass = 0.0
|
||||||
self.bandPass = 0.0
|
self.bandPass = 0.0
|
||||||
self.bandReject = 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.mode = mode
|
||||||
result.sampleRate = sampleRate
|
result.sampleRate = sampleRate
|
||||||
result.reset()
|
result.reset()
|
||||||
result.a = 0.0
|
result.a = 0.0
|
||||||
result.b = 0.0
|
result.b = 0.0
|
||||||
result.maxCutoff = sampleRate / 6.0
|
result.maxCutoff = sampleRate / 6.0
|
||||||
result.needsUpdate = true
|
result.needs_update = true
|
||||||
|
|
||||||
|
|
||||||
proc calcCoef*(self: var SVFilter) =
|
proc calcCoef*(self: var FilterSV) =
|
||||||
if self.needsUpdate:
|
if self.needs_update:
|
||||||
self.a = 2.0 * sin(PI * self.cutoff / self.sampleRate)
|
self.a = 2.0 * sin(PI * self.cutoff / self.sampleRate)
|
||||||
|
|
||||||
if self.q > 0.0:
|
if self.q > 0.0:
|
||||||
|
@ -42,36 +42,36 @@ proc calcCoef*(self: var SVFilter) =
|
||||||
else:
|
else:
|
||||||
self.b = 0.0
|
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)
|
let fc = min(self.maxCutoff, cutoff)
|
||||||
|
|
||||||
if fc != self.cutoff:
|
if fc != self.cutoff:
|
||||||
self.cutoff = fc
|
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:
|
if q != self.q:
|
||||||
self.q = 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
|
self.mode = mode
|
||||||
|
|
||||||
|
|
||||||
proc setSampleRate*(self: var SVFilter, sampleRate: float) =
|
proc setSampleRate*(self: var FilterSV, sampleRate: float) =
|
||||||
if sampleRate != self.sampleRate:
|
if sampleRate != self.sampleRate:
|
||||||
self.sampleRate = sampleRate
|
self.sampleRate = sampleRate
|
||||||
self.needsUpdate = true
|
self.needs_update = true
|
||||||
self.reset()
|
self.reset()
|
||||||
self.calcCoef()
|
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.lowPass += self.a * self.bandPass
|
||||||
self.hiPass = sample - (self.lowPass + (self.b * self.bandPass))
|
self.hiPass = sample - (self.lowPass + (self.b * self.bandPass))
|
||||||
self.bandPass += self.a * self.hiPass
|
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({
|
const examples = to_table({
|
||||||
"amp": "urn:nymph:examples:amp",
|
"amp": "urn:nymph:examples:amp",
|
||||||
"faustlpf": "urn:nymph:examples:faustlpf",
|
"multimode_filter": "urn:nymph:examples:multimode-filter",
|
||||||
"miditranspose": "urn:nymph:examples:miditranspose",
|
|
||||||
"multimodefilter": "urn:nymph:examples:multimodefilter",
|
|
||||||
"tiltfilter": "urn:nymph:examples:tiltfilter",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,7 +47,7 @@ proc getExample(task_name: string): Example =
|
||||||
result.name = changeFileExt(args[^1], "")
|
result.name = changeFileExt(args[^1], "")
|
||||||
|
|
||||||
let examplesDir = thisDir() / "examples"
|
let examplesDir = thisDir() / "examples"
|
||||||
result.source = examplesDir / result.name & "_plugin.nim"
|
result.source = examplesDir / changeFileExt(result.name, "nim")
|
||||||
|
|
||||||
if not fileExists(result.source):
|
if not fileExists(result.source):
|
||||||
quit(&"Example '{result.name}' not found.")
|
quit(&"Example '{result.name}' not found.")
|
||||||
|
@ -72,9 +69,6 @@ task build_ex, "Build given example plugin":
|
||||||
switch("mm", "arc")
|
switch("mm", "arc")
|
||||||
switch("out", ex.dll)
|
switch("out", ex.dll)
|
||||||
|
|
||||||
when defined(gcc):
|
|
||||||
switch("passC", "-fvisibility=hidden")
|
|
||||||
|
|
||||||
when not defined(release) and not defined(debug):
|
when not defined(release) and not defined(debug):
|
||||||
echo &"Compiling plugin {ex.name} in release mode."
|
echo &"Compiling plugin {ex.name} in release mode."
|
||||||
switch("define", "release")
|
switch("define", "release")
|
||||||
|
@ -89,7 +83,7 @@ task lv2lint, "Run lv2lint check on given example plugin":
|
||||||
let ex = getExample("lv2lint")
|
let ex = getExample("lv2lint")
|
||||||
|
|
||||||
if fileExists(ex.dll):
|
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:
|
else:
|
||||||
echo &"Example '{ex.name}' shared library not found. Use task 'build_ex' to build it."
|
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}\"")
|
exec(&"lv2bm --full-test -i white \"{ex.uri}\"")
|
||||||
else:
|
else:
|
||||||
echo &"Example '{ex.name}' shared library not found. Use task 'build_ex' to build it."
|
echo &"Example '{ex.name}' shared library not found. Use task 'build_ex' to build it."
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,32 @@
|
||||||
import nymph/core
|
const LV2_CORE_URI* = "http://lv2plug.in/ns/lv2core"
|
||||||
export core
|
|
||||||
|
|
||||||
|
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…
Reference in New Issue