Implement MIDI procesing (WIP)
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
This commit is contained in:
parent
ff6ddf77d2
commit
ba6aaa22ab
|
@ -0,0 +1,8 @@
|
||||||
|
@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> .
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
@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 .
|
|
@ -0,0 +1,98 @@
|
||||||
|
## A simple MIDI event processor LV2 plugin
|
||||||
|
|
||||||
|
import std/[math, strformat, 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
|
||||||
|
|
||||||
|
|
||||||
|
proc printFeatures(features: ptr UncheckedArray[ptr Lv2Feature]) =
|
||||||
|
if features != nil:
|
||||||
|
var i = 0
|
||||||
|
while true:
|
||||||
|
let feature = features[i]
|
||||||
|
if feature == nil:
|
||||||
|
break
|
||||||
|
echo &"URI: {feature[].uri}"
|
||||||
|
echo &"Data: {cast[int](feature[].data)}"
|
||||||
|
inc i
|
||||||
|
|
||||||
|
|
||||||
|
proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
|
||||||
|
bundlePath: cstring; features: ptr UncheckedArray[ptr Lv2Feature]):
|
||||||
|
Lv2Handle {.cdecl.} =
|
||||||
|
printFeatures(features)
|
||||||
|
let amp: ptr MidiTransposePlugin = createShared(MidiTransposePlugin)
|
||||||
|
amp.map = cast[ptr UridMap](lv2FeaturesData(features, lv2UridMap))
|
||||||
|
assert amp.map != nil
|
||||||
|
amp.midi_urid = amp.map[].map(amp.map[].handle, lv2MidiMidiEvent)
|
||||||
|
echo &"{lv2MidiMidiEvent} = {amp.midi_urid.int}"
|
||||||
|
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)
|
||||||
|
if amp.input.atom.size > 8:
|
||||||
|
for ev in items(amp.input):
|
||||||
|
if Urid(ev.body.`type`) == amp.midi_urid:
|
||||||
|
# TODO
|
||||||
|
var msg = cast[ptr UncheckedArray[uint8]](ev.body.addr + 1)
|
||||||
|
echo &"0x{toHex(msg[0], 2)} 0x{toHex(msg[1], 2)} 0x{toHex(msg[2], 2)}"
|
||||||
|
|
||||||
|
|
||||||
|
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
|
|
@ -26,6 +26,7 @@ type Example = tuple
|
||||||
const examples = to_table({
|
const examples = to_table({
|
||||||
"amp": "urn:nymph:examples:amp",
|
"amp": "urn:nymph:examples:amp",
|
||||||
"multimode_filter": "urn:nymph:examples:multimode-filter",
|
"multimode_filter": "urn:nymph:examples:multimode-filter",
|
||||||
|
"miditranspose": "urn:nymph:examples:miditranspose",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,7 +84,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 -I \"{ex.bundle}\" \"{ex.uri}\"")
|
exec(&"lv2lint -s NimMain -s NimDestroyGlobals -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."
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
## 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.
|
||||||
|
##
|
||||||
|
|
||||||
|
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`)
|
||||||
|
|
||||||
|
#[
|
||||||
|
{.push header: "lv2/atom/atom.h".}
|
||||||
|
|
||||||
|
var atomReferenceType* {.importc: "LV2_ATOM_REFERENCE_TYPE".}: int
|
||||||
|
|
||||||
|
##
|
||||||
|
## Const version of LV2_ATOM_CONTENTS.
|
||||||
|
##
|
||||||
|
proc atomContentsConst*(`type`: untyped; atom: untyped) {.importc: "LV2_ATOM_CONTENTS_CONST".}
|
||||||
|
|
||||||
|
##
|
||||||
|
## Return a pointer to the body of an Atom. The "body" of an atom is the
|
||||||
|
## data just past the LV2_Atom head (i.e. the same offset for all types).
|
||||||
|
##
|
||||||
|
proc atomBody*(atom: untyped) {.importc: "LV2_ATOM_BODY".}
|
||||||
|
|
||||||
|
##
|
||||||
|
## Const version of LV2_ATOM_BODY.
|
||||||
|
##
|
||||||
|
proc atomBodyConst*(atom: untyped) {.importc: "LV2_ATOM_BODY_CONST".}
|
||||||
|
|
||||||
|
{.pop.}
|
||||||
|
]#
|
||||||
|
|
||||||
|
type
|
||||||
|
## The header of an atom:Atom.
|
||||||
|
Atom* {.bycopy.} = object
|
||||||
|
size*: uint32 ## Size in bytes, not including type and size.
|
||||||
|
`type`*: uint32 ## of this atom (mapped URI).
|
||||||
|
|
||||||
|
## An atom:Int or atom:Bool. May be cast to LV2_Atom.
|
||||||
|
AtomInt* {.bycopy.} = object
|
||||||
|
atom*: Atom ## Atom header.
|
||||||
|
body*: int32 ## Integer value.
|
||||||
|
|
||||||
|
## An atom:Long. May be cast to LV2_Atom.
|
||||||
|
AtomLong* {.bycopy.} = object
|
||||||
|
atom*: Atom ## Atom header.
|
||||||
|
body*: int64 ## Integer value.
|
||||||
|
|
||||||
|
## An atom:Float. May be cast to LV2_Atom.
|
||||||
|
AtomFloat* {.bycopy.} = object
|
||||||
|
atom*: Atom ## Atom header.
|
||||||
|
body*: cfloat ## Floating point value.
|
||||||
|
|
||||||
|
## An atom:Double. May be cast to LV2_Atom.
|
||||||
|
AtomDouble* {.bycopy.} = object
|
||||||
|
atom*: Atom ## Atom header.
|
||||||
|
body*: cdouble ## Floating point value.
|
||||||
|
|
||||||
|
## An atom:Bool. May be cast to LV2_Atom.
|
||||||
|
AtomBool* = distinct AtomInt
|
||||||
|
|
||||||
|
## An atom:URID. May be cast to LV2_Atom.
|
||||||
|
AtomUrid* {.bycopy.} = object
|
||||||
|
atom*: Atom ## Atom header.
|
||||||
|
body*: uint32 ## URID.
|
||||||
|
|
||||||
|
## An atom:String. May be cast to LV2_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*: uint32 ## DataURID.
|
||||||
|
lang*: uint32 ## Language URID.
|
||||||
|
## Contents (a null-terminated UTF-8 string) follow here.
|
||||||
|
|
||||||
|
## An atom:Literal. May be cast to LV2_Atom.
|
||||||
|
AtomLiteral* {.bycopy.} = object
|
||||||
|
atom*: Atom ## Atom header.
|
||||||
|
body*: AtomLiteralBody ## Body.
|
||||||
|
|
||||||
|
## An atom:Tuple. May be cast to LV2_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 LV2_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*: uint32 ## Key (predicate) (mapped URI).
|
||||||
|
context*: uint32 ## 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 LV2_Atom.
|
||||||
|
AtomProperty* {.bycopy.} = object
|
||||||
|
atom*: Atom ## Atom header.
|
||||||
|
body*: AtomPropertyBody ## Body.
|
||||||
|
|
||||||
|
## The body of an atom:Object. May be cast to LV2_Atom.
|
||||||
|
AtomObjectBody* {.bycopy.} = object
|
||||||
|
id*: uint32 ## URID, or 0 for blank.
|
||||||
|
otype*: uint32 ## URID (same as rdf:type, for fast dispatch).
|
||||||
|
## Contents (a series of property bodies) follow here.
|
||||||
|
|
||||||
|
## An atom:Object. May be cast to LV2_Atom.
|
||||||
|
AtomObject* {.bycopy.} = object
|
||||||
|
atom*: Atom ## Atom header.
|
||||||
|
body*: AtomObjectBody ## Body.
|
||||||
|
|
||||||
|
## The header of an atom:Event. Note this is NOT an LV2_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 atom:Sequence (a sequence of events).
|
||||||
|
##
|
||||||
|
## The unit field is either a URID that described an appropriate time stamp
|
||||||
|
## type, or may be 0 where a default stamp is known. For
|
||||||
|
## LV2_Descriptor::run(), the default stamp is audio frames.
|
||||||
|
##
|
||||||
|
## The contents of a sequence is a series of LV2_Atom_Event, each aligned
|
||||||
|
## to 64-bits, for example:
|
||||||
|
## <pre>
|
||||||
|
## | Event 1 (size 6) | Event 2
|
||||||
|
## | | | | | | | | |
|
||||||
|
## | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||||
|
## |FRAMES |SIZE |TYPE |DATADATADATAPAD|FRAMES |...
|
||||||
|
## </pre>
|
||||||
|
##
|
||||||
|
AtomSequenceBody* {.bycopy.} = object
|
||||||
|
unit*: uint32 ## 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.
|
|
@ -0,0 +1,182 @@
|
||||||
|
## Copyright 2008-2015 David Robillard <d@drobilla.net>
|
||||||
|
## SPDX-License-Identifier: ISC
|
||||||
|
##
|
||||||
|
## Helper functions for the LV2 Atom extension.
|
||||||
|
##
|
||||||
|
## Note these functions are all static inline, do not take their address.
|
||||||
|
##
|
||||||
|
## This header is non-normative, it is provided for convenience.
|
||||||
|
##
|
||||||
|
## Utilities for working with atoms.
|
||||||
|
##
|
||||||
|
|
||||||
|
from system/ansi_c import c_memcmp, c_memcpy
|
||||||
|
import ../atom
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
## 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)))
|
||||||
|
|
||||||
|
##
|
||||||
|
## 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 == nil or (atom.`type` == 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
|
||||||
|
c_memcmp(a + 1, b + 1, a.size) == 0)
|
||||||
|
|
||||||
|
##
|
||||||
|
## Sequence Iterator
|
||||||
|
##
|
||||||
|
## Get an iterator pointing to the first event in a Sequence body.
|
||||||
|
##
|
||||||
|
proc atomSequenceBegin*(body: ptr AtomSequenceBody): ptr AtomEvent {.inline.} =
|
||||||
|
return cast[ptr AtomEvent](body + 1)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
## Get an iterator pointing to the end of a Sequence body.
|
||||||
|
##
|
||||||
|
proc atomSequenceEnd*(body: ptr AtomSequenceBody; size: uint32): ptr AtomEvent {.inline.} =
|
||||||
|
return cast[ptr AtomEvent](cast[ptr uint8](body) + atomPadSize(size))
|
||||||
|
|
||||||
|
##
|
||||||
|
## Return true iff `i` has reached the end of `body`.
|
||||||
|
##
|
||||||
|
proc atomSequenceIsEnd*(body: ptr AtomSequenceBody; size: Natural; i: ptr AtomEvent): bool {.inline.} =
|
||||||
|
return cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.uint)
|
||||||
|
|
||||||
|
##
|
||||||
|
## Return an iterator to the element following `i`.
|
||||||
|
##
|
||||||
|
proc atomSequenceNext*(i: ptr AtomEvent): ptr AtomEvent {.inline.} =
|
||||||
|
return cast[ptr AtomEvent](i + sizeof(AtomEvent) + atomPadSize(i.body.size))
|
||||||
|
|
||||||
|
##
|
||||||
|
## An iterator for looping over all events in a Sequence.
|
||||||
|
## @param seq 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)
|
||||||
|
|
||||||
|
## TODO: Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body.
|
||||||
|
|
||||||
|
##
|
||||||
|
## Sequence Utilities
|
||||||
|
##
|
||||||
|
## Clear all events from `sequence`.
|
||||||
|
##
|
||||||
|
## This simply resets the size field, the other fields are left untouched.
|
||||||
|
##
|
||||||
|
proc atomSequenceClear*(seq: ptr AtomSequence) {.inline.} =
|
||||||
|
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 NULL 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)
|
||||||
|
c_memcpy(e, event, totalSize)
|
||||||
|
seq.atom.size += atomPadSize(totalSize)
|
||||||
|
return e
|
||||||
|
|
||||||
|
##
|
||||||
|
## Tuple Iterator
|
||||||
|
##
|
||||||
|
## Get an iterator pointing to the first element in `tup`.
|
||||||
|
##
|
||||||
|
proc atomTupleBegin*(tup: ptr AtomTuple): ptr Atom {.inline.} =
|
||||||
|
return cast[ptr Atom](atomContents(Atom, tup))
|
||||||
|
|
||||||
|
##
|
||||||
|
## Return true iff `i` has reached the end of `body`.
|
||||||
|
##
|
||||||
|
proc atomTupleIsEnd*(body: pointer; size: Natural; i: ptr Atom): bool {.inline.} =
|
||||||
|
return cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.int)
|
||||||
|
|
||||||
|
##
|
||||||
|
## Return an iterator to the element following `i`.
|
||||||
|
##
|
||||||
|
proc atomTupleNext*(i: ptr Atom): ptr Atom {.inline.} =
|
||||||
|
return cast[ptr Atom](cast[ptr uint8](i) + sizeof(Atom) + atomPadSize(i.size))
|
||||||
|
|
||||||
|
|
||||||
|
## TODO:
|
||||||
|
##
|
||||||
|
## A iterator for looping over all properties of a Tuple.
|
||||||
|
## @param tuple The tuple to iterate over
|
||||||
|
|
||||||
|
## TODO:
|
||||||
|
## Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body.
|
||||||
|
|
||||||
|
##
|
||||||
|
## Object Iterator
|
||||||
|
##
|
||||||
|
## Return a pointer to the first property in `body`.
|
||||||
|
##
|
||||||
|
proc atomObjectBegin*(body: ptr AtomObjectBody): ptr AtomPropertyBody {.inline.} =
|
||||||
|
return cast[ptr AtomPropertyBody](body + 1)
|
||||||
|
|
||||||
|
##
|
||||||
|
## Return true iff `i` has reached the end of `obj`.
|
||||||
|
##
|
||||||
|
proc atomObjectIsEnd*(body: ptr AtomObjectBody; size: Natural; i: ptr AtomPropertyBody): bool {.inline.} =
|
||||||
|
return cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.int)
|
||||||
|
|
||||||
|
##
|
||||||
|
## Return an iterator to the property following `i`.
|
||||||
|
##
|
||||||
|
proc atomObjectNext*(i: ptr AtomPropertyBody): ptr AtomPropertyBody {.inline.} =
|
||||||
|
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.
|
||||||
|
##
|
|
@ -0,0 +1,189 @@
|
||||||
|
## 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
|
|
@ -0,0 +1,8 @@
|
||||||
|
plugin:
|
||||||
|
urid: urn:foobar#v1
|
||||||
|
port_group:
|
||||||
|
name: "stereo"
|
||||||
|
audio_input:
|
||||||
|
"in_l"
|
||||||
|
audio_input:
|
||||||
|
"in_r"
|
|
@ -0,0 +1,100 @@
|
||||||
|
## 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"
|
||||||
|
|
||||||
|
##
|
||||||
|
## Opaque pointer to host data for uridMap.
|
||||||
|
##
|
||||||
|
|
||||||
|
type UridMapHandle* = pointer
|
||||||
|
|
||||||
|
##
|
||||||
|
## Opaque pointer to host data for uridUnmap.
|
||||||
|
##
|
||||||
|
|
||||||
|
type UridUnmapHandle* = pointer
|
||||||
|
|
||||||
|
##
|
||||||
|
## URI mapped to an integer.
|
||||||
|
##
|
||||||
|
|
||||||
|
type Urid* = distinct uint32
|
||||||
|
|
||||||
|
##
|
||||||
|
## URID Map Feature (LV2_URID__map)
|
||||||
|
##
|
||||||
|
|
||||||
|
type 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 (LV2_URID__unmap)
|
||||||
|
##
|
||||||
|
|
||||||
|
type 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.}
|
|
@ -0,0 +1,42 @@
|
||||||
|
import ../nymph
|
||||||
|
|
||||||
|
## 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 features != nil:
|
||||||
|
var i = 0
|
||||||
|
while true:
|
||||||
|
let feature = features[i]
|
||||||
|
if feature == nil:
|
||||||
|
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