Some Atom handling refactoring

Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
This commit is contained in:
Christopher Arndt 2024-05-22 19:14:34 +02:00
parent d39cea5a98
commit 11c9fc653d
4 changed files with 143 additions and 112 deletions

View File

@ -29,7 +29,7 @@ proc instantiate(descriptor: ptr Lv2Descriptor; sampleRate: cdouble;
if amp.map == nil: if amp.map == nil:
return nil return nil
amp.midi_urid = amp.map[].map(amp.map[].handle, lv2MidiMidiEvent) amp.midi_urid = amp.map.map(amp.map.handle, lv2MidiMidiEvent)
return cast[Lv2Handle](amp) return cast[Lv2Handle](amp)
@ -56,17 +56,17 @@ proc run(instance: Lv2Handle; nSamples: cuint) {.cdecl.} =
atomSequenceClear(amp.output) atomSequenceClear(amp.output)
amp.output.atom.type = amp.input.atom.type amp.output.atom.type = amp.input.atom.type
if amp.input.atom.size > 8: if not atomSequenceIsEmpty(amp.input):
#echo &"Event sequence size: {amp.input.atom.size}" #echo &"Event sequence size: {amp.input.atom.size}"
let noteOffset = clamp(floor(amp.transposition[] + 0.5), -12, 12).uint8
for ev in items(amp.input): for ev in amp.input:
if Urid(ev.body.`type`) == amp.midi_urid: if ev.body.`type` == amp.midi_urid:
var msg = cast[ptr UncheckedArray[uint8]](atomContents(AtomEvent, ev)) 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)}" #echo &"0x{toHex(msg[0], 2)} 0x{toHex(msg[1], 2)} 0x{toHex(msg[2], 2)}"
case midiGetMessageType(msg[]): case midiGetMessageType(msg[]):
of midiMsgNoteOff, midiMsgNoteOn, midiMsgNotePressure: of midiMsgNoteOff, midiMsgNoteOn, midiMsgNotePressure:
let noteOffset = clamp(floor(amp.transposition[] + 0.5), -12, 12).uint8
msg[1] = clamp(msg[1] + noteOffset, 0, 127).uint8 msg[1] = clamp(msg[1] + noteOffset, 0, 127).uint8
else: else:
discard discard

View File

@ -7,6 +7,7 @@
## ##
import ptrmath import ptrmath
import urid
const const
lv2AtomBaseUri ="http://lv2plug.in/ns/ext/atom" lv2AtomBaseUri ="http://lv2plug.in/ns/ext/atom"
@ -64,7 +65,7 @@ type
## The header of an atom:Atom. ## The header of an atom:Atom.
Atom* {.bycopy.} = object Atom* {.bycopy.} = object
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`*: Urid ## Type of this atom (mapped URI).
## An atom:Int or atom:Bool. May be cast to Atom. ## An atom:Int or atom:Bool. May be cast to Atom.
AtomInt* {.bycopy.} = object AtomInt* {.bycopy.} = object
@ -92,7 +93,7 @@ type
## An atom:URID. May be cast to 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*: Urid ## Urid.
## An atom:String. May be cast to Atom. ## An atom:String. May be cast to Atom.
AtomString* {.bycopy.} = object AtomString* {.bycopy.} = object
@ -101,8 +102,8 @@ type
## The body of an atom:Literal. ## The body of an atom:Literal.
AtomLiteralBody* {.bycopy.} = object AtomLiteralBody* {.bycopy.} = object
datatype*: uint32 ## DataURID. datatype*: Urid ## Datatype Urid.
lang*: uint32 ## Language URID. lang*: Urid ## 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 Atom. ## An atom:Literal. May be cast to Atom.
@ -128,8 +129,8 @@ type
## The body of an atom:Property (typically in an atom:Object). ## The body of an atom:Property (typically in an atom:Object).
AtomPropertyBody* {.bycopy.} = object AtomPropertyBody* {.bycopy.} = object
key*: uint32 ## Key (predicate) (mapped URI). key*: Urid ## Key (predicate) (mapped URI).
context*: uint32 ## Context URID (may be, and generally is, 0). context*: Urid ## Context URID (may be, and generally is, 0).
value*: Atom ## Value atom header. value*: Atom ## Value atom header.
## Value atom body follows here. ## Value atom body follows here.
@ -140,8 +141,8 @@ type
## The body of an atom:Object. May be cast to 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*: Urid ## Urid, or 0 for blank.
otype*: uint32 ## URID (same as rdf:type, for fast dispatch). otype*: Urid ## 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 Atom. ## An atom:Object. May be cast to Atom.
@ -176,7 +177,7 @@ type
## </pre> ## </pre>
## ##
AtomSequenceBody* {.bycopy.} = object AtomSequenceBody* {.bycopy.} = object
unit*: uint32 ## URID of unit of event time stamps. unit*: Urid ## Urid of unit of event time stamps.
pad*: uint32 ## Currently unused. pad*: uint32 ## Currently unused.
## Contents (a series of events) follow here. ## Contents (a series of events) follow here.

View File

@ -13,6 +13,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 import ../ptrmath
import ../urid
## ##
## Pad a size to 64 bits. ## Pad a size to 64 bits.
@ -31,7 +32,7 @@ proc atomTotalSize*(atom: ptr Atom): uint32 {.inline.} =
## Return true iff `atom` is null. ## Return true iff `atom` is null.
## ##
proc atomIsNull*(atom: ptr Atom): bool {.inline.} = proc atomIsNull*(atom: ptr Atom): bool {.inline.} =
return atom == nil or (atom.`type` == 0 and atom.size == 0) return atom == nil or (atom.`type` == Urid(0) and atom.size == 0)
## ##
## Return true iff `a` is equal to `b`. ## Return true iff `a` is equal to `b`.
@ -46,31 +47,30 @@ proc atomEquals*(a: ptr Atom; b: ptr Atom): bool {.inline.} =
## ##
## Get an iterator pointing to the first event in a Sequence body. ## Get an iterator pointing to the first event in a Sequence body.
## ##
proc atomSequenceBegin*(body: ptr AtomSequenceBody): ptr AtomEvent {.inline.} = template atomSequenceBegin*(body: ptr AtomSequenceBody): ptr AtomEvent =
return cast[ptr AtomEvent](body + 1) cast[ptr AtomEvent](body + 1)
## ##
## Get an iterator pointing to the end of a Sequence body. ## Get an iterator pointing to the end of a Sequence body.
## ##
proc atomSequenceEnd*(body: ptr AtomSequenceBody; size: uint32): ptr AtomEvent {.inline.} = template atomSequenceEnd*(body: ptr AtomSequenceBody; size: Natural): ptr AtomEvent =
return cast[ptr AtomEvent](cast[ptr uint8](body) + atomPadSize(size)) cast[ptr AtomEvent](cast[ptr uint8](body) + atomPadSize(size.uint32))
## ##
## Return true iff `i` has reached the end of `body`. ## Return true iff `i` has reached the end of `body`.
## ##
proc atomSequenceIsEnd*(body: ptr AtomSequenceBody; size: Natural; i: ptr AtomEvent): bool {.inline.} = template atomSequenceIsEnd*(body: ptr AtomSequenceBody; size: Natural; i: ptr AtomEvent): bool =
return cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.uint) cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.uint)
## ##
## Return an iterator to the element following `i`. ## Return an iterator to the element following `i`.
## ##
proc atomSequenceNext*(i: ptr AtomEvent): ptr AtomEvent {.inline.} = template atomSequenceNext*(i: ptr AtomEvent): ptr AtomEvent =
return cast[ptr AtomEvent](cast[ptr uint8](i) + sizeof(AtomEvent) + atomPadSize(i.body.size)) 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 an AtomSequence.
## @param seq The sequence to iterate over ## @param seq Pointer to the sequence to iterate over
## ##
iterator items*(seq: ptr AtomSequence): ptr AtomEvent {.inline.} = iterator items*(seq: ptr AtomSequence): ptr AtomEvent {.inline.} =
var event = atomSequenceBegin(seq.body.addr) var event = atomSequenceBegin(seq.body.addr)
@ -78,11 +78,28 @@ iterator items*(seq: ptr AtomSequence): ptr AtomEvent {.inline.} =
yield event yield event
event = atomSequenceNext(event) event = atomSequenceNext(event)
## TODO: Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. ##
## An iterator for looping over all events in an AtomSequenceBody.
## @param body Pointer to the sequence body to iterate over
## @param size Size in bytes of the sequence body (including the 8 bytes of AtomSequenceBody)
##
iterator items*(body: ptr AtomSequenceBody, size: Natural): ptr AtomEvent {.inline.} =
var event = atomSequenceBegin(body)
while not atomSequenceIsEnd(body, size, event):
yield event
event = atomSequenceNext(event)
## ##
## Sequence Utilities ## Sequence Utilities
## ##
##
## Test if AtomSequence is empty, i.e the body has no events
##
template atomSequenceIsEmpty*(seq: ptr AtomSequence): bool =
seq.atom.size == sizeof(AtomSequenceBody).uint32
##
## Clear all events from `sequence`. ## Clear all events from `sequence`.
## ##
## This simply resets the size field, the other fields are left untouched. ## This simply resets the size field, the other fields are left untouched.
@ -118,48 +135,60 @@ proc atomSequenceAppendEvent*(seq: ptr AtomSequence; capacity: uint32;
## ##
## Get an iterator pointing to the first element in `tup`. ## Get an iterator pointing to the first element in `tup`.
## ##
proc atomTupleBegin*(tup: ptr AtomTuple): ptr Atom {.inline.} = template atomTupleBegin*(tup: ptr AtomTuple): ptr Atom =
return cast[ptr Atom](atomContents(Atom, tup)) cast[ptr Atom](atomContents(Atom, tup))
## ##
## Return true iff `i` has reached the end of `body`. ## Return true iff `i` has reached the end of `body`.
## ##
proc atomTupleIsEnd*(body: pointer; size: Natural; i: ptr Atom): bool {.inline.} = template atomTupleIsEnd*(body: pointer; size: Natural; i: ptr Atom): bool =
return cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.int) cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.uint)
## ##
## Return an iterator to the element following `i`. ## Return an iterator to the element following `i`.
## ##
proc atomTupleNext*(i: ptr Atom): ptr Atom {.inline.} = template atomTupleNext*(i: ptr Atom): ptr Atom =
return cast[ptr Atom](cast[ptr uint8](i) + sizeof(Atom) + atomPadSize(i.size)) cast[ptr Atom](cast[ptr uint8](i) + sizeof(Atom) + atomPadSize(i.size))
## TODO:
## ##
## A iterator for looping over all properties of a Tuple. ## An iterator for looping over all elements of an AtomTuple.
## @param tuple The tuple to iterate over ## @param tuple Pointer to the tuple to iterate over
##
iterator items*(tup: ptr AtomTuple): ptr Atom {.inline.} =
var atom = atomTupleBegin(tup)
while not atomTupleIsEnd(atomBody(tup), tup.atom.size, atom):
yield atom
atom = atomTupleNext(atom)
## TODO: ##
## Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. ## An iterator for looping over all elements of a headerless tuple.
## @param tuple Pointer to the first Atom of the tuple to iterate over
## @param size Size in bytes of the tuple body
##
iterator items*(tup: ptr Atom, size: Natural): ptr Atom {.inline.} =
var atom = tup
while not atomTupleIsEnd(tup, size, atom):
yield atom
atom = atomTupleNext(atom)
## ##
## Object Iterator ## Object Iterator
## ##
## Return a pointer to the first property in `body`. ## Return a pointer to the first property in `body`.
## ##
proc atomObjectBegin*(body: ptr AtomObjectBody): ptr AtomPropertyBody {.inline.} = template atomObjectBegin*(body: ptr AtomObjectBody): ptr AtomPropertyBody =
return cast[ptr AtomPropertyBody](body + 1) cast[ptr AtomPropertyBody](body + 1)
## ##
## Return true iff `i` has reached the end of `obj`. ## Return true iff `i` has reached the end of `obj`.
## ##
proc atomObjectIsEnd*(body: ptr AtomObjectBody; size: Natural; i: ptr AtomPropertyBody): bool {.inline.} = template atomObjectIsEnd*(body: ptr AtomObjectBody; size: Natural; i: ptr AtomPropertyBody): bool =
return cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.int) cast[ptr uint8](i) >= (cast[ptr uint8](body) + size.uint)
## ##
## Return an iterator to the property following `i`. ## Return an iterator to the property following `i`.
## ##
proc atomObjectNext*(i: ptr AtomPropertyBody): ptr AtomPropertyBody {.inline.} = template atomObjectNext*(i: ptr AtomPropertyBody): ptr AtomPropertyBody =
let value = cast[ptr Atom](cast[ptr uint8](i) + 2 * sizeof(uint32)) 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)) return cast[ptr AtomPropertyBody](cast[ptr uint8](i) + atomPadSize(sizeof(AtomPropertyBody).uint32 + value.size))

View File

@ -14,81 +14,82 @@ const
lv2UridMap* = lv2UridPrefix & "map" lv2UridMap* = lv2UridPrefix & "map"
lv2UridUnmap* = lv2UridPrefix & "unmap" lv2UridUnmap* = lv2UridPrefix & "unmap"
## type
## Opaque pointer to host data for UridMap. ##
## ## URI mapped to an integer.
type UridMapHandle* = pointer ##
Urid* = distinct uint32
## ##
## Opaque pointer to host data for uridUnmap. ## Opaque pointer to host data for UridMap.
## ##
type UridUnmapHandle* = pointer UridMapHandle* = pointer
## ##
## URI mapped to an integer. ## Opaque pointer to host data for uridUnmap.
## ##
type Urid* = distinct uint32 UridUnmapHandle* = pointer
##
## URID Map Feature (lv2UridMap)
##
type UridMap* {.bycopy.} = object
## ##
## Opaque pointer to host data. ## URID Map Feature (lv2UridMap)
## ##
## This MUST be passed to map_uri() whenever it is called. UridMap* {.bycopy.} = object
## Otherwise, it must not be interpreted in any way. ##
## ## Opaque pointer to host data.
handle*: UridMapHandle ##
## ## This MUST be passed to map_uri() whenever it is called.
## Get the numeric ID of a URI. ## Otherwise, it must not be interpreted in any way.
## ##
## If the ID does not already exist, it will be created. handle*: UridMapHandle
## ##
## This function is referentially transparent; any number of calls with the ## Get the numeric ID of a URI.
## 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 ## If the ID does not already exist, it will be created.
## same ID if the host considers those URIs equivalent. ##
## ## This function is referentially transparent; any number of calls with the
## This function is not necessarily very fast or RT-safe: plugins SHOULD ## same arguments is guaranteed to return the same value over the life of a
## cache any IDs they might need in performance critical situations. ## plugin instance. Note, however, that several URIs MAY resolve to the
## ## same ID if the host considers those URIs equivalent.
## 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 ## This function is not necessarily very fast or RT-safe: plugins SHOULD
## return 0 from this function in non-exceptional circumstances (i.e. the ## cache any IDs they might need in performance critical situations.
## URI map SHOULD be dynamic). ##
## ## The return value 0 is reserved and indicates that an ID for that URI
## @param handle Must be the callback_data member of this struct. ## could not be created for whatever reason. However, hosts SHOULD NOT
## @param uri The URI to be mapped to an integer ID. ## return 0 from this function in non-exceptional circumstances (i.e. the
## ## URI map SHOULD be dynamic).
map*: proc (handle: UridMapHandle; uri: cstring): Urid ##
## @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 (lv2UridUnmap)
##
type UridUnmap* {.bycopy.} = object
## ##
## Opaque pointer to host data. ## URI Unmap Feature (lv2UridUnmap)
## ##
## This MUST be passed to unmap() whenever it is called. UridUnmap* {.bycopy.} = object
## Otherwise, it must not be interpreted in any way. ##
## ## Opaque pointer to host data.
handle*: UridUnmapHandle ##
## ## This MUST be passed to unmap() whenever it is called.
## Get the URI for a previously mapped numeric ID. ## Otherwise, it must not be interpreted in any way.
## ##
## Returns NULL if `urid` is not yet mapped. Otherwise, the corresponding handle*: UridUnmapHandle
## 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 ## Get the URI for a previously mapped numeric ID.
## 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 ## Returns NULL if `urid` is not yet mapped. Otherwise, the corresponding
## of the plugin. Plugins that intend to perform string comparison on ## URI is returned in a canonical form. This MAY not be the exact same
## unmapped URIs SHOULD first canonicalise URI strings with a call to ## string that was originally passed to LV2_URID_Map::map(), but it MUST be
## map_uri() followed by a call to unmap_uri(). ## 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
## @param handle Must be the callback_data member of this struct. ## of the plugin. Plugins that intend to perform string comparison on
## @param urid The ID to be mapped back to the URI string. ## unmapped URIs SHOULD first canonicalise URI strings with a call to
## ## map_uri() followed by a call to unmap_uri().
unmap*: proc (handle: UridUnmapHandle; urid: Urid): cstring ##
## @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.} proc `==`* (x: Urid, y: Urid): bool {.borrow.}