Skip to content

Detecting xruns in process callback #81

@dj-foxxy

Description

@dj-foxxy

Hi and thanks for the great library, been using it for years!

What's the best way to detect if the last process callback lead to an xrun? Here's my use case: I'm sending MIDI, which works 99% of the time. But when an xrun occurs, the MIDI events aren't sent to connected inputs and on the next process callback I clear the buffer and they're lost forever. Here's how I'm currently doing it:

jack_midi_event_write = jack._lib.jack_midi_event_write

jack_port_get_buffer = jack._lib.jack_port_get_buffer


class Process:
    __slots__ = ('_before', '_buffer', '_client', '_port', '_xrun')

    def __init__(self, client: jack.Client, port: jack.OwnMidiPort) -> None:
        self._before = 0;
        self._buffer = jack.RingBuffer(2 ** 8)
        self._xrun = threading.Event()
        self._client = client
        self._port = port

    def process(self, frames: int) -> None:
        xrun = self._xrun
        xrun_set = xrun.is_set()
        port = self._port
        client = port._client
        last_frame_time = client.last_frame_time
        blocksize = client.blocksize

        if not xrun_set and last_frame_time - blocksize == self._before:
            port.clear_buffer()
        if xrun_set:
            xrun.clear()

        self._before = last_frame_time
        src_buffer = self._buffer
        space = src_buffer.read_space

        if space != 0:
            data = src_buffer.read(space)
            dst_buffer = jack_port_get_buffer(port._ptr, blocksize)
            i = 0
            while i != space:
                jack_midi_event_write(dst_buffer, 0, data[i : i + 3], 3)
                i += 3

    def handle_xrun(self, delay_usec: float) -> None:
        self._xrun.set()

    def _append(self, event: Tuple[int, int, int]) -> None:
        self._buffer.write(bytes(event))

    def note_on(self, note: int) -> None:
        self._append((0x99, note, 100))

    def note_off(self, note: int) -> None:
        self._append((0x89, note, 0))

I'm doing two things:

  1. I'm comparing last_frame_time - blocksize to the last_frame_time on the previous process callback.

  2. Using the set_xrun_callback to set a threading.Event()

I'm not sure if I'm doing any of this correctly, just experimenting to see what works. I know you're not meant to use Python for realtime, but it's working too well to justify moving to C++.

Thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions