Add nimble tasks and example 'amp' plugin

Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
This commit is contained in:
Christopher Arndt 2024-04-20 02:50:10 +02:00
parent 95100a0f03
commit a151e96233
8 changed files with 286 additions and 30 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
nimble.paths
*.so
*.dll
/.lv2/

59
examples/amp.lv2/amp.ttl Normal file
View File

@ -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 .

View File

@ -0,0 +1,8 @@
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<urn:nymph:examples:amp>
a lv2:Plugin ;
lv2:binary <libamp.so> ;
rdfs:seeAlso <amp.ttl> .

81
examples/amp.nim Normal file
View File

@ -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

1
examples/config.nims Normal file
View File

@ -0,0 +1 @@
switch("path", "$projectDir/../src")

View File

@ -1,4 +1,6 @@
# Package
import std/strformat
# Package definition
version = "0.1.0"
author = "Christopher Arndt"
@ -6,7 +8,120 @@ description = "A Nim library for writing audio and MIDI plugins conforming to
license = "MIT"
srcDir = "src"
# Dependencies
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."

View File

@ -1,46 +1,32 @@
const LV2_CORE_URI* = "http://lv2plug.in/ns/lv2core"
type Lv2Handle* = pointer
type Lv2Feature* = object
uri*: cstring
data*: pointer
type Lv2Descriptor* = object
uri*: cstring
instantiate* = proc(
descriptor: ptr Lv2Descriptor,
sampleRate: cdouble,
bundlePath: cstring,
features: ptr ptr Lv2Feature
): Lv2Handle {.cdecl.}
instantiate*: proc(descriptor: ptr Lv2Descriptor, sampleRate: cdouble, bundlePath: cstring,
features: ptr ptr Lv2Feature): Lv2Handle {.cdecl.}
connectPort*: proc(
instance: Lv2Handle,
port: cuint,
dataLocation: pointer
) {.cdecl.}
connectPort*: proc(instance: Lv2Handle, port: cuint, dataLocation: pointer) {.cdecl.}
activate*: proc(
instance: Lv2Handle
) {.cdecl.}
activate*: proc(instance: Lv2Handle) {.cdecl.}
run*: proc(
instance: Lv2Handle,
sampleCount: cuint
) {.cdecl.}
run*: proc(instance: Lv2Handle, sampleCount: cuint) {.cdecl.}
deactivate*: proc(
instance: Lv2Handle
) {.cdecl.}
deactivate*: proc(instance: Lv2Handle) {.cdecl.}
cleanup*: proc(
instance: Lv2Handle
) {.cdecl.}
cleanup*: proc(instance: Lv2Handle) {.cdecl.}
extensionData*: proc(uri: cstring): pointer {.cdecl.}
extensionData*: proc(
uri: cstring
): pointer {.cdecl.}
type lv2Descriptor* = proc(index: cuint): ptr Lv2Descriptor {.cdecl.}

View File

@ -1 +1,3 @@
switch("path", "$projectDir/../src")
switch("path", "$projectDir/../src")
switch("warning[BareExcept]", "off")
switch("warning[UnusedImport]", "off")