From 9bb9321dcb2113dbdd1b594f33d37c8435e15a64 Mon Sep 17 00:00:00 2001 From: Christopher Arndt Date: Tue, 21 May 2024 18:09:40 +0200 Subject: [PATCH] Make MIDI transpose example functional Signed-off-by: Christopher Arndt --- examples/miditranspose.nim | 47 ++++++++++++++++------------- src/nymph/atom.nim | 61 ++++++++++++++------------------------ src/nymph/atom/util.nim | 11 ++----- src/nymph/ptrmath.nim | 6 ++++ src/nymph/urid.nim | 14 +++------ src/nymph/util.nim | 2 +- 6 files changed, 63 insertions(+), 78 deletions(-) create mode 100644 src/nymph/ptrmath.nim diff --git a/examples/miditranspose.nim b/examples/miditranspose.nim index eec3ba5..df5c14c 100644 --- a/examples/miditranspose.nim +++ b/examples/miditranspose.nim @@ -1,6 +1,8 @@ -## A simple MIDI event processor LV2 plugin +## A simple MIDI transpose LV2 plugin -import std/[math, strformat, strutils] +import std/math +#import std/strformat +#import std/strutils import nymph/[atom, core, midi, util, urid] import nymph/atom/util @@ -18,27 +20,17 @@ type 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 + + if amp.map == nil: + return nil + amp.midi_urid = amp.map[].map(amp.map[].handle, lv2MidiMidiEvent) - echo &"{lv2MidiMidiEvent} = {amp.midi_urid.int}" + return cast[Lv2Handle](amp) @@ -53,18 +45,33 @@ proc connectPort(instance: Lv2Handle; port: cuint; 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) + let outCapacity = amp.output.atom.size + atomSequenceClear(amp.output) + amp.output.atom.type = amp.input.atom.type + if amp.input.atom.size > 8: + #echo &"Event sequence size: {amp.input.atom.size}" + 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)}" + var msg = cast[ptr UncheckedArray[uint8]](atomContents(AtomEvent, ev)) + #echo &"0x{toHex(msg[0], 2)} 0x{toHex(msg[1], 2)} 0x{toHex(msg[2], 2)}" + + case midiGetMessageType(msg[]): + of midiMsgNoteOff, midiMsgNoteOn, midiMsgNotePressure: + let noteOffset = clamp(floor(amp.transposition[] + 0.5), -12, 12).uint8 + msg[1] = clamp(msg[1] + noteOffset, 0, 127).uint8 + else: + discard + + discard atomSequenceAppendEvent(amp.output, outCapacity, ev) proc deactivate(instance: Lv2Handle) {.cdecl.} = diff --git a/src/nymph/atom.nim b/src/nymph/atom.nim index 4a19e19..be316f0 100644 --- a/src/nymph/atom.nim +++ b/src/nymph/atom.nim @@ -6,6 +6,8 @@ ## See for details. ## +import ptrmath + const lv2AtomBaseUri ="http://lv2plug.in/ns/ext/atom" lv2AtomPrefix = lv2AtomBaseUri & "#" @@ -51,29 +53,12 @@ const 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). +## data just past the 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.} -]# +template atomBody*(atom: untyped): pointer = + atomContents(Atom, atom) type ## The header of an atom:Atom. @@ -81,35 +66,35 @@ type 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. + ## An atom:Int or atom:Bool. May be cast to Atom. AtomInt* {.bycopy.} = object atom*: Atom ## Atom header. body*: int32 ## Integer value. - ## An atom:Long. May be cast to LV2_Atom. + ## An atom:Long. May be cast to Atom. AtomLong* {.bycopy.} = object atom*: Atom ## Atom header. body*: int64 ## Integer value. - ## An atom:Float. May be cast to LV2_Atom. + ## An atom:Float. May be cast to Atom. AtomFloat* {.bycopy.} = object atom*: Atom ## Atom header. body*: cfloat ## Floating point value. - ## An atom:Double. May be cast to LV2_Atom. + ## An atom:Double. May be cast to Atom. AtomDouble* {.bycopy.} = object atom*: Atom ## Atom header. body*: cdouble ## Floating point value. - ## An atom:Bool. May be cast to LV2_Atom. + ## An atom:Bool. May be cast to Atom. AtomBool* = distinct AtomInt - ## An atom:URID. May be cast to LV2_Atom. + ## An atom:URID. May be cast to Atom. AtomUrid* {.bycopy.} = object atom*: Atom ## Atom header. body*: uint32 ## URID. - ## An atom:String. May be cast to LV2_Atom. + ## An atom:String. May be cast to Atom. AtomString* {.bycopy.} = object atom*: Atom ## Atom header. ## Contents (a null-terminated UTF-8 string) follow here. @@ -120,12 +105,12 @@ type lang*: uint32 ## Language URID. ## Contents (a null-terminated UTF-8 string) follow here. - ## An atom:Literal. May be cast to LV2_Atom. + ## An atom:Literal. May be cast to Atom. AtomLiteral* {.bycopy.} = object atom*: Atom ## Atom header. body*: AtomLiteralBody ## Body. - ## An atom:Tuple. May be cast to LV2_Atom. + ## An atom:Tuple. May be cast to Atom. AtomTuple* {.bycopy.} = object atom*: Atom ## Atom header. ## Contents (a series of complete atoms) follow here. @@ -136,7 +121,7 @@ type 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. + ## An atom:Vector. May be cast to Atom. AtomVector* {.bycopy.} = object atom*: Atom ## Atom header. body*: AtomVectorBody ## Body. @@ -148,23 +133,23 @@ type value*: Atom ## Value atom header. ## Value atom body follows here. - ## An atom:Property. May be cast to LV2_Atom. + ## An atom:Property. May be cast to Atom. AtomProperty* {.bycopy.} = object atom*: Atom ## Atom header. body*: AtomPropertyBody ## Body. - ## The body of an atom:Object. May be cast to LV2_Atom. + ## The body of an atom:Object. May be cast to 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. + ## An atom:Object. May be cast to Atom. AtomObject* {.bycopy.} = object atom*: Atom ## Atom header. body*: AtomObjectBody ## Body. - ## The header of an atom:Event. Note this is NOT an LV2_Atom. + ## The header of an atom:Event. Note this is NOT an Atom. AtomEventTime* {.bycopy, union.} = object frames*: int64 ## Time in audio frames. beats*: cdouble ## Time in beats. @@ -175,13 +160,13 @@ type ## Body atom contents follow here. ## - ## The body of an atom:Sequence (a sequence of events). + ## The body of an AtomSequence (a sequence of events). ## - ## The unit field is either a URID that described an appropriate time stamp + ## 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. + ## lv2Descriptor.run(), the default stamp is audio frames. ## - ## The contents of a sequence is a series of LV2_Atom_Event, each aligned + ## The contents of a sequence is a series of AtomEvent, each aligned ## to 64-bits, for example: ##
     ## | Event 1 (size 6)                              | Event 2
diff --git a/src/nymph/atom/util.nim b/src/nymph/atom/util.nim
index 7193666..6df4228 100644
--- a/src/nymph/atom/util.nim
+++ b/src/nymph/atom/util.nim
@@ -12,14 +12,7 @@
 
 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)))
+import ../ptrmath
 
 ##
 ## Pad a size to 64 bits.
@@ -73,7 +66,7 @@ proc atomSequenceIsEnd*(body: ptr AtomSequenceBody; size: Natural; i: ptr AtomEv
 ## 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))
+    return cast[ptr AtomEvent](cast[ptr uint8](i) + sizeof(AtomEvent) + atomPadSize(i.body.size))
 
 ##
 ## An iterator for looping over all events in a Sequence.
diff --git a/src/nymph/ptrmath.nim b/src/nymph/ptrmath.nim
new file mode 100644
index 0000000..9ee7395
--- /dev/null
+++ b/src/nymph/ptrmath.nim
@@ -0,0 +1,6 @@
+##
+## 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)))
diff --git a/src/nymph/urid.nim b/src/nymph/urid.nim
index 06c0d8c..e6e1c63 100644
--- a/src/nymph/urid.nim
+++ b/src/nymph/urid.nim
@@ -15,27 +15,23 @@ const
     lv2UridUnmap* = lv2UridPrefix & "unmap"
 
 ##
-## Opaque pointer to host data for uridMap.
+## 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)
+## URID Map Feature (lv2UridMap)
 ##
-
 type UridMap* {.bycopy.} = object
     ##
     ## Opaque pointer to host data.
@@ -67,11 +63,9 @@ type UridMap* {.bycopy.} = object
     ##
     map*: proc (handle: UridMapHandle; uri: cstring): Urid
 
-
 ##
-## URI Unmap Feature (LV2_URID__unmap)
+## URI Unmap Feature (lv2UridUnmap)
 ##
-
 type UridUnmap* {.bycopy.} = object
     ##
     ## Opaque pointer to host data.
@@ -97,4 +91,4 @@ type UridUnmap* {.bycopy.} = object
     ##
     unmap*: proc (handle: UridUnmapHandle; urid: Urid): cstring
 
-proc `==`* (x: Urid, y: Urid): bool {.borrow.}
\ No newline at end of file
+proc `==`* (x: Urid, y: Urid): bool {.borrow.}
diff --git a/src/nymph/util.nim b/src/nymph/util.nim
index 38833ef..74d65b2 100644
--- a/src/nymph/util.nim
+++ b/src/nymph/util.nim
@@ -1,4 +1,4 @@
-import ../nymph
+import core
 
 ## Return the data for a feature in a features array.