diff --git a/README.md b/README.md index 43417da..43826d3 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ A [Nim] wrapper for the [JACK] [C API] This software is in *alpha status* and has no official release yet. -The basic JACK APIs (client lifecycle, ports, callbacks) have been wrapped and -are functional (see [examples]), but latency, transport and meta-data APIs still -need wrapping. Also, symbol names may still be changed and things moved around -before the first public release. +The basic JACK APIs (client lifecycle, ports, callbacks, MIDI, transport) have +been wrapped and are functional (see [examples]), but latency, threading and +meta-data APIs still need wrapping. Also, 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 diff --git a/examples/jacket_transport_query.nim b/examples/jacket_transport_query.nim new file mode 100644 index 0000000..dad03ee --- /dev/null +++ b/examples/jacket_transport_query.nim @@ -0,0 +1,54 @@ +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 diff --git a/src/jacket.nim b/src/jacket.nim index 1b46c9d..1b9e5d6 100644 --- a/src/jacket.nim +++ b/src/jacket.nim @@ -89,7 +89,86 @@ type CaptureLatency, 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 + type JackProcessCallback* = proc (nframes: NFrames; arg: pointer): cint {.cdecl.} JackThreadCallback* = proc (arg: pointer): pointer {.cdecl.} @@ -109,6 +188,10 @@ type JackInfoCallback* = 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 ------------------------------ @@ -476,6 +559,50 @@ proc timeToFrames*(client: ClientP; time: Time): NFrames {.importc: "jack_time_t # jack_time_t jack_get_time(void) 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 ----------------------------- proc setErrorFunction*(errorCallback: JackErrorCallback) {.importc: "jack_set_error_function".}