Compare commits

..

2 Commits

Author SHA1 Message Date
Christopher Arndt 7ff56ac688 refactor: remove superfluous semicolons from end of comments
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
2023-05-22 07:10:40 +02:00
Christopher Arndt 6e2dab78fe feat: wrap MIDI API and add example MIDI client
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
2023-05-22 07:09:35 +02:00
4 changed files with 59 additions and 240 deletions

View File

@ -7,10 +7,10 @@ A [Nim] wrapper for the [JACK] [C API]
This software is in *alpha status* and has no official release yet. This software is in *alpha status* and has no official release yet.
The basic JACK APIs (client lifecycle, ports, callbacks, MIDI, transport) have The basic JACK APIs (client lifecycle, ports, callbacks) have been wrapped and
been wrapped and are functional (see [examples]), but latency, threading and are functional (see [examples]), but latency, transport and meta-data APIs still
meta-data APIs still need wrapping. Also, symbol names may still be changed need wrapping. Also, symbol names may still be changed and things moved around
and things moved around before the first public release. before the first public release.
Also, I plan to add a higher-level abstraction on top of the direct mapping 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 from Nim procs and types to C functions and types, probably in the form of

View File

@ -74,7 +74,7 @@ proc main() =
jclient.onShutdown(shutdownCb, nil) jclient.onShutdown(shutdownCb, nil)
# Create output port # Create output port
midiPort = jclient.portRegister("midi_in", JACK_DEFAULT_MIDI_TYPE, PortIsInput.ord, 0) midiPort = jclient.portRegister("out_1", JACK_DEFAULT_MIDI_TYPE, PortIsInput.ord, 0)
# Activate JACK client ... # Activate JACK client ...
if jclient.activate() == 0: if jclient.activate() == 0:

View File

@ -1,54 +0,0 @@
import std/[logging, strformat]
import jacket
var
jclient: ClientP
status: cint
pos: Position
transportState: TransportState
var log = newConsoleLogger(when defined(release): lvlInfo else: lvlDebug)
proc errorCb(msg: cstring) {.cdecl.} =
# Suppress verbose JACK error messages when server is not available by
# default. Pass ``lvlAll`` when creating the logger to enable them.
debug "JACK error: " & $msg
addHandler(log)
setErrorFunction(errorCb)
jclient = clientOpen("jacket_info", NullOption.ord, status.addr)
debug "JACK server status: " & $status
if jclient == nil:
error getJackStatusErrorString(status)
quit 1
transportState = transportQuery(jclient, pos.addr)
echo fmt"usecs: {pos.usecs}"
echo fmt"frameRate: {pos.frameRate}"
echo fmt"frame: {pos.frame}"
echo fmt"valid: {pos.valid.ord}"
if bool(pos.valid.ord and PositionBBT.ord):
echo fmt"bar: {pos.bar}"
echo fmt"beat: {pos.beat}"
echo fmt"tick: {pos.tick}"
echo fmt"barStartTick: {pos.barStartTick}"
echo fmt"beatsPerBar: {pos.beatsPerBar}"
echo fmt"beatType: {pos.beatType}"
echo fmt"beatsPerMinute: {pos.beatsPerMinute}"
case transportState
of TransportStopped:
echo "JACK transport stopped, starting it now."
transportStart(jclient)
of TransportRolling:
echo "JACK transport rolling, stopping it now."
transportStop(jclient)
of TransportStarting:
echo "JACK transport starting, nothing to do."
else:
echo "Unknown JACK transport state."
discard jclient.clientClose

View File

@ -24,15 +24,14 @@ const
JACK_DEFAULT_AUDIO_TYPE* = "32 bit float mono audio" JACK_DEFAULT_AUDIO_TYPE* = "32 bit float mono audio"
JACK_DEFAULT_MIDI_TYPE* = "8 bit raw midi" JACK_DEFAULT_MIDI_TYPE* = "8 bit raw midi"
# ----------------------------- Custom Types ------------------------------ # ----------------------------- Custom Types ------------------------------
type type
Time* = uint64 Time* = culonglong
NFrames* = uint32 NFrames* = culong
Uuid* = uint64 Uuid* = culonglong
PortId* = uint32 PortId* = culong
PortTypeId* = uint32 PortTypeId* = culong
DefaultAudioSample* = cfloat DefaultAudioSample* = cfloat
type type
@ -43,7 +42,8 @@ type
type type
MidiData* = uint8 MidiData* = uint8
MidiEvent* = object
MidiEvent* {.importc: "jack_midi_event_t", header: "<jack/midiport.h>".} = object
time*: NFrames time*: NFrames
size*: csize_t size*: csize_t
buffer*: ptr UncheckedArray[MidiData] buffer*: ptr UncheckedArray[MidiData]
@ -78,10 +78,10 @@ type
type type
JackPortFlags* {.size: sizeof(culong) pure.} = enum JackPortFlags* {.size: sizeof(culong) pure.} = enum
PortIsInput = 0x01, PortIsInput = 0x1,
PortIsOutput = 0x02, PortIsOutput = 0x2,
PortIsPhysical = 0x04, PortIsPhysical = 0x4,
PortCanMonitor = 0x08, PortCanMonitor = 0x8,
PortIsTerminal = 0x10 PortIsTerminal = 0x10
type type
@ -89,86 +89,7 @@ type
CaptureLatency, CaptureLatency,
PlaybackLatency PlaybackLatency
# Transport
type
PositionBits* {.size: sizeof(cint).} = enum
PositionBBT = 0x10,
PositionTimecode = 0x20,
BBTFrameOffset = 0x40,
AudioVideoRatio = 0x80,
VideoFrameOffset = 0x100,
TickDouble = 0x200
TransportState* {.size: sizeof(cint).} = enum
TransportStopped = 0,
TransportRolling = 1,
TransportLooping = 2,
TransportStarting = 3,
TransportNetStarting = 4
type
Position* = object
unique1*: uint64
usecs*: Time
frameRate*: NFrames
frame*: NFrames
valid*: PositionBits
bar*: int32
beat*: int32
tick*: int32
barStartTick*: cdouble
beatsPerBar*: cfloat
beatType*: cfloat
ticksPerBeat*: cdouble
beatsPerMinute*: cdouble
frameTime*: cdouble
nextTime*: cdouble
bbtOffset*: NFrames
audioFramesPerVideoFrame*: cfloat
videoOffset*: NFrames
tickDouble*: cdouble
padding*: array[5, int32]
unique2*: uint64
PositionP* = ptr Position
#[ DEPRECATED
typedef enum {
JackTransportState = 0x1,
JackTransportPosition = 0x2,
JackTransportLoop = 0x4,
JackTransportSMPTE = 0x8,
JackTransportBBT = 0x10
} jack_transport_bits_t;
typedef struct {
jack_nframes_t frame_rate;
jack_time_t usecs;
jack_transport_bits_t valid;
jack_transport_state_t transport_state;
jack_nframes_t frame;
jack_nframes_t loop_start;
jack_nframes_t loop_end;
long smpte_offset
float smpte_frame_rate;
int bar;
int beat;
int tick;
double bar_start_tick;
float beats_per_bar;
float beat_type;
double ticks_per_beat;
double beats_per_minute;
} jack_transport_info_t;
]#
const
JACK_POSITION_MASK* = (PositionBBT.ord or PositionTimecode.ord)
EXTENDED_TIME_INFO* = true
JACK_TICK_DOUBLE* = true
# Callback function types # Callback function types
type type
JackProcessCallback* = proc (nframes: NFrames; arg: pointer): cint {.cdecl.} JackProcessCallback* = proc (nframes: NFrames; arg: pointer): cint {.cdecl.}
JackThreadCallback* = proc (arg: pointer): pointer {.cdecl.} JackThreadCallback* = proc (arg: pointer): pointer {.cdecl.}
@ -188,10 +109,6 @@ type
JackInfoCallback* = proc (msg: cstring) {.cdecl.} JackInfoCallback* = proc (msg: cstring) {.cdecl.}
JackErrorCallback* = proc (msg: cstring) {.cdecl.} JackErrorCallback* = proc (msg: cstring) {.cdecl.}
JackSyncCallback* = proc (state: TransportState; pos: ptr Position; arg: pointer): cint {.cdecl.}
JackTimebaseCallback* = proc (state: TransportState; nframes: NFrames; pos: ptr Position; newPos: cint;
arg: pointer) {.cdecl.}
# ----------------------------- Version info ------------------------------ # ----------------------------- Version info ------------------------------
@ -216,9 +133,8 @@ proc free*(`ptr`: pointer) {.importc: "jack_free".}
proc clientOpen*(clientName: cstring; options: cint; status: ptr cint): ClientP {. proc clientOpen*(clientName: cstring; options: cint; status: ptr cint): ClientP {.
varargs, importc: "jack_client_open".} varargs, importc: "jack_client_open".}
#[ DEPRECATED # DEPRECATED
jack_client_t * jack_client_new (const char *client_name) # proc clientNew*(clientName: cstring): ClientP {.importc: "jack_client_new".}
]#
# int jack_client_close (jack_client_t *client) # int jack_client_close (jack_client_t *client)
proc clientClose*(client: ClientP): cint {.importc: "jack_client_close"} proc clientClose*(client: ClientP): cint {.importc: "jack_client_close"}
@ -229,11 +145,13 @@ proc clientNameSize*(): cint {.importc: "jack_client_name_size"}
# char * jack_get_client_name (jack_client_t *client) # char * jack_get_client_name (jack_client_t *client)
proc getClientName*(client: ClientP): cstring {.importc: "jack_get_client_name".} proc getClientName*(client: ClientP): cstring {.importc: "jack_get_client_name".}
# char *jack_get_uuid_for_client_name (jack_client_t *client, const char *client_name) # char *jack_get_uuid_for_client_name (jack_client_t *client,
# const char *client_name)
proc getUuidForClientName*(client: ClientP; clientName: cstring): cstring {. proc getUuidForClientName*(client: ClientP; clientName: cstring): cstring {.
importc: "jack_get_uuid_for_client_name".} importc: "jack_get_uuid_for_client_name".}
# char *jack_get_client_name_by_uuid (jack_client_t *client, const char *client_uuid) # char *jack_get_client_name_by_uuid (jack_client_t *client,
# const char *client_uuid )
proc getClientNameByUuid*(client: ClientP; clientUuid: cstring): cstring {. proc getClientNameByUuid*(client: ClientP; clientUuid: cstring): cstring {.
importc: "jack_get_client_name_by_uuid".} importc: "jack_get_client_name_by_uuid".}
@ -255,14 +173,15 @@ proc getClientPid*(name: cstring): cint {.importc: "jack_get_client_pid".}
# FIXME: not implemented yet # FIXME: not implemented yet
# jack_native_thread_t jack_client_thread_id (jack_client_t *client) # jack_native_thread_t jack_client_thread_id (jack_client_t *client)
# proc clientThreadId*(client: ClientP): NativeThread {.importc: "jack_client_thread_id".} # proc clientThreadId*(client: ClientP): NativeThread {.
# importc: "jack_client_thread_id".}
# int jack_is_realtime (jack_client_t *client) # int jack_is_realtime (jack_client_t *client)
proc isRealtime*(client: ClientP): cint {.importc: "jack_is_realtime".} proc isRealtime*(client: ClientP): cint {.importc: "jack_is_realtime".}
#[ DEPRECATED # DEPRECATED
jack_nframes_t jack_thread_wait (jack_client_t *client, int status) # proc threadWait*(client: ClientP; status: cint): NFrames {.
]# # importc: "jack_thread_wait".}
# jack_nframes_t jack_cycle_wait (jack_client_t* client) # jack_nframes_t jack_cycle_wait (jack_client_t* client)
proc cycleWait*(client: ClientP): NFrames {.importc: "jack_cycle_wait".} proc cycleWait*(client: ClientP): NFrames {.importc: "jack_cycle_wait".}
@ -335,9 +254,9 @@ proc getSampleRate*(client: ClientP): NFrames {.importc: "jack_get_sample_rate".
# jack_nframes_t jack_get_buffer_size (jack_client_t *) # jack_nframes_t jack_get_buffer_size (jack_client_t *)
proc getBufferSize*(client: ClientP): NFrames {.importc: "jack_get_buffer_size".} proc getBufferSize*(client: ClientP): NFrames {.importc: "jack_get_buffer_size".}
#[ DEPRECATED # DEPRECATED
int jack_engine_takeover_timebase (jack_client_t *) # proc engineTakeoverTimebase*(a1: ClientP): cint {.
]# # importc: "jack_engine_takeover_timebase".}
# float jack_cpu_load (jack_client_t *client) # float jack_cpu_load (jack_client_t *client)
proc cpuLoad*(client: ClientP): cfloat {.importc: "jack_cpu_load".} proc cpuLoad*(client: ClientP): cfloat {.importc: "jack_cpu_load".}
@ -390,22 +309,24 @@ proc portConnectedTo*(port: PortP; portName: cstring): cint {.importc: "jack_por
# const char ** jack_port_get_connections (const jack_port_t *port) # const char ** jack_port_get_connections (const jack_port_t *port)
# #
# CAVEAT: The caller is responsible for calling jack_free() on any non-NULL returned value. # CAVEAT: The caller is responsible for calling jack_free() on any non-NULL
# returned value.
proc portGetConnections*(port: PortP): cstringArray {.importc: "jack_port_get_connections".} proc portGetConnections*(port: PortP): cstringArray {.importc: "jack_port_get_connections".}
# const char ** jack_port_get_all_connections (const jack_client_t *client, # const char ** jack_port_get_all_connections (const jack_client_t *client,
# const jack_port_t *port) # const jack_port_t *port)
# #
# CAVEAT: The caller is responsible for calling jack_free() on any non-NULL returned value. # CAVEAT: The caller is responsible for calling jack_free() on any non-NULL
# returned value.
proc portGetAllConnections*(client: ClientP; port: PortP): cstringArray {. proc portGetAllConnections*(client: ClientP; port: PortP): cstringArray {.
importc: "jack_port_get_all_connections".} importc: "jack_port_get_all_connections".}
#[ DEPRECATED #[ DEPRECATED
int jack_port_tie (jack_port_t *src, jack_port_t *dst) proc portTie*(src: PortP; dst: PortP): cint {.importc: "jack_port_tie".}
int jack_port_untie (jack_port_t *port) proc portUntie*(port: PortP): cint {.importc: "jack_port_untie".}
int jack_port_set_name (jack_port_t *port, const char *port_name) proc portSetName*(port: PortP; portName: cstring): cint {.importc: "jack_port_set_name".}
]# ]#
# int jack_port_rename (jack_client_t* client, jack_port_t *port, const char *port_name) # int jack_port_rename (jack_client_t* client, jack_port_t *port, const char *port_name)
@ -435,25 +356,6 @@ proc portEnsureMonitor*(port: PortP; onoff: cint): cint {.
# int jack_port_monitoring_input (jack_port_t *port) # int jack_port_monitoring_input (jack_port_t *port)
proc portMonitoringInput*(port: PortP): cint {.importc: "jack_port_monitoring_input".} proc portMonitoringInput*(port: PortP): cint {.importc: "jack_port_monitoring_input".}
# ------------------------------ Port Lookup ------------------------------
# const char ** jack_get_ports (jack_client_t *client,
# const char *port_name_pattern,
# const char *type_name_pattern,
# unsigned long flags)
#
# CAVEAT: The caller is responsible for calling jack_free() on any non-NULL returned value.
proc getPorts*(client: ClientP; portNamePattern: cstring;
typeNamePattern: cstring; flags: culong): cstringArray {.importc: "jack_get_ports".}
# jack_port_t * jack_port_by_name (jack_client_t *client, const char *port_name)
proc portByName*(client: ClientP; portName: cstring): PortP {.importc: "jack_port_by_name".}
# jack_port_t * jack_port_by_id (jack_client_t *client, jack_port_id_t port_id)
proc portById*(client: ClientP; portId: PortId): PortP {.importc: "jack_port_by_id".}
# ------------------------------ Connections ------------------------------ # ------------------------------ Connections ------------------------------
# int jack_connect (jack_client_t *client, # int jack_connect (jack_client_t *client,
@ -479,7 +381,6 @@ proc portTypeSize*(): cint {.importc: "jack_port_type_size".}
proc portTypeGetBufferSize*(client: ClientP; portType: cstring): csize_t {. proc portTypeGetBufferSize*(client: ClientP; portType: cstring): csize_t {.
importc: "jack_port_type_get_buffer_size".} importc: "jack_port_type_get_buffer_size".}
# --------------------------------- MIDI ---------------------------------- # --------------------------------- MIDI ----------------------------------
# jack_nframes_t jack_midi_get_event_count (void *port_buffer) # jack_nframes_t jack_midi_get_event_count (void *port_buffer)
@ -506,7 +407,6 @@ proc midiEventWrite*(portBuffer: pointer, time: NFrames, data: ptr MidiData, dat
# uint32_t jack_midi_get_lost_event_count (void *port_buffer) # uint32_t jack_midi_get_lost_event_count (void *port_buffer)
proc midiGetLostEventCount*(portBuffer: pointer): uint32 {.importc: "jack_midi_get_lost_event_count".} proc midiGetLostEventCount*(portBuffer: pointer): uint32 {.importc: "jack_midi_get_lost_event_count".}
# -------------------------------- Latency -------------------------------- # -------------------------------- Latency --------------------------------
#[ FIXME: not implemented yet #[ FIXME: not implemented yet
@ -529,6 +429,23 @@ proc portGetTotalLatency*(client: ClientP; port: PortP): NFrames {.importc: "jac
proc recomputeTotalLatency*(a1: ClientP; port: PortP): cint {.importc: "jack_recompute_total_latency".} proc recomputeTotalLatency*(a1: ClientP; port: PortP): cint {.importc: "jack_recompute_total_latency".}
]# ]#
# ------------------------------ Port Lookup ------------------------------
# const char ** jack_get_ports (jack_client_t *client,
# const char *port_name_pattern,
# const char *type_name_pattern,
# unsigned long flags)
#
# CAVEAT: The caller is responsible for calling jack_free() on any non-NULL
# returned value.
proc getPorts*(client: ClientP; portNamePattern: cstring;
typeNamePattern: cstring; flags: culong): cstringArray {.importc: "jack_get_ports".}
# jack_port_t * jack_port_by_name (jack_client_t *client, const char *port_name)
proc portByName*(client: ClientP; portName: cstring): PortP {.importc: "jack_port_by_name".}
# jack_port_t * jack_port_by_id (jack_client_t *client, jack_port_id_t port_id)
proc portById*(client: ClientP; portId: PortId): PortP {.importc: "jack_port_by_id".}
# ----------------------------- Time handling ----------------------------- # ----------------------------- Time handling -----------------------------
@ -542,10 +459,10 @@ proc frameTime*(client: ClientP): NFrames {.importc: "jack_frame_time".}
proc lastFrameTime*(client: ClientP): NFrames {.importc: "jack_last_frame_time".} proc lastFrameTime*(client: ClientP): NFrames {.importc: "jack_last_frame_time".}
# int jack_get_cycle_times(const jack_client_t *client, # int jack_get_cycle_times(const jack_client_t *client,
# jack_nframes_t *current_frames, # jack_nframes_t *current_frames,
# jack_time_t *current_usecs, # jack_time_t *current_usecs,
# jack_time_t *next_usecs, # jack_time_t *next_usecs,
# float *period_usecs) # float *period_usecs)
proc getCycleTimes*(client: ClientP; currentFrames: ptr NFrames; proc getCycleTimes*(client: ClientP; currentFrames: ptr NFrames;
currentUsecs: ptr Time; nextUsecs: ptr Time; currentUsecs: ptr Time; nextUsecs: ptr Time;
periodUsecs: ptr cfloat): cint {.importc: "jack_get_cycle_times".} periodUsecs: ptr cfloat): cint {.importc: "jack_get_cycle_times".}
@ -559,50 +476,6 @@ proc timeToFrames*(client: ClientP; time: Time): NFrames {.importc: "jack_time_t
# jack_time_t jack_get_time(void) # jack_time_t jack_get_time(void)
proc getTime*(): Time {.importc: "jack_get_time".} proc getTime*(): Time {.importc: "jack_get_time".}
# ------------------------------- Transport -------------------------------
# int jack_release_timebase (jack_client_t *client)
proc releaseTimebase*(client: ClientP): cint {.importc: "jack_release_timebase".}
# int jack_set_sync_callback (jack_client_t *client, JackSyncCallback sync_callback, void *arg)
proc setSyncCallback*(client: ClientP; syncCallback: JackSyncCallback; arg: pointer): cint {.
importc: "jack_set_sync_callback".}
# int jack_set_sync_timeout (jack_client_t *client, jack_time_t timeout)
proc setSyncTimeout*(client: ClientP; timeout: Time): cint {.importc: "jack_set_sync_timeout".}
# int jack_set_timebase_callback (jack_client_t *client,
# int conditional,
# JackTimebaseCallback timebase_callback,
# void *arg)
proc setTimebaseCallback*(client: ClientP; conditional: cint; timebaseCallback: JackTimebaseCallback;
arg: pointer): cint {.
importc: "jack_set_timebase_callback".}
# int jack_transport_locate (jack_client_t *client, jack_nframes_t frame)
proc transportLocate*(client: ClientP; frame: NFrames): cint {.importc: "jack_transport_locate".}
# jack_transport_state_t jack_transport_query (const jack_client_t *client, jack_position_t *pos)
proc transportQuery*(client: ClientP; pos: PositionP): TransportState {.importc: "jack_transport_query".}
# jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client)
proc getCurrentTransportFrame*(client: ClientP): NFrames {.importc: "jack_get_current_transport_frame".}
# int jack_transport_reposition (jack_client_t *client, const jack_position_t *pos)
proc transportReposition*(client: ClientP; pos: PositionP): cint {.importc: "jack_transport_reposition".}
# void jack_transport_start (jack_client_t *client)
proc transportStart*(client: ClientP) {.importc: "jack_transport_start".}
# void jack_transport_stop (jack_client_t *client)
proc transportStop*(client: ClientP) {.importc: "jack_transport_stop".}
#[ DEPRECATED
void jack_get_transport_info (jack_client_t *client, jack_transport_info_t *tinfo)
void jack_set_transport_info (jack_client_t *client, jack_transport_info_t *tinfo)
]#
# ---------------------------- Error handling ----------------------------- # ---------------------------- Error handling -----------------------------
proc setErrorFunction*(errorCallback: JackErrorCallback) {.importc: "jack_set_error_function".} proc setErrorFunction*(errorCallback: JackErrorCallback) {.importc: "jack_set_error_function".}