Implement MIDI procesing (WIP)
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
This commit is contained in:
		
							parent
							
								
									ff6ddf77d2
								
							
						
					
					
						commit
						ba6aaa22ab
					
				
							
								
								
									
										8
									
								
								examples/miditranspose.lv2/manifest.ttl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								examples/miditranspose.lv2/manifest.ttl
									
									
									
									
									
										Normal 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:miditranspose>
 | 
			
		||||
    a lv2:Plugin ;
 | 
			
		||||
    lv2:binary <libmiditranspose.so> ;
 | 
			
		||||
    rdfs:seeAlso <miditranspose.ttl> .
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								examples/miditranspose.lv2/miditranspose.ttl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								examples/miditranspose.lv2/miditranspose.ttl
									
									
									
									
									
										Normal file
									
								
							@ -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 .
 | 
			
		||||
							
								
								
									
										98
									
								
								examples/miditranspose.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								examples/miditranspose.nim
									
									
									
									
									
										Normal file
									
								
							@ -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({
 | 
			
		||||
    "amp": "urn:nymph:examples:amp",
 | 
			
		||||
    "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")
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
        echo &"Example '{ex.name}' shared library not found. Use task 'build_ex' to build it."
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										201
									
								
								src/nymph/atom.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								src/nymph/atom.nim
									
									
									
									
									
										Normal file
									
								
							@ -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.
 | 
			
		||||
							
								
								
									
										182
									
								
								src/nymph/atom/util.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								src/nymph/atom/util.nim
									
									
									
									
									
										Normal file
									
								
							@ -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.
 | 
			
		||||
##
 | 
			
		||||
							
								
								
									
										189
									
								
								src/nymph/midi.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								src/nymph/midi.nim
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
			
		||||
							
								
								
									
										8
									
								
								src/nymph/plugin.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/nymph/plugin.nim
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
plugin:
 | 
			
		||||
    urid: urn:foobar#v1
 | 
			
		||||
    port_group:
 | 
			
		||||
        name: "stereo"
 | 
			
		||||
        audio_input:
 | 
			
		||||
            "in_l"
 | 
			
		||||
        audio_input:
 | 
			
		||||
            "in_r"
 | 
			
		||||
							
								
								
									
										100
									
								
								src/nymph/urid.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/nymph/urid.nim
									
									
									
									
									
										Normal file
									
								
							@ -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.}
 | 
			
		||||
							
								
								
									
										42
									
								
								src/nymph/util.nim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/nymph/util.nim
									
									
									
									
									
										Normal file
									
								
							@ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user