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/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.} =

View File

@ -6,6 +6,8 @@
## See <http://lv2plug.in/ns/ext/atom> 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:
## <pre>
## | Event 1 (size 6) | Event 2

View File

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

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"
##
## 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.

View File

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