From fa14ed5f30062259fa42976210057e8241202567 Mon Sep 17 00:00:00 2001 From: Julien Acroute Date: Thu, 21 May 2015 13:53:51 +0200 Subject: [PATCH 1/6] Add set_timebase_callback() --- jack.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jack.py b/jack.py index 51b5c7e..c6202ac 100644 --- a/jack.py +++ b/jack.py @@ -105,6 +105,7 @@ } jack_position_bits_t; /* _jack_position: see below in "packed" section */ typedef struct _jack_position jack_position_t; +typedef void (*JackTimebaseCallback)(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos, void *arg); /* deprecated: jack_transport_bits_t */ /* deprecated: jack_transport_info_t */ @@ -236,11 +237,11 @@ /* TODO: jack_release_timebase */ /* TODO: jack_set_sync_callback */ /* TODO: jack_set_sync_timeout */ -/* TODO: jack_set_timebase_callback */ +int jack_set_timebase_callback(jack_client_t* client, int conditional, JackTimebaseCallback timebase_callback, void* arg); int jack_transport_locate(jack_client_t* client, jack_nframes_t frame); jack_transport_state_t jack_transport_query(const jack_client_t* client, jack_position_t* pos); jack_nframes_t jack_get_current_transport_frame(const jack_client_t* client); -int jack_transport_reposition(jack_client_t* client, const jack_position_t* pos); +/* TODO: jack_transport_reposition */ void jack_transport_start(jack_client_t* client); void jack_transport_stop(jack_client_t* client); /* deprecated: jack_get_transport_info */ From 29a745369af23ef592f74a9f1faf3d41b7115fba Mon Sep 17 00:00:00 2001 From: Julien Acroute Date: Wed, 27 May 2015 14:12:40 +0200 Subject: [PATCH 2/6] Add doc about timebase callback and make conditional parameter to optional --- jack.py | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/jack.py b/jack.py index c6202ac..2bca20f 100644 --- a/jack.py +++ b/jack.py @@ -1248,6 +1248,104 @@ def callback_wrapper(_): self._ptr, callback_wrapper, _ffi.NULL), "Error setting xrun callback") + def set_timebase_callback(self, callback, conditional=False, + userdata=None): + """Register as timebase master for the JACK subsystem. + + The timebase master registers a callback that updates extended + position information such as beats or timecode whenever + necessary. Without this extended information, there is no need + for this function. + + There is never more than one master at a time. When a new client + takes over, the former callback is no longer called. Taking over + the timebase may be done conditionally, so it fails if there was + a master already. + + The method may be called whether the client has been activated + or not. + + Parameters + ---------- + callback : callable + Realtime function that returns extended position + information. Its output affects all of the following process + cycle. This realtime function must not wait. + + This function is called immediately after `process()` in the + same thread whenever the transport is rolling, or when any + client has requested a new position in the previous + cycle. The first cycle after `set_timebase_callback()` is + also treated as a new position, or the first cycle after + `jack_activate()` if the client had been inactive. + + The timebase master may not use its `pos` argument to set + `pos.frame`. To change position, use + `jack_transport_reposition()` or + `jack_transport_locate()`. These functions are + realtime-safe, the `timebase_callback` can call them + directly. + + It must have this signature:: + + callback(state:int, + frames:int, + pos:CFFI_struct, + new_pos:bool, arg) -> None + + * state : int + current transport state. + * frames : int + number of frames in current period. + * pos : Position CFFI structure + position structure for the next cycle; `pos.frame` will + be its frame number. If `new_pos` is `False`, this + structure contains extended position information from + the current cycle. If `True`, it contains whatever was + set by the requester. The `timebase_callback`'s task is + to update the extended information here. See `JACK + transport documentation`__ for the available fields. + * new_pos : bool + `True` (non-zero) for a newly requested `pos`, or for + the first cycle after the `timebase_callback` is + defined. `arg` the argument supplied by + `set_timebase_callback()`. + * arg : + the argument supplied by `set_timebase_callback()`. + conditional : bool + set to `True` for a conditional request. If set to `True` + this call will fail if there is already a timebase master + userdata : + an optional argument for the callback function. + + Returns + ------- + bool + `True` if new timebase master was set. `False` if + conditional request fail bacause another master is already + registered. + +__ http://jackaudio.org/files/docs/html/structjack__position__t.html + + """ + @self._callback("JackTimebaseCallback") + def callback_wrapper(state, frames, pos, new_pos, _): + return callback(state, frames, pos, new_pos, userdata) + + return_code = _lib.jack_set_timebase_callback(self._ptr, + conditional, + callback_wrapper, + _ffi.NULL) + + if return_code == 0: + return True + elif conditional and return_code == -1: + return False + else: + raise JackError("{0} ({1})" + .format("Error setting timebase callback", + return_code)) + def get_uuid_for_client_name(self, name): """Get the session ID for a client name. From b4722b020808f21a4100bce762a2c52c14ad6291 Mon Sep 17 00:00:00 2001 From: Julien Acroute Date: Wed, 27 May 2015 14:33:51 +0200 Subject: [PATCH 3/6] remove userdata argument --- jack.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/jack.py b/jack.py index 2bca20f..63c318d 100644 --- a/jack.py +++ b/jack.py @@ -1248,8 +1248,7 @@ def callback_wrapper(_): self._ptr, callback_wrapper, _ffi.NULL), "Error setting xrun callback") - def set_timebase_callback(self, callback, conditional=False, - userdata=None): + def set_timebase_callback(self, callback, conditional=False): """Register as timebase master for the JACK subsystem. The timebase master registers a callback that updates extended @@ -1291,7 +1290,7 @@ def set_timebase_callback(self, callback, conditional=False, callback(state:int, frames:int, pos:CFFI_struct, - new_pos:bool, arg) -> None + new_pos:bool) -> None * state : int current transport state. @@ -1308,15 +1307,10 @@ def set_timebase_callback(self, callback, conditional=False, * new_pos : bool `True` (non-zero) for a newly requested `pos`, or for the first cycle after the `timebase_callback` is - defined. `arg` the argument supplied by - `set_timebase_callback()`. - * arg : - the argument supplied by `set_timebase_callback()`. + defined. conditional : bool set to `True` for a conditional request. If set to `True` this call will fail if there is already a timebase master - userdata : - an optional argument for the callback function. Returns ------- @@ -1330,7 +1324,7 @@ def set_timebase_callback(self, callback, conditional=False, """ @self._callback("JackTimebaseCallback") def callback_wrapper(state, frames, pos, new_pos, _): - return callback(state, frames, pos, new_pos, userdata) + return callback(state, frames, pos, new_pos) return_code = _lib.jack_set_timebase_callback(self._ptr, conditional, From c71f872d795c1de98b193161b2140f2f46cfe837 Mon Sep 17 00:00:00 2001 From: Julien Acroute Date: Mon, 1 Jun 2015 12:31:52 +0200 Subject: [PATCH 4/6] Enable setting callback by decoration, fix bug in doc and simplify test of return values --- jack.py | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/jack.py b/jack.py index 63c318d..1d0ed72 100644 --- a/jack.py +++ b/jack.py @@ -1248,7 +1248,7 @@ def callback_wrapper(_): self._ptr, callback_wrapper, _ffi.NULL), "Error setting xrun callback") - def set_timebase_callback(self, callback, conditional=False): + def set_timebase_callback(self, callback=None, conditional=False): """Register as timebase master for the JACK subsystem. The timebase master registers a callback that updates extended @@ -1302,8 +1302,8 @@ def set_timebase_callback(self, callback, conditional=False): structure contains extended position information from the current cycle. If `True`, it contains whatever was set by the requester. The `timebase_callback`'s task is - to update the extended information here. See `JACK - transport documentation`__ for the available fields. + to update the extended information here. See + :meth:`transport_query_struct` for the available fields. * new_pos : bool `True` (non-zero) for a newly requested `pos`, or for the first cycle after the `timebase_callback` is @@ -1316,29 +1316,26 @@ def set_timebase_callback(self, callback, conditional=False): ------- bool `True` if new timebase master was set. `False` if - conditional request fail bacause another master is already + conditional request fail because another master is already registered. - -__ http://jackaudio.org/files/docs/html/structjack__position__t.html - """ + if callback is None: + return lambda cb: self.set_timebase_callback(cb, conditional) + @self._callback("JackTimebaseCallback") def callback_wrapper(state, frames, pos, new_pos, _): - return callback(state, frames, pos, new_pos) + return callback(state, frames, pos, bool(new_pos)) - return_code = _lib.jack_set_timebase_callback(self._ptr, - conditional, - callback_wrapper, - _ffi.NULL) + err = _lib.jack_set_timebase_callback(self._ptr, + conditional, + callback_wrapper, + _ffi.NULL) - if return_code == 0: - return True - elif conditional and return_code == -1: + # Because of a bug in JACK2 (see TODO), we also check for -1: + if conditional and err in (_errno.EBUSY, -1): return False - else: - raise JackError("{0} ({1})" - .format("Error setting timebase callback", - return_code)) + _check(err, "Error setting timebase callback") + return True def get_uuid_for_client_name(self, name): """Get the session ID for a client name. From 5fc7ed7a46b1ec84ff973c84bd4ccbdf61d86e6c Mon Sep 17 00:00:00 2001 From: Julien Acroute Date: Tue, 2 Jun 2015 12:23:37 +0200 Subject: [PATCH 5/6] Add pull request reference --- jack.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jack.py b/jack.py index 1d0ed72..13dacdc 100644 --- a/jack.py +++ b/jack.py @@ -1332,6 +1332,7 @@ def callback_wrapper(state, frames, pos, new_pos, _): _ffi.NULL) # Because of a bug in JACK2 (see TODO), we also check for -1: + # See https://github.com/jackaudio/jack2/pull/123 if conditional and err in (_errno.EBUSY, -1): return False _check(err, "Error setting timebase callback") From eb191a710f24c71d45643b66b7899a90323c6857 Mon Sep 17 00:00:00 2001 From: Julien Acroute Date: Tue, 2 Jun 2015 12:27:55 +0200 Subject: [PATCH 6/6] Add version --- jack.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jack.py b/jack.py index 13dacdc..5d37f5f 100644 --- a/jack.py +++ b/jack.py @@ -1331,7 +1331,8 @@ def callback_wrapper(state, frames, pos, new_pos, _): callback_wrapper, _ffi.NULL) - # Because of a bug in JACK2 (see TODO), we also check for -1: + # Because of a bug in JACK2 version <= 1.9.10, we also check + # for -1: # See https://github.com/jackaudio/jack2/pull/123 if conditional and err in (_errno.EBUSY, -1): return False