Compare commits
2 Commits
024803f1d4
...
c27c9988b7
Author | SHA1 | Date |
---|---|---|
Christopher Arndt | c27c9988b7 | |
Christopher Arndt | a151e96233 |
|
@ -0,0 +1,4 @@
|
||||||
|
nimble.paths
|
||||||
|
*.so
|
||||||
|
*.dll
|
||||||
|
/.lv2/
|
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2024 Christopher Arndt <chris@chrisarndt.de>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
|
@ -0,0 +1,8 @@
|
||||||
|
# nymph
|
||||||
|
|
||||||
|
A [Nim] library for writing audio and MIDI plugins conforming to the [LV2] standard
|
||||||
|
|
||||||
|
|
||||||
|
[LV2]: https://lv2plug.in/
|
||||||
|
[Nim]: https://nim-lang.org/
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
@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 rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||||
|
@prefix unit: <http://lv2plug.in/ns/extensions/units#> .
|
||||||
|
|
||||||
|
<urn:nymph:examples:amp>
|
||||||
|
a lv2:Plugin, lv2:AmplifierPlugin , doap:Project ;
|
||||||
|
|
||||||
|
lv2:optionalFeature lv2:hardRTCapable , bufs:boundedBlockLength , opts:options ;
|
||||||
|
|
||||||
|
lv2:requiredFeature <http://lv2plug.in/ns/ext/urid#map> ;
|
||||||
|
|
||||||
|
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 "Gain" ;
|
||||||
|
lv2:symbol "gain" ;
|
||||||
|
lv2:default 0.0 ;
|
||||||
|
lv2:minimum -90.0 ;
|
||||||
|
lv2:maximum 20.0 ;
|
||||||
|
unit:unit unit:db ;
|
||||||
|
];
|
||||||
|
|
||||||
|
rdfs:comment """
|
||||||
|
A simple amplifier / attenuator.
|
||||||
|
""" ;
|
||||||
|
|
||||||
|
doap:name "nymph example amp" ;
|
||||||
|
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 .
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
|
||||||
|
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||||
|
|
||||||
|
<urn:nymph:examples:amp>
|
||||||
|
a lv2:Plugin ;
|
||||||
|
lv2:binary <libamp.so> ;
|
||||||
|
rdfs:seeAlso <amp.ttl> .
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
## A simple amplifier LV2 plugin
|
||||||
|
|
||||||
|
import std/math
|
||||||
|
import nymph
|
||||||
|
|
||||||
|
const PluginUri = "urn:nymph:examples:amp"
|
||||||
|
|
||||||
|
type
|
||||||
|
SampleBuffer = UncheckedArray[cfloat]
|
||||||
|
|
||||||
|
PluginPort {.pure.} = enum
|
||||||
|
Input, Output, Gain
|
||||||
|
|
||||||
|
AmpPlugin = object
|
||||||
|
input: ptr SampleBuffer
|
||||||
|
output: ptr SampleBuffer
|
||||||
|
gain: ptr cfloat
|
||||||
|
|
||||||
|
|
||||||
|
template db2coeff(db: cfloat): cfloat =
|
||||||
|
pow(10.0, db / 20.0)
|
||||||
|
|
||||||
|
|
||||||
|
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
|
||||||
|
bundlePath: cstring; features: ptr ptr Lv2Feature):
|
||||||
|
Lv2Handle {.cdecl.} =
|
||||||
|
return createShared(AmpPlugin)
|
||||||
|
|
||||||
|
|
||||||
|
proc connectPort(instance: Lv2Handle; port: cuint;
|
||||||
|
dataLocation: pointer) {.cdecl.} =
|
||||||
|
let amp = cast[ptr AmpPlugin](instance)
|
||||||
|
case cast[PluginPort](port)
|
||||||
|
of PluginPort.Input:
|
||||||
|
amp.input = cast[ptr SampleBuffer](dataLocation)
|
||||||
|
of PluginPort.Output:
|
||||||
|
amp.output = cast[ptr SampleBuffer](dataLocation)
|
||||||
|
of PluginPort.Gain:
|
||||||
|
amp.gain = cast[ptr cfloat](dataLocation)
|
||||||
|
|
||||||
|
|
||||||
|
proc activate(instance: Lv2Handle) {.cdecl.} =
|
||||||
|
discard
|
||||||
|
|
||||||
|
|
||||||
|
proc run(instance: Lv2Handle; nSamples: cuint) {.cdecl.} =
|
||||||
|
let amp = cast[ptr AmpPlugin](instance)
|
||||||
|
for pos in 0 ..< nSamples:
|
||||||
|
amp.output[pos] = amp.input[pos] * db2coeff(amp.gain[])
|
||||||
|
|
||||||
|
|
||||||
|
proc deactivate(instance: Lv2Handle) {.cdecl.} =
|
||||||
|
discard
|
||||||
|
|
||||||
|
|
||||||
|
proc cleanup(instance: Lv2Handle) {.cdecl.} =
|
||||||
|
freeShared(cast[ptr AmpPlugin](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
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
switch("path", "$projectDir/../src")
|
119
nymph.nimble
119
nymph.nimble
|
@ -1,4 +1,6 @@
|
||||||
# Package
|
import std/strformat
|
||||||
|
|
||||||
|
# Package definition
|
||||||
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
author = "Christopher Arndt"
|
author = "Christopher Arndt"
|
||||||
|
@ -6,7 +8,120 @@ description = "A Nim library for writing audio and MIDI plugins conforming to
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
srcDir = "src"
|
srcDir = "src"
|
||||||
|
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
requires "nim >= 2.0"
|
requires "nim >= 2.0"
|
||||||
|
|
||||||
|
# Custom tasks
|
||||||
|
|
||||||
|
const examples = to_table({
|
||||||
|
"amp": "urn:nymph:examples:amp"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
proc parseArgs(): tuple[options: seq[string], args: seq[string]] =
|
||||||
|
## Parse task specific command line arguments into option switches and positional arguments
|
||||||
|
for arg in commandLineParams:
|
||||||
|
if arg[0] == '-': # -d:foo or --define:foo
|
||||||
|
result.options.add(arg)
|
||||||
|
else:
|
||||||
|
result.args.add(arg)
|
||||||
|
|
||||||
|
|
||||||
|
proc showArgs() =
|
||||||
|
## Show task environment (for debugging when writing nimble tasks)
|
||||||
|
echo "Command: ", getCommand()
|
||||||
|
echo "ProjectName: ", projectName()
|
||||||
|
echo "ProjectDir: ", projectDir()
|
||||||
|
echo "ProjectPath: ", projectPath()
|
||||||
|
echo "Task args: ", commandLineParams
|
||||||
|
|
||||||
|
for i in 0..paramCount():
|
||||||
|
echo &"Arg {i}: ", paramStr(i)
|
||||||
|
|
||||||
|
|
||||||
|
task build_ex, "Build given example plugin":
|
||||||
|
#showArgs()
|
||||||
|
let (_, args) = parseArgs()
|
||||||
|
|
||||||
|
if args.len == 0:
|
||||||
|
echo "Usage: nimble build_ex <example name>"
|
||||||
|
return
|
||||||
|
|
||||||
|
let example = args[^1]
|
||||||
|
let source = thisDir() & "/examples/" & example & ".nim"
|
||||||
|
let bundle = thisDir() & "/examples/" & example & ".lv2"
|
||||||
|
let dll = bundle & "/" & toDll(example)
|
||||||
|
|
||||||
|
if fileExists(source):
|
||||||
|
switch("app", "lib")
|
||||||
|
switch("noMain", "on")
|
||||||
|
switch("mm", "arc")
|
||||||
|
switch("opt", "speed")
|
||||||
|
switch("define", "release")
|
||||||
|
switch("out", dll)
|
||||||
|
setCommand("compile", source)
|
||||||
|
else:
|
||||||
|
echo &"Example '{example}' not found."
|
||||||
|
|
||||||
|
|
||||||
|
task lv2lint, "Run lv2lint check on given example plugin":
|
||||||
|
let (_, args) = parseArgs()
|
||||||
|
|
||||||
|
if args.len == 0:
|
||||||
|
echo "Usage: nimble lv2lint <example name>"
|
||||||
|
return
|
||||||
|
|
||||||
|
let example = args[^1]
|
||||||
|
let uri = examples.getOrDefault(example)
|
||||||
|
|
||||||
|
if uri == "":
|
||||||
|
echo &"Plugin URI for example '{example}' not set."
|
||||||
|
return
|
||||||
|
|
||||||
|
let examplesDir = thisDir() & "/examples"
|
||||||
|
let bundle = examplesDir & "/" & example & ".lv2"
|
||||||
|
let dll = bundle & "/" & toDll(example)
|
||||||
|
|
||||||
|
if fileExists(dll):
|
||||||
|
exec(&"lv2lint -s NimMain -I {bundle} \"{uri}\"")
|
||||||
|
else:
|
||||||
|
echo &"Example '{example}' shared library not found. Use task 'build_ex' to build it."
|
||||||
|
|
||||||
|
|
||||||
|
task lv2bm, "Run lv2bm benchmark on given example plugin":
|
||||||
|
let (_, args) = parseArgs()
|
||||||
|
|
||||||
|
if args.len == 0:
|
||||||
|
echo "Usage: nimble lv2bm <example name>"
|
||||||
|
return
|
||||||
|
|
||||||
|
let example = args[^1]
|
||||||
|
let uri = examples.getOrDefault(example)
|
||||||
|
|
||||||
|
if uri == "":
|
||||||
|
echo &"Plugin URI for example '{example}' not set."
|
||||||
|
return
|
||||||
|
|
||||||
|
let examplesDir = thisDir() & "/examples"
|
||||||
|
let bundle = examplesDir & "/" & example & ".lv2"
|
||||||
|
let dll = bundle & "/" & toDll(example)
|
||||||
|
|
||||||
|
if fileExists(dll):
|
||||||
|
let lv2_path = getEnv("LV2_PATH")
|
||||||
|
let tempLv2Dir = thisDir() & "/.lv2"
|
||||||
|
let bundleLink = tempLv2Dir & "/" & example & ".lv2"
|
||||||
|
|
||||||
|
mkDir(tempLv2Dir)
|
||||||
|
rmFile(bundleLink)
|
||||||
|
exec(&"ln -s \"{bundle}\" \"{bundleLink}\"")
|
||||||
|
|
||||||
|
if lv2_path == "":
|
||||||
|
putEnv("LV2_PATH", tempLv2Dir)
|
||||||
|
else:
|
||||||
|
putEnv("LV2_PATH", tempLv2Dir & ":" & lv2_path)
|
||||||
|
|
||||||
|
exec(&"lv2bm --full-test -i white \"{uri}\"")
|
||||||
|
else:
|
||||||
|
echo &"Example '{example}' shared library not found. Use task 'build_ex' to build it."
|
||||||
|
|
||||||
|
|
|
@ -1,46 +1,32 @@
|
||||||
const LV2_CORE_URI* = "http://lv2plug.in/ns/lv2core"
|
const LV2_CORE_URI* = "http://lv2plug.in/ns/lv2core"
|
||||||
|
|
||||||
|
|
||||||
type Lv2Handle* = pointer
|
type Lv2Handle* = pointer
|
||||||
|
|
||||||
|
|
||||||
type Lv2Feature* = object
|
type Lv2Feature* = object
|
||||||
uri*: cstring
|
uri*: cstring
|
||||||
data*: pointer
|
data*: pointer
|
||||||
|
|
||||||
|
|
||||||
type Lv2Descriptor* = object
|
type Lv2Descriptor* = object
|
||||||
uri*: cstring
|
uri*: cstring
|
||||||
|
|
||||||
instantiate* = proc(
|
instantiate*: proc(descriptor: ptr Lv2Descriptor, sampleRate: cdouble, bundlePath: cstring,
|
||||||
descriptor: ptr Lv2Descriptor,
|
features: ptr ptr Lv2Feature): Lv2Handle {.cdecl.}
|
||||||
sampleRate: cdouble,
|
|
||||||
bundlePath: cstring,
|
|
||||||
features: ptr ptr Lv2Feature
|
|
||||||
): Lv2Handle {.cdecl.}
|
|
||||||
|
|
||||||
connectPort*: proc(
|
connectPort*: proc(instance: Lv2Handle, port: cuint, dataLocation: pointer) {.cdecl.}
|
||||||
instance: Lv2Handle,
|
|
||||||
port: cuint,
|
|
||||||
dataLocation: pointer
|
|
||||||
) {.cdecl.}
|
|
||||||
|
|
||||||
activate*: proc(
|
activate*: proc(instance: Lv2Handle) {.cdecl.}
|
||||||
instance: Lv2Handle
|
|
||||||
) {.cdecl.}
|
|
||||||
|
|
||||||
run*: proc(
|
run*: proc(instance: Lv2Handle, sampleCount: cuint) {.cdecl.}
|
||||||
instance: Lv2Handle,
|
|
||||||
sampleCount: cuint
|
|
||||||
) {.cdecl.}
|
|
||||||
|
|
||||||
deactivate*: proc(
|
deactivate*: proc(instance: Lv2Handle) {.cdecl.}
|
||||||
instance: Lv2Handle
|
|
||||||
) {.cdecl.}
|
|
||||||
|
|
||||||
cleanup*: proc(
|
cleanup*: proc(instance: Lv2Handle) {.cdecl.}
|
||||||
instance: Lv2Handle
|
|
||||||
) {.cdecl.}
|
extensionData*: proc(uri: cstring): pointer {.cdecl.}
|
||||||
|
|
||||||
extensionData*: proc(
|
|
||||||
uri: cstring
|
|
||||||
): pointer {.cdecl.}
|
|
||||||
|
|
||||||
type lv2Descriptor* = proc(index: cuint): ptr Lv2Descriptor {.cdecl.}
|
type lv2Descriptor* = proc(index: cuint): ptr Lv2Descriptor {.cdecl.}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
switch("path", "$projectDir/../src")
|
switch("path", "$projectDir/../src")
|
||||||
|
switch("warning[BareExcept]", "off")
|
||||||
|
switch("warning[UnusedImport]", "off")
|
||||||
|
|
Loading…
Reference in New Issue