Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions examples/chatty_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,11 @@ def freewheel(starting):
@client.set_blocksize_callback
def blocksize(blocksize):
print("setting blocksize to", blocksize)
return jack.SUCCESS


@client.set_samplerate_callback
def samplerate(samplerate):
print("setting samplerate to", samplerate)
return jack.SUCCESS


@client.set_client_registration_callback
Expand All @@ -81,21 +79,18 @@ def port_connect(a, b, connect):
@client.set_port_rename_callback
def port_rename(port, old, new):
print("renamed", port, "from", repr(old), "to", repr(new))
return jack.SUCCESS
except AttributeError:
print("Could not register port rename callback (not available on JACK1).")


@client.set_graph_order_callback
def graph_order():
print("graph order changed")
return jack.SUCCESS


@client.set_xrun_callback
def xrun():
print("xrun; delay", client.xrun_delayed_usecs, "microseconds")
return jack.SUCCESS


print("activating JACK")
Expand Down
1 change: 0 additions & 1 deletion examples/midi_chords.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ def process(frames):
for i in INTERVALS:
# Note: This may raise an exception:
outport.write_midi_event(offset, (status, pitch + i, vel))
return jack.CALL_AGAIN

with client:
print("#" * 80)
Expand Down
1 change: 0 additions & 1 deletion examples/midi_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ def process(frames):
for offset, data in port.incoming_midi_events():
print("{0}: 0x{1}".format(client.last_frame_time + offset,
binascii.hexlify(data).decode()))
return jack.CALL_AGAIN

with client:
print("#" * 80)
Expand Down
2 changes: 0 additions & 2 deletions examples/midi_sine.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,13 @@ def process(frames):
dead = [k for k, v in voices.items() if v.weight <= 0]
for pitch in dead:
del voices[pitch]
return jack.CALL_AGAIN


@client.set_samplerate_callback
def samplerate(samplerate):
global fs
fs = samplerate
voices.clear()
return jack.SUCCESS


@client.set_shutdown_callback
Expand Down
1 change: 0 additions & 1 deletion examples/thru_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ def process(frames):
assert frames == client.blocksize
for i, o in zip(client.inports, client.outports):
o.get_buffer()[:] = i.get_buffer()
return jack.CALL_AGAIN


@client.set_shutdown_callback
Expand Down
141 changes: 87 additions & 54 deletions jack.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,14 +316,8 @@
NETSTARTING = _lib.JackTransportNetStarting
"""Waiting for sync ready on the network."""

CALL_AGAIN = 0
"""Possible return value for process callback."""
STOP_CALLING = 1
"""Possible return value for process callback."""
SUCCESS = 0
"""Possible return value for several callbacks."""
FAILURE = 1
"""Possible return value for several callbacks."""
_SUCCESS = 0
_FAILURE = 1


class Client(object):
Expand Down Expand Up @@ -835,7 +829,7 @@ def set_shutdown_callback(self, callback):
"""
@self._callback("JackInfoShutdownCallback")
def callback_wrapper(code, reason, _):
return callback(Status(code), _ffi.string(reason).decode())
callback(Status(code), _ffi.string(reason).decode())

_lib.jack_on_info_shutdown(self._ptr, callback_wrapper, _ffi.NULL)

Expand Down Expand Up @@ -877,22 +871,28 @@ def set_process_callback(self, callback):
User-supplied function that is called by the engine anytime
there is work to be done. It must have this signature::

callback(frames:int) -> int
callback(frames:int) -> None

The argument `frames` specifies the number of frames that
have to be processed in the current audio block. It will be
the same number as :attr:`blocksize` and it will be a power
of two.
The `callback` must return zero on success (if `callback`
shall be called again for the next audio block) and non-zero
on error (if `callback` shall not be called again).
You can use the module constants :data:`CALL_AGAIN` and
:data:`STOP_CALLING`, respectively.

As long as the client is active, the `callback` will be
called once in each process cycle. However, if an exception
is raised inside of a `callback`, it will not be called
anymore. The exception :class:`CallbackExit` can be used to
silently prevent further callback invocations, all other
exceptions will print an error message to *stderr*.

"""
@self._callback("JackProcessCallback", error=STOP_CALLING)
@self._callback("JackProcessCallback", error=_FAILURE)
def callback_wrapper(frames, _):
return callback(frames)
try:
callback(frames)
except CallbackExit:
return _FAILURE
return _SUCCESS

_check(_lib.jack_set_process_callback(
self._ptr, callback_wrapper, _ffi.NULL),
Expand Down Expand Up @@ -931,7 +931,7 @@ def set_freewheel_callback(self, callback):
"""
@self._callback("JackFreewheelCallback")
def callback_wrapper(starting, _):
return callback(bool(starting))
callback(bool(starting))

_check(_lib.jack_set_freewheel_callback(
self._ptr, callback_wrapper, _ffi.NULL),
Expand All @@ -958,12 +958,11 @@ def set_blocksize_callback(self, callback):
User-supplied function that is invoked whenever the JACK
engine buffer size changes. It must have this signature::

callback(blocksize:int) -> int
callback(blocksize:int) -> None

The argument `blocksize` is the new buffer size.
The `callback` must return zero on success and non-zero on
error. You can use the module constants :data:`jack.SUCCESS`
and :data:`jack.FAILURE`, respectively.
The `callback` is supposed to raise :class:`CallbackExit` on
error.

.. note:: Although this function is called in the JACK
process thread, the normal process cycle is suspended
Expand All @@ -977,9 +976,13 @@ def set_blocksize_callback(self, callback):
:attr:`blocksize`

"""
@self._callback("JackBufferSizeCallback", error=FAILURE)
@self._callback("JackBufferSizeCallback", error=_FAILURE)
def callback_wrapper(blocksize, _):
return callback(blocksize)
try:
callback(blocksize)
except CallbackExit:
return _FAILURE
return _SUCCESS

_check(_lib.jack_set_buffer_size_callback(
self._ptr, callback_wrapper, _ffi.NULL),
Expand All @@ -1004,21 +1007,24 @@ def set_samplerate_callback(self, callback):
User-supplied function that is called when the engine sample
rate changes. It must have this signature::

callback(samplerate:int) -> int
callback(samplerate:int) -> None

The argument `samplerate` is the new engine sample rate.
The `callback` must return zero on success and non-zero on
error. You can use the module constants :data:`jack.SUCCESS`
and :data:`jack.FAILURE`, respectively.
The `callback` is supposed to raise :class:`CallbackExit` on
error.

See Also
--------
:attr:`samplerate`

"""
@self._callback("JackSampleRateCallback", error=FAILURE)
@self._callback("JackSampleRateCallback", error=_FAILURE)
def callback_wrapper(samplerate, _):
return callback(samplerate)
try:
callback(samplerate)
except CallbackExit:
return _FAILURE
return _SUCCESS

_check(_lib.jack_set_sample_rate_callback(
self._ptr, callback_wrapper, _ffi.NULL),
Expand Down Expand Up @@ -1052,7 +1058,7 @@ def set_client_registration_callback(self, callback):
"""
@self._callback("JackClientRegistrationCallback")
def callback_wrapper(name, register, _):
return callback(_ffi.string(name).decode(), bool(register))
callback(_ffi.string(name).decode(), bool(register))

_check(_lib.jack_set_client_registration_callback(
self._ptr, callback_wrapper, _ffi.NULL),
Expand Down Expand Up @@ -1093,7 +1099,7 @@ def set_port_registration_callback(self, callback):
@self._callback("JackPortRegistrationCallback")
def callback_wrapper(port, register, _):
port = self._wrap_port_ptr(_lib.jack_port_by_id(self._ptr, port))
return callback(port, bool(register))
callback(port, bool(register))

_check(_lib.jack_set_port_registration_callback(
self._ptr, callback_wrapper, _ffi.NULL),
Expand Down Expand Up @@ -1135,7 +1141,7 @@ def set_port_connect_callback(self, callback):
def callback_wrapper(a, b, connect, _):
a = self._wrap_port_ptr(_lib.jack_port_by_id(self._ptr, a))
b = self._wrap_port_ptr(_lib.jack_port_by_id(self._ptr, b))
return callback(a, b, bool(connect))
callback(a, b, bool(connect))

_check(_lib.jack_set_port_connect_callback(
self._ptr, callback_wrapper, _ffi.NULL),
Expand All @@ -1160,15 +1166,14 @@ def set_port_rename_callback(self, callback):
User-supplied function that is called whenever the port name
has been changed. It must have this signature::

callback(port:Port, old:str, new:str) -> int
callback(port:Port, old:str, new:str) -> None

The first argument is the port that has been renamed (a
:class:`Port`, :class:`MidiPort`, :class:`OwnPort` or
:class:`OwnMidiPort` object); the second and third argument
is the old and new name, respectively.
The `callback` must return zero on success and non-zero on
error. You can use the module constants :data:`jack.SUCCESS`
and :data:`jack.FAILURE`, respectively.
The `callback` is supposed to raise :class:`CallbackExit` on
error.

See Also
--------
Expand All @@ -1184,11 +1189,15 @@ def set_port_rename_callback(self, callback):
94c819accfab2612050e875c24cf325daa0fd26d

"""
@self._callback("JackPortRenameCallback", error=FAILURE)
@self._callback("JackPortRenameCallback", error=_FAILURE)
def callback_wrapper(port, old_name, new_name, _):
port = self._wrap_port_ptr(_lib.jack_port_by_id(self._ptr, port))
return callback(port, _ffi.string(old_name).decode(),
_ffi.string(new_name).decode())
try:
callback(port, _ffi.string(old_name).decode(),
_ffi.string(new_name).decode())
except CallbackExit:
return _FAILURE
return _SUCCESS

_check(_lib.jack_set_port_rename_callback(
self._ptr, callback_wrapper, _ffi.NULL),
Expand All @@ -1214,16 +1223,19 @@ def set_graph_order_callback(self, callback):
processing graph is reordered.
It must have this signature::

callback() -> int
callback() -> None

The `callback` must return zero on success and non-zero on
error. You can use the module constants :data:`jack.SUCCESS`
and :data:`jack.FAILURE`, respectively.
The `callback` is supposed to raise :class:`CallbackExit` on
error.

"""
@self._callback("JackGraphOrderCallback", error=FAILURE)
@self._callback("JackGraphOrderCallback", error=_FAILURE)
def callback_wrapper(_):
return callback()
try:
callback()
except CallbackExit:
return _FAILURE
return _SUCCESS

_check(_lib.jack_set_graph_order_callback(
self._ptr, callback_wrapper, _ffi.NULL),
Expand All @@ -1248,20 +1260,23 @@ def set_xrun_callback(self, callback):
User-supplied function that is called whenever an xrun has
occured. It must have this signature::

callback() -> int
callback() -> None

The `callback` must return zero on success and non-zero on
error. You can use the module constants :data:`jack.SUCCESS`
and :data:`jack.FAILURE`, respectively.
The `callback` is supposed to raise :class:`CallbackExit` on
error.

See Also
--------
:attr:`xrun_delayed_usecs`

"""
@self._callback("JackXRunCallback", error=FAILURE)
@self._callback("JackXRunCallback", error=_FAILURE)
def callback_wrapper(_):
return callback()
try:
callback()
except CallbackExit:
return _FAILURE
return _SUCCESS

_check(_lib.jack_set_xrun_callback(
self._ptr, callback_wrapper, _ffi.NULL),
Expand Down Expand Up @@ -1338,7 +1353,7 @@ def set_timebase_callback(self, callback=None, conditional=False):

@self._callback("JackTimebaseCallback")
def callback_wrapper(state, blocksize, pos, new_pos, _):
return callback(state, blocksize, pos, bool(new_pos))
callback(state, blocksize, pos, bool(new_pos))

err = _lib.jack_set_timebase_callback(self._ptr, conditional,
callback_wrapper, _ffi.NULL)
Expand Down Expand Up @@ -2439,6 +2454,24 @@ class JackError(Exception):
pass


class CallbackExit(Exception):

"""To be raised in a callback function to signal failure.

See Also
--------
:meth:`Client.set_process_callback`
:meth:`Client.set_blocksize_callback`
:meth:`Client.set_samplerate_callback`
:meth:`Client.set_port_rename_callback`
:meth:`Client.set_graph_order_callback`
:meth:`Client.set_xrun_callback`

"""

pass


def position2dict(pos):
"""Convert CFFI position struct to a dict."""
assert pos.unique_1 == pos.unique_2
Expand Down Expand Up @@ -2543,7 +2576,7 @@ def _set_error_or_info_function(callback, setter):
else:
@_ffi.callback("void (*)(const char*)")
def callback_wrapper(msg):
return callback(_ffi.string(msg).decode())
callback(_ffi.string(msg).decode())

_keepalive[setter] = callback_wrapper
setter(callback_wrapper)
Expand Down