Make MIDI transpose example functional

Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
This commit is contained in:
Christopher Arndt 2024-05-21 18:09:40 +02:00
parent ba6aaa22ab
commit 9bb9321dcb
6 changed files with 63 additions and 78 deletions

View File

@ -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, core, midi, util, urid]
import nymph/atom/util import nymph/atom/util
@ -18,27 +20,17 @@ type
midi_urid: Urid 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; proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
bundlePath: cstring; features: ptr UncheckedArray[ptr Lv2Feature]): bundlePath: cstring; features: ptr UncheckedArray[ptr Lv2Feature]):
Lv2Handle {.cdecl.} = Lv2Handle {.cdecl.} =
printFeatures(features)
let amp: ptr MidiTransposePlugin = createShared(MidiTransposePlugin) let amp: ptr MidiTransposePlugin = createShared(MidiTransposePlugin)
amp.map = cast[ptr UridMap](lv2FeaturesData(features, lv2UridMap)) 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) amp.midi_urid = amp.map[].map(amp.map[].handle, lv2MidiMidiEvent)
echo &"{lv2MidiMidiEvent} = {amp.midi_urid.int}"
return cast[Lv2Handle](amp) return cast[Lv2Handle](amp)
@ -53,18 +45,33 @@ proc connectPort(instance: Lv2Handle; port: cuint;
of PluginPort.Transposition: of PluginPort.Transposition:
amp.transposition = cast[ptr cfloat](dataLocation) amp.transposition = cast[ptr cfloat](dataLocation)
proc activate(instance: Lv2Handle) {.cdecl.} = proc activate(instance: Lv2Handle) {.cdecl.} =
discard discard
proc run(instance: Lv2Handle; nSamples: cuint) {.cdecl.} = proc run(instance: Lv2Handle; nSamples: cuint) {.cdecl.} =
let amp = cast[ptr MidiTransposePlugin](instance) 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: if amp.input.atom.size > 8:
#echo &"Event sequence size: {amp.input.atom.size}"
for ev in items(amp.input): for ev in items(amp.input):
if Urid(ev.body.`type`) == amp.midi_urid: if Urid(ev.body.`type`) == amp.midi_urid:
# TODO var msg = cast[ptr UncheckedArray[uint8]](atomContents(AtomEvent, ev))
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)}"
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.} = proc deactivate(instance: Lv2Handle) {.cdecl.} =

View File

@ -6,6 +6,8 @@
## See <http://lv2plug.in/ns/ext/atom> for details. ## See <http://lv2plug.in/ns/ext/atom> for details.
## ##
import ptrmath
const const
lv2AtomBaseUri ="http://lv2plug.in/ns/ext/atom" lv2AtomBaseUri ="http://lv2plug.in/ns/ext/atom"
lv2AtomPrefix = lv2AtomBaseUri & "#" lv2AtomPrefix = lv2AtomBaseUri & "#"
@ -51,29 +53,12 @@ const
template atomContents*(`type`, atom: untyped): pointer = template atomContents*(`type`, atom: untyped): pointer =
cast[ptr uint8](atom) + sizeof(`type`) 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 ## 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".} template atomBody*(atom: untyped): pointer =
atomContents(Atom, atom)
##
## Const version of LV2_ATOM_BODY.
##
proc atomBodyConst*(atom: untyped) {.importc: "LV2_ATOM_BODY_CONST".}
{.pop.}
]#
type type
## The header of an atom:Atom. ## The header of an atom:Atom.
@ -81,35 +66,35 @@ type
size*: uint32 ## Size in bytes, not including type and size. size*: uint32 ## Size in bytes, not including type and size.
`type`*: uint32 ## of this atom (mapped URI). `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 AtomInt* {.bycopy.} = object
atom*: Atom ## Atom header. atom*: Atom ## Atom header.
body*: int32 ## Integer value. body*: int32 ## Integer value.
## An atom:Long. May be cast to LV2_Atom. ## An atom:Long. May be cast to Atom.
AtomLong* {.bycopy.} = object AtomLong* {.bycopy.} = object
atom*: Atom ## Atom header. atom*: Atom ## Atom header.
body*: int64 ## Integer value. body*: int64 ## Integer value.
## An atom:Float. May be cast to LV2_Atom. ## An atom:Float. May be cast to Atom.
AtomFloat* {.bycopy.} = object AtomFloat* {.bycopy.} = object
atom*: Atom ## Atom header. atom*: Atom ## Atom header.
body*: cfloat ## Floating point value. 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 AtomDouble* {.bycopy.} = object
atom*: Atom ## Atom header. atom*: Atom ## Atom header.
body*: cdouble ## Floating point value. 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 AtomBool* = distinct AtomInt
## An atom:URID. May be cast to LV2_Atom. ## An atom:URID. May be cast to Atom.
AtomUrid* {.bycopy.} = object AtomUrid* {.bycopy.} = object
atom*: Atom ## Atom header. atom*: Atom ## Atom header.
body*: uint32 ## URID. body*: uint32 ## URID.
## An atom:String. May be cast to LV2_Atom. ## An atom:String. May be cast to Atom.
AtomString* {.bycopy.} = object AtomString* {.bycopy.} = object
atom*: Atom ## Atom header. atom*: Atom ## Atom header.
## Contents (a null-terminated UTF-8 string) follow here. ## Contents (a null-terminated UTF-8 string) follow here.
@ -120,12 +105,12 @@ type
lang*: uint32 ## Language URID. lang*: uint32 ## Language URID.
## Contents (a null-terminated UTF-8 string) follow here. ## 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 AtomLiteral* {.bycopy.} = object
atom*: Atom ## Atom header. atom*: Atom ## Atom header.
body*: AtomLiteralBody ## Body. body*: AtomLiteralBody ## Body.
## An atom:Tuple. May be cast to LV2_Atom. ## An atom:Tuple. May be cast to Atom.
AtomTuple* {.bycopy.} = object AtomTuple* {.bycopy.} = object
atom*: Atom ## Atom header. atom*: Atom ## Atom header.
## Contents (a series of complete atoms) follow here. ## Contents (a series of complete atoms) follow here.
@ -136,7 +121,7 @@ type
childType*: uint32 ## The of each element in the vector. childType*: uint32 ## The of each element in the vector.
## Contents (a series of packed atom bodies) follow here. ## 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 AtomVector* {.bycopy.} = object
atom*: Atom ## Atom header. atom*: Atom ## Atom header.
body*: AtomVectorBody ## Body. body*: AtomVectorBody ## Body.
@ -148,23 +133,23 @@ type
value*: Atom ## Value atom header. value*: Atom ## Value atom header.
## Value atom body follows here. ## 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 AtomProperty* {.bycopy.} = object
atom*: Atom ## Atom header. atom*: Atom ## Atom header.
body*: AtomPropertyBody ## Body. 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 AtomObjectBody* {.bycopy.} = object
id*: uint32 ## URID, or 0 for blank. id*: uint32 ## URID, or 0 for blank.
otype*: uint32 ## URID (same as rdf:type, for fast dispatch). otype*: uint32 ## URID (same as rdf:type, for fast dispatch).
## Contents (a series of property bodies) follow here. ## 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 AtomObject* {.bycopy.} = object
atom*: Atom ## Atom header. atom*: Atom ## Atom header.
body*: AtomObjectBody ## Body. 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 AtomEventTime* {.bycopy, union.} = object
frames*: int64 ## Time in audio frames. frames*: int64 ## Time in audio frames.
beats*: cdouble ## Time in beats. beats*: cdouble ## Time in beats.
@ -175,13 +160,13 @@ type
## Body atom contents follow here. ## 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 ## 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: ## to 64-bits, for example:
## <pre> ## <pre>
## | Event 1 (size 6) | Event 2 ## | Event 1 (size 6) | Event 2

View File

@ -12,14 +12,7 @@
from system/ansi_c import c_memcmp, c_memcpy from system/ansi_c import c_memcmp, c_memcpy
import ../atom import ../atom
import ../ptrmath
##
## 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. ## 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`. ## Return an iterator to the element following `i`.
## ##
proc atomSequenceNext*(i: ptr AtomEvent): ptr AtomEvent {.inline.} = 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. ## An iterator for looping over all events in a Sequence.

6
src/nymph/ptrmath.nim Normal file
View File

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

View File

@ -15,27 +15,23 @@ const
lv2UridUnmap* = lv2UridPrefix & "unmap" lv2UridUnmap* = lv2UridPrefix & "unmap"
## ##
## Opaque pointer to host data for uridMap. ## Opaque pointer to host data for UridMap.
## ##
type UridMapHandle* = pointer type UridMapHandle* = pointer
## ##
## Opaque pointer to host data for uridUnmap. ## Opaque pointer to host data for uridUnmap.
## ##
type UridUnmapHandle* = pointer type UridUnmapHandle* = pointer
## ##
## URI mapped to an integer. ## URI mapped to an integer.
## ##
type Urid* = distinct uint32 type Urid* = distinct uint32
## ##
## URID Map Feature (LV2_URID__map) ## URID Map Feature (lv2UridMap)
## ##
type UridMap* {.bycopy.} = object type UridMap* {.bycopy.} = object
## ##
## Opaque pointer to host data. ## Opaque pointer to host data.
@ -67,11 +63,9 @@ type UridMap* {.bycopy.} = object
## ##
map*: proc (handle: UridMapHandle; uri: cstring): Urid map*: proc (handle: UridMapHandle; uri: cstring): Urid
## ##
## URI Unmap Feature (LV2_URID__unmap) ## URI Unmap Feature (lv2UridUnmap)
## ##
type UridUnmap* {.bycopy.} = object type UridUnmap* {.bycopy.} = object
## ##
## Opaque pointer to host data. ## Opaque pointer to host data.
@ -97,4 +91,4 @@ type UridUnmap* {.bycopy.} = object
## ##
unmap*: proc (handle: UridUnmapHandle; urid: Urid): cstring unmap*: proc (handle: UridUnmapHandle; urid: Urid): cstring
proc `==`* (x: Urid, y: Urid): bool {.borrow.} proc `==`* (x: Urid, y: Urid): bool {.borrow.}

View File

@ -1,4 +1,4 @@
import ../nymph import core
## Return the data for a feature in a features array. ## Return the data for a feature in a features array.