feat: wrap LV2 log feature and extend amp example plugin to use it

Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
This commit is contained in:
Christopher Arndt 2026-01-07 05:32:02 +01:00
parent 5987482643
commit d851df1669
3 changed files with 104 additions and 11 deletions

View File

@ -6,12 +6,15 @@
@prefix params: <http://lv2plug.in/ns/ext/parameters#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
<urn:nymph:examples:amp>
a lv2:Plugin , lv2:AmplifierPlugin , doap:Project ;
lv2:optionalFeature lv2:hardRTCapable , bufs:boundedBlockLength , opts:options ;
lv2:requiredFeature urid:map ;
opts:supportedOption bufs:nominalBlockLength ,
bufs:maxBlockLength ,
params:sampleRate ;

View File

@ -1,7 +1,7 @@
## A simple amplifier LV2 plugin
import std/math
import nymph
import std/[math, strformat]
import nymph/[core, log, urid, util]
const PluginUri = "urn:nymph:examples:amp"
@ -14,6 +14,8 @@ type
AmpPlugin = object
input: ptr SampleBuffer
output: ptr SampleBuffer
map: ptr UridMap
log: Logger
gain: ptr cfloat
@ -25,38 +27,53 @@ proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
bundlePath: cstring; features: ptr UncheckedArray[ptr Lv2Feature]):
Lv2Handle {.cdecl.} =
try:
return createShared(AmpPlugin)
let plug: ptr AmpPlugin = createShared(AmpPlugin)
plug.map = cast[ptr UridMap](lv2FeaturesData(features, lv2UridMap))
if plug.map.isNil:
freeShared(plug)
return nil
let logPtr = cast[ptr Log](lv2FeaturesData(features, lv2LogLog))
plug.log.init(logPtr, plug.map)
plug.log.note("nymph amp plugin instance created.")
return cast[Lv2Handle](plug)
except OutOfMemDefect:
return nil
proc connectPort(instance: Lv2Handle; port: cuint;
dataLocation: pointer) {.cdecl.} =
let amp = cast[ptr AmpPlugin](instance)
let plug = cast[ptr AmpPlugin](instance)
case cast[PluginPort](port)
of PluginPort.Input:
amp.input = cast[ptr SampleBuffer](dataLocation)
plug.input = cast[ptr SampleBuffer](dataLocation)
of PluginPort.Output:
amp.output = cast[ptr SampleBuffer](dataLocation)
plug.output = cast[ptr SampleBuffer](dataLocation)
of PluginPort.Gain:
amp.gain = cast[ptr cfloat](dataLocation)
plug.gain = cast[ptr cfloat](dataLocation)
proc activate(instance: Lv2Handle) {.cdecl.} =
discard
let plug = cast[ptr AmpPlugin](instance)
plug.log.note("nymph amp plugin activated.")
proc run(instance: Lv2Handle; nSamples: cuint) {.cdecl.} =
let amp = cast[ptr AmpPlugin](instance)
let plug = cast[ptr AmpPlugin](instance)
for pos in 0 ..< nSamples:
amp.output[pos] = amp.input[pos] * db2coeff(amp.gain[])
plug.output[pos] = plug.input[pos] * db2coeff(plug.gain[])
proc deactivate(instance: Lv2Handle) {.cdecl.} =
discard
let plug = cast[ptr AmpPlugin](instance)
plug.log.note("nymph amp plugin deactivated.")
proc cleanup(instance: Lv2Handle) {.cdecl.} =
let plug = cast[ptr AmpPlugin](instance)
plug.log.note("De-allocating nymph amp plugin instance.")
freeShared(cast[ptr AmpPlugin](instance))
@ -80,6 +97,9 @@ let descriptor = Lv2Descriptor(
proc lv2Descriptor(index: cuint): ptr Lv2Descriptor {.
cdecl, exportc, dynlib, extern: "lv2_descriptor".} =
echo fmt"nymph am plugin descriptor #{index} requested"
if index == 0:
NimMain()
return addr(descriptor)

70
src/nymph/log.nim Normal file
View File

@ -0,0 +1,70 @@
## Copyright 2012-2016 David Robillard <d@drobilla.net>
## Copyright 2012-2016 Christopher Arndt <info@chrisarndt.de>
## SPDX-License-Identifier: ISC
##
## Interface for plugins to log via the host.
##
## See <http://lv2plug.in/ns/ext/log> for details.
##
import urid
const
lv2LogBaseUri* = "http://lv2plug.in/ns/ext/log"
lv2LogPrefix = lv2LogBaseUri & "#"
lv2LogEntry* = lv2LogPrefix & "Entry"
lv2LogError* = lv2LogPrefix & "Error"
lv2LogNote* = lv2LogPrefix & "Note"
lv2LogTrace* = lv2LogPrefix & "Trace"
lv2LogWarning* = lv2LogPrefix & "Warning"
lv2LogLog* = lv2LogPrefix & "log"
type
LogHandle* = distinct pointer
Log* = object
handle: LogHandle
printf*: proc(handle: LogHandle, `type`: Urid, fmt: cstring) {.cdecl, varargs.}
Logger* = object
pLog: ptr Log
Error, Note, Trace, Warning: Urid
proc init*(logger: var Logger, log: ptr Log, map: ptr UridMap) =
logger.pLog = log
if not map.isNil:
logger.Error = map.map(map.handle, lv2LogError)
logger.Note = map.map(map.handle, lv2LogNote)
logger.Trace = map.map(map.handle, lv2LogTrace)
logger.Warning = map.map(map.handle, lv2LogWarning)
else:
logger.Error = Urid(0)
logger.Note = Urid(0)
logger.Trace = Urid(0)
logger.Warning = Urid(0)
proc log*(logger: Logger, `type`: Urid, msg: string) =
if logger.pLog.isNil:
echo(msg)
else:
logger.pLog.printf(logger.pLog.handle, `type`, msg.cstring)
proc error*(logger: Logger, msg: string) =
log(logger, logger.Error, msg)
proc note*(logger: Logger, msg: string) =
log(logger, logger.Note, msg)
proc trace*(logger: Logger, msg: string) =
log(logger, logger.Trace, msg)
proc warning*(logger: Logger, msg: string) =
log(logger, logger.Warning, msg)