Compare commits

..

5 Commits

Author SHA1 Message Date
Christopher Arndt 05ec305a04 refactor: minor example tweaks
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
2023-05-25 18:04:16 +02:00
Christopher Arndt 1027a2e1dd docs: add todo document
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
2023-05-25 18:04:16 +02:00
Christopher Arndt 892d087c40 tests: add initial tests
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
2023-05-25 18:04:16 +02:00
Christopher Arndt bff6f5e9fa feat: wrap ringbuffer API and add usage example
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
2023-05-25 18:04:16 +02:00
Christopher Arndt 6112afad73 fix: export internal client API functions
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
2023-05-25 17:39:25 +02:00
7 changed files with 147 additions and 12 deletions

View File

@ -8,11 +8,10 @@ A [Nim] wrapper for the [JACK] [C API]
This software is in *alpha status* and has no official release yet.
The majority of JACK client APIs have been wrapped and are functional (see
[examples]), but some APIs (e.g. threading and ringbuffers) still need
wrapping. Others, like the server control or the deprecated session API, will
probably not covered by these bindings. While this project is in alpha or beta
stage, symbol names may still be changed and things moved around before the
first public release.
[examples]), but some APIs (e.g. threading) still need wrapping. Others, like
the server control or the deprecated session API, will probably not covered by
these bindings. While this project is in alpha or beta stage, symbol names may
still be changed and things moved around before the first public release.
Also, I plan to add a higher-level abstraction on top of the direct mapping
from Nim procs and types to C functions and types, probably in the form of

14
TODO.md Normal file
View File

@ -0,0 +1,14 @@
# TODO
## Threading API
Still needs to be wrapped. How to handle `jack_native_thread_t` type?
## Internal Clients
Jack 1 and JACK 2 are disagreeing on the signatures of the functions for
loading and getting a handles for internal clients:
* https://github.com/jackaudio/jack2/blob/develop/common/jack/intclient.h#L66
* https://github.com/jackaudio/headers/blob/2bfa5069718ca4f4dc091e0be845958f2d8a5ba8/intclient.h#L69
* https://jackaudio.org/api/intclient_8h.html#a176a2daf66c8777eb1a845068fd7a822

View File

@ -1,7 +1,6 @@
import std/logging
import jacket
var jclient: ClientP
var status: cint
var log = newConsoleLogger(when defined(release): lvlInfo else: lvlDebug)
@ -12,7 +11,7 @@ proc errorCb(msg: cstring) {.cdecl.} =
addHandler(log)
setErrorFunction(errorCb)
jclient = clientOpen("jacket_info", NullOption.ord, status.addr)
var jclient = clientOpen("jacket_info", NullOption.ord, status.addr)
debug "JACK server status: " & $status
if jclient == nil:
@ -22,9 +21,9 @@ if jclient == nil:
echo("JACK version: ", getVersionString())
echo("Sample rate: ", jclient.getSampleRate)
echo("Buffer size: ", jclient.getBufferSize)
echo("RT enabled: ", if jclient.isRealtime > 0: "yes" else: "no")
echo("DSP load: ", jclient.cpuLoad, "%")
echo("Server time: ", getTime())
echo("Client name: ", jclient.getClientName)
echo("RT enabled: ", if jclient.isRealtime > 0: "yes" else: "no")
discard jclient.clientClose

View File

@ -0,0 +1,49 @@
import std/[logging, strformat]
import jacket
var log = newConsoleLogger(when defined(release): lvlInfo else: lvlDebug)
proc main() =
addHandler(log)
let size = 32
var read, written: int
var data = [0x64.uint8, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66]
var recvBuf: array[8, uint8]
let rb = ringbufferCreate(size.csize_t)
debug fmt"Created ringbuffer of {size} bytes size."
doAssert ringbufferReadSpace(rb) == 0
written = cast[int](ringbufferWrite(rb, cast[cstring](data.addr), 4))
doAssert written == 4
debug fmt"Written {written} bytes to ringbuffer."
doAssert ringbufferReadSpace(rb) == 4
written = cast[int](ringbufferWrite(rb, cast[cstring](data[4].addr), 4))
doAssert written == 4
debug fmt"Written {written} bytes to ringbuffer."
doAssert ringbufferReadSpace(rb) == 8
read = cast[int](ringbufferRead(rb, cast[cstring](recvBuf.addr), 4))
doAssert read == 4
debug fmt"Read {read} bytes from ringbuffer into receive buffer: {recvBuf}"
doAssert ringbufferReadSpace(rb) == 4
read = cast[int](ringbufferRead(rb, cast[cstring](recvBuf[4].addr), 4))
doAssert read == 4
debug fmt"Read {read} bytes from ringbuffer into receive buffer: {recvBuf}"
doAssert ringbufferReadSpace(rb) == 0
debug "Freeing ringbuffer memory."
ringbufferFree(rb)
when(isMainModule):
main()

View File

@ -172,6 +172,17 @@ const
EXTENDED_TIME_INFO* = true
JACK_TICK_DOUBLE* = true
# Ringbuffer
type
RingbufferData* = object
buf*: ptr char
len*: csize_t
RingbufferDataP* = ptr RingbufferData
Ringbuffer = distinct object
RingbufferP* = ptr Ringbuffer
# Metadata
type
@ -291,22 +302,22 @@ jack_nframes_t jack_thread_wait (jack_client_t *client, int status)
# --------------------------- Internal Clients ----------------------------
# char *jack_get_internal_client_name (jack_client_t *client, jack_intclient_t intclient);
proc getInternalClientName(client: ClientP; intclient: IntClient): cstring {.
proc getInternalClientName*(client: ClientP; intclient: IntClient): cstring {.
importc: "jack_get_internal_client_name".}
# jack_intclient_t jack_internal_client_handle (jack_client_t *client, const char *client_name,
# jack_status_t *status)
proc internalClientHandle(client: ClientP; clientName: cstring; status: ptr cint): IntClient {.
proc internalClientHandle*(client: ClientP; clientName: cstring; status: ptr cint): IntClient {.
importc: "jack_internal_client_handle".}
# jack_intclient_t jack_internal_client_load (jack_client_t *client, const char *client_name,
# jack_options_t options, jack_status_t *status, ...)
proc internalClientLoad(client: ClientP; clientName: cstring; options: cint; status: ptr cint): IntClient {.
proc internalClientLoad*(client: ClientP; clientName: cstring; options: cint; status: ptr cint): IntClient {.
varargs, importc: "jack_internal_client_load".}
# jack_status_t jack_internal_client_unload (jack_client_t *client, jack_intclient_t intclient)
proc internalClientUnload(client: ClientP; intclient: IntClient): cint {.
proc internalClientUnload*(client: ClientP; intclient: IntClient): cint {.
importc: "jack_internal_client_unload".}
#[ DEPRECATED
@ -643,6 +654,47 @@ void jack_get_transport_info (jack_client_t *client, jack_transport_info_t *tinf
void jack_set_transport_info (jack_client_t *client, jack_transport_info_t *tinfo)
]#
# ----------------------------- Ringbuffers -------------------------------
# jack_ringbuffer_t *jack_ringbuffer_create (size_t sz)
proc ringbufferCreate*(sz: csize_t): RingbufferP {.importc: "jack_ringbuffer_create".}
# void jack_ringbuffer_free (jack_ringbuffer_t *rb)
proc ringbufferFree*(rb: RingbufferP) {.importc: "jack_ringbuffer_free".}
# void jack_ringbuffer_get_read_vector (const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec)
proc ringbufferGetReadVector*(rb: RingbufferP, vec: var RingbufferDataP) {.importc: "jack_ringbuffer_get_read_vector".}
# void jack_ringbuffer_get_write_vector (const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec)
proc ringbufferGetWriteVector*(rb: RingbufferP, vec: var RingbufferDataP) {.importc: "jack_ringbuffer_get_write_vector".}
# size_t jack_ringbuffer_read (jack_ringbuffer_t *rb, char *dest, size_t cnt)
proc ringbufferRead*(rb: RingbufferP, dest: cstring, cnt: csize_t): csize_t {.importc: "jack_ringbuffer_read".}
# size_t jack_ringbuffer_peek (jack_ringbuffer_t *rb, char *dest, size_t cnt)
proc ringbufferPeek*(rb: RingbufferP, dest: cstring, cnt: csize_t): csize_t {.importc: "jack_ringbuffer_peek".}
# void jack_ringbuffer_read_advance (jack_ringbuffer_t *rb, size_t cnt)
proc ringbufferReadAdvance*(rb: RingbufferP, cnt: csize_t) {.importc: "jack_ringbuffer_read_advance".}
# size_t jack_ringbuffer_read_space (const jack_ringbuffer_t *rb)
proc ringbufferReadSpace*(rb: RingbufferP): csize_t {.importc: "jack_ringbuffer_read_space".}
# int jack_ringbuffer_mlock (jack_ringbuffer_t *rb)
proc ringbufferMlock*(rb: RingbufferP): int {.importc: "jack_ringbuffer_mlock".}
# void jack_ringbuffer_reset (jack_ringbuffer_t *rb)
proc ringbufferReset*(rb: RingbufferP) {.importc: "jack_ringbuffer_reset".}
# size_t jack_ringbuffer_write (jack_ringbuffer_t *rb, const char *src, size_t cnt)
proc ringbufferWrite*(rb: RingbufferP, src: cstring, cnt: csize_t): csize_t {.importc: "jack_ringbuffer_write".}
# void jack_ringbuffer_write_advance (jack_ringbuffer_t *rb, size_t cnt)
proc ringbufferWriteAdvance*(rb: RingbufferP, cnt: csize_t) {.importc: "jack_ringbuffer_write_advance".}
# size_t jack_ringbuffer_write_space (const jack_ringbuffer_t *rb)
proc ringbufferWriteSpace*(rb: RingbufferP): csize_t {.importc: "jack_ringbuffer_write_space".}
# ------------------------------- Metadata --------------------------------
# int jack_set_property (jack_client_t*, jack_uuid_t subject, const char* key, const char* value, const char* type)

View File

@ -1 +1,3 @@
--path:"../src/"
--warning[BareExcept]:off
--warning[UnusedImport]:off

20
tests/test_version.nim Normal file
View File

@ -0,0 +1,20 @@
import std/[re, strformat, unittest]
import jacket
suite "test version":
test "getVersion":
var major, minor, micro, proto: cint
getVersion(major.addr, minor.addr, micro.addr, proto.addr)
#echo fmt"{major}.{minor}.{micro} proto {proto}"
check:
# yes, the function simply returns 0 for all vars :-D
major == 0
minor == 0
micro == 0
proto == 0
test "getVersionString":
let version = getVersionString()
#echo $version
check:
$typeof(version) == "cstring"
match($version, re(r"\d+\.\d+\.\d+"))