summaryrefslogtreecommitdiffstats
path: root/pipewire-jack
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:28:17 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:28:17 +0000
commit7a46c07230b8d8108c0e8e80df4522d0ac116538 (patch)
treed483300dab478b994fe199a5d19d18d74153718a /pipewire-jack
parentInitial commit. (diff)
downloadpipewire-7a46c07230b8d8108c0e8e80df4522d0ac116538.tar.xz
pipewire-7a46c07230b8d8108c0e8e80df4522d0ac116538.zip
Adding upstream version 0.3.65.upstream/0.3.65upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pipewire-jack')
-rw-r--r--pipewire-jack/examples/video-dsp-play.c203
-rw-r--r--pipewire-jack/jack/control.h658
-rw-r--r--pipewire-jack/jack/intclient.h130
-rw-r--r--pipewire-jack/jack/jack.h1477
-rw-r--r--pipewire-jack/jack/jslist.h293
-rw-r--r--pipewire-jack/jack/metadata.h322
-rw-r--r--pipewire-jack/jack/midiport.h197
-rw-r--r--pipewire-jack/jack/net.h429
-rw-r--r--pipewire-jack/jack/ringbuffer.h243
-rw-r--r--pipewire-jack/jack/session.h302
-rw-r--r--pipewire-jack/jack/statistics.h57
-rw-r--r--pipewire-jack/jack/systemdeps.h141
-rw-r--r--pipewire-jack/jack/thread.h160
-rw-r--r--pipewire-jack/jack/transport.h247
-rw-r--r--pipewire-jack/jack/types.h740
-rw-r--r--pipewire-jack/jack/uuid.h50
-rw-r--r--pipewire-jack/jack/weakjack.h125
-rw-r--r--pipewire-jack/jack/weakmacros.h97
-rw-r--r--pipewire-jack/meson.build5
-rw-r--r--pipewire-jack/src/control.c472
-rw-r--r--pipewire-jack/src/dummy.c39
-rw-r--r--pipewire-jack/src/export.c36
-rw-r--r--pipewire-jack/src/meson.build98
-rw-r--r--pipewire-jack/src/metadata.c421
-rw-r--r--pipewire-jack/src/net.c169
-rw-r--r--pipewire-jack/src/pipewire-jack-extensions.h50
-rw-r--r--pipewire-jack/src/pipewire-jack.c6509
-rwxr-xr-xpipewire-jack/src/pw-jack.in78
-rw-r--r--pipewire-jack/src/ringbuffer.c302
-rw-r--r--pipewire-jack/src/statistics.c66
-rw-r--r--pipewire-jack/src/uuid.c111
31 files changed, 14227 insertions, 0 deletions
diff --git a/pipewire-jack/examples/video-dsp-play.c b/pipewire-jack/examples/video-dsp-play.c
new file mode 100644
index 0000000..be4c94c
--- /dev/null
+++ b/pipewire-jack/examples/video-dsp-play.c
@@ -0,0 +1,203 @@
+/* PipeWire
+ *
+ * Copyright © 2019 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <SDL2/SDL.h>
+
+#include <jack/jack.h>
+#include <pipewire-jack-extensions.h>
+
+#define MAX_BUFFERS 64
+
+#define JACK_DEFAULT_VIDEO_TYPE "32 bit float RGBA video"
+
+#define CLAMP(v,low,high) \
+({ \
+ __typeof__(v) _v = (v); \
+ __typeof__(low) _low = (low); \
+ __typeof__(high) _high = (high); \
+ (_v < _low) ? _low : (_v > _high) ? _high : _v; \
+})
+
+struct pixel {
+ float r, g, b, a;
+};
+
+struct data {
+ const char *path;
+
+ SDL_Renderer *renderer;
+ SDL_Window *window;
+ SDL_Texture *texture;
+ SDL_Texture *cursor;
+
+ jack_client_t *client;
+ const char *client_name;
+ jack_port_t *in_port;
+
+ jack_image_size_t size;
+
+ int counter;
+ SDL_Rect rect;
+ SDL_Rect cursor_rect;
+};
+
+static void handle_events(struct data *data)
+{
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_QUIT:
+ exit(0);
+ break;
+ }
+ }
+}
+
+static int
+process (jack_nframes_t nframes, void *arg)
+{
+ struct data *data = (struct data*)arg;
+ void *sdata, *ddata;
+ int sstride, dstride;
+ uint32_t i, j;
+ uint8_t *src, *dst;
+
+ sdata = jack_port_get_buffer (data->in_port, nframes);
+
+ handle_events(data);
+
+ if (SDL_LockTexture(data->texture, NULL, &ddata, &dstride) < 0) {
+ fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError());
+ goto done;
+ }
+
+ /* copy video image in texture */
+ sstride = data->size.stride;
+
+ src = sdata;
+ dst = ddata;
+
+ for (i = 0; i < data->size.height; i++) {
+ struct pixel *p = (struct pixel *) src;
+ for (j = 0; j < data->size.width; j++) {
+ dst[j * 4 + 0] = CLAMP(lrintf(p[j].r * 255.0f), 0, 255);
+ dst[j * 4 + 1] = CLAMP(lrintf(p[j].g * 255.0f), 0, 255);
+ dst[j * 4 + 2] = CLAMP(lrintf(p[j].b * 255.0f), 0, 255);
+ dst[j * 4 + 3] = CLAMP(lrintf(p[j].a * 255.0f), 0, 255);
+ }
+ src += sstride;
+ dst += dstride;
+ }
+ SDL_UnlockTexture(data->texture);
+
+ SDL_RenderClear(data->renderer);
+ SDL_RenderCopy(data->renderer, data->texture, &data->rect, NULL);
+ SDL_RenderPresent(data->renderer);
+
+ done:
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct data data = { 0, };
+ jack_options_t options = JackNullOption;
+ jack_status_t status;
+ int res;
+
+ data.client = jack_client_open ("video-dsp-play", options, &status);
+ if (data.client == NULL) {
+ fprintf (stderr, "jack_client_open() failed, "
+ "status = 0x%2.0x\n", status);
+ if (status & JackServerFailed) {
+ fprintf (stderr, "Unable to connect to JACK server\n");
+ }
+ exit (1);
+ }
+ if (status & JackServerStarted) {
+ fprintf (stderr, "JACK server started\n");
+ }
+ if (status & JackNameNotUnique) {
+ data.client_name = jack_get_client_name(data.client);
+ fprintf (stderr, "unique name `%s' assigned\n", data.client_name);
+ }
+
+ jack_set_process_callback (data.client, process, &data);
+
+ if ((res = jack_get_video_image_size(data.client, &data.size)) < 0) {
+ fprintf(stderr, "can't get video size: %d %s\n", res, strerror(-res));
+ return -1;
+ }
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ fprintf(stderr, "can't initialize SDL: %s\n", SDL_GetError());
+ return -1;
+ }
+
+ if (SDL_CreateWindowAndRenderer
+ (data.size.width, data.size.height, SDL_WINDOW_RESIZABLE, &data.window, &data.renderer)) {
+ fprintf(stderr, "can't create window: %s\n", SDL_GetError());
+ return -1;
+ }
+
+ data.texture = SDL_CreateTexture(data.renderer,
+ SDL_PIXELFORMAT_RGBA32,
+ SDL_TEXTUREACCESS_STREAMING,
+ data.size.width,
+ data.size.height);
+ data.rect.x = 0;
+ data.rect.y = 0;
+ data.rect.w = data.size.width;
+ data.rect.h = data.size.height;
+
+ data.in_port = jack_port_register (data.client, "input",
+ JACK_DEFAULT_VIDEO_TYPE,
+ JackPortIsInput, 0);
+
+ if (data.in_port == NULL) {
+ fprintf(stderr, "no more JACK ports available\n");
+ exit (1);
+ }
+
+ if (jack_activate (data.client)) {
+ fprintf (stderr, "cannot activate client");
+ exit (1);
+ }
+
+ while (1) {
+ sleep (1);
+ }
+
+ jack_client_close (data.client);
+
+ SDL_DestroyTexture(data.texture);
+ SDL_DestroyRenderer(data.renderer);
+ SDL_DestroyWindow(data.window);
+
+ return 0;
+}
diff --git a/pipewire-jack/jack/control.h b/pipewire-jack/jack/control.h
new file mode 100644
index 0000000..e466abc
--- /dev/null
+++ b/pipewire-jack/jack/control.h
@@ -0,0 +1,658 @@
+/* -*- Mode: C ; c-basic-offset: 4 -*- */
+/*
+ JACK control API
+
+ Copyright (C) 2008 Nedko Arnaudov
+ Copyright (C) 2008 GRAME
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/**
+ * @file jack/control.h
+ * @ingroup publicheader
+ * @brief JACK control API
+ *
+ */
+
+#ifndef JACKCTL_H__2EEDAD78_DF4C_4B26_83B7_4FF1A446A47E__INCLUDED
+#define JACKCTL_H__2EEDAD78_DF4C_4B26_83B7_4FF1A446A47E__INCLUDED
+
+#include <jack/types.h>
+#include <jack/jslist.h>
+#include <jack/systemdeps.h>
+#if !defined(sun) && !defined(__sun__)
+#include <stdbool.h>
+#endif
+
+/** Parameter types, intentionally similar to jack_driver_param_type_t */
+typedef enum
+{
+ JackParamInt = 1, /**< @brief value type is a signed integer */
+ JackParamUInt, /**< @brief value type is an unsigned integer */
+ JackParamChar, /**< @brief value type is a char */
+ JackParamString, /**< @brief value type is a string with max size of ::JACK_PARAM_STRING_MAX+1 chars */
+ JackParamBool, /**< @brief value type is a boolean */
+} jackctl_param_type_t;
+
+/** Driver types */
+typedef enum
+{
+ JackMaster = 1, /**< @brief master driver */
+ JackSlave /**< @brief slave driver */
+} jackctl_driver_type_t;
+
+/** @brief Max value that jackctl_param_type_t type can have */
+#define JACK_PARAM_MAX (JackParamBool + 1)
+
+/** @brief Max length of string parameter value, excluding terminating null char */
+#define JACK_PARAM_STRING_MAX 127
+
+/** @brief Type for parameter value */
+/* intentionally similar to jack_driver_param_value_t */
+union jackctl_parameter_value
+{
+ uint32_t ui; /**< @brief member used for ::JackParamUInt */
+ int32_t i; /**< @brief member used for ::JackParamInt */
+ char c; /**< @brief member used for ::JackParamChar */
+ char str[JACK_PARAM_STRING_MAX + 1]; /**< @brief member used for ::JackParamString */
+ bool b; /**< @brief member used for ::JackParamBool */
+};
+
+/** opaque type for server object */
+typedef struct jackctl_server jackctl_server_t;
+
+/** opaque type for driver object */
+typedef struct jackctl_driver jackctl_driver_t;
+
+/** opaque type for internal client object */
+typedef struct jackctl_internal jackctl_internal_t;
+
+/** opaque type for parameter object */
+typedef struct jackctl_parameter jackctl_parameter_t;
+
+/** opaque type for sigmask object */
+typedef struct jackctl_sigmask jackctl_sigmask_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if 0
+} /* Adjust editor indent */
+#endif
+
+/**
+ * @defgroup ControlAPI The API for starting and controlling a JACK server
+ * @{
+ */
+
+/**
+ * Call this function to setup process signal handling. As a general
+ * rule, it is required for proper operation for the server object.
+ *
+ * @param flags signals setup flags, use 0 for none. Currently no
+ * flags are defined
+ *
+ * @return the configurated signal set.
+ */
+jackctl_sigmask_t *
+jackctl_setup_signals(
+ unsigned int flags);
+
+/**
+ * Call this function to wait on a signal set.
+ *
+ * @param signals signals set to wait on
+ */
+void
+jackctl_wait_signals(
+ jackctl_sigmask_t * signals);
+
+/**
+ * \bold THIS FUNCTION IS DEPRECATED AND SHOULD NOT BE USED IN
+ * NEW JACK PROJECTS
+ *
+ * @deprecated Please use jackctl_server_create2().
+ */
+jackctl_server_t *
+jackctl_server_create(
+ bool (* on_device_acquire)(const char * device_name),
+ void (* on_device_release)(const char * device_name));
+
+/**
+ * Call this function to create server object.
+ *
+ * @param on_device_acquire - Optional callback to be called before device is acquired. If false is returned, device usage will fail
+ * @param on_device_release - Optional callback to be called after device is released.
+ * @param on_device_reservation_loop - Optional callback to be called when looping/idling the reservation.
+ *
+ * @return server object handle, NULL if creation of server object
+ * failed. Successfully created server object must be destroyed with
+ * paired call to ::jackctl_server_destroy
+ */
+jackctl_server_t *
+jackctl_server_create2(
+ bool (* on_device_acquire)(const char * device_name),
+ void (* on_device_release)(const char * device_name),
+ void (* on_device_reservation_loop)(void));
+
+/**
+ * Call this function to destroy server object.
+ *
+ * @param server server object handle to destroy
+ */
+void
+jackctl_server_destroy(
+ jackctl_server_t * server);
+
+/**
+ * Call this function to open JACK server
+ *
+ * @param server server object handle
+ * @param driver driver to use
+ *
+ * @return success status: true - success, false - fail
+ */
+bool
+jackctl_server_open(
+ jackctl_server_t * server,
+ jackctl_driver_t * driver);
+
+/**
+ * Call this function to start JACK server
+ *
+ * @param server server object handle
+ *
+ * @return success status: true - success, false - fail
+ */
+bool
+jackctl_server_start(
+ jackctl_server_t * server);
+
+/**
+ * Call this function to stop JACK server
+ *
+ * @param server server object handle
+ *
+ * @return success status: true - success, false - fail
+ */
+bool
+jackctl_server_stop(
+ jackctl_server_t * server);
+
+/**
+ * Call this function to close JACK server
+ *
+ * @param server server object handle
+ *
+ * @return success status: true - success, false - fail
+ */
+bool
+jackctl_server_close(
+ jackctl_server_t * server);
+
+/**
+ * Call this function to get list of available drivers. List node data
+ * pointers is a driver object handle (::jackctl_driver_t).
+ *
+ * @param server server object handle to get drivers for
+ *
+ * @return Single linked list of driver object handles. Must not be
+ * modified. Always same for same server object.
+ */
+const JSList *
+jackctl_server_get_drivers_list(
+ jackctl_server_t * server);
+
+/**
+ * Call this function to get list of server parameters. List node data
+ * pointers is a parameter object handle (::jackctl_parameter_t).
+ *
+ * @param server server object handle to get parameters for
+ *
+ * @return Single linked list of parameter object handles. Must not be
+ * modified. Always same for same server object.
+ */
+const JSList *
+jackctl_server_get_parameters(
+ jackctl_server_t * server);
+
+/**
+ * Call this function to get list of available internal clients. List node data
+ * pointers is a internal client object handle (::jackctl_internal_t).
+ *
+ * @param server server object handle to get internal clients for
+ *
+ * @return Single linked list of internal client object handles. Must not be
+ * modified. Always same for same server object.
+ */
+const JSList *
+jackctl_server_get_internals_list(
+ jackctl_server_t * server);
+
+/**
+ * Call this function to load one internal client.
+ * (can be used when the server is running)
+ *
+ * @param server server object handle
+ * @param internal internal to use
+ *
+ * @return success status: true - success, false - fail
+ */
+bool
+jackctl_server_load_internal(
+ jackctl_server_t * server,
+ jackctl_internal_t * internal);
+
+/**
+ * Call this function to unload one internal client.
+ * (can be used when the server is running)
+ *
+ * @param server server object handle
+ * @param internal internal to unload
+ *
+ * @return success status: true - success, false - fail
+ */
+bool
+jackctl_server_unload_internal(
+ jackctl_server_t * server,
+ jackctl_internal_t * internal);
+
+/**
+ * Call this function to load a session file.
+ * (can be used when the server is running)
+ *
+ * @param server server object handle
+ * @param file the session file to load, containing a list of
+ * internal clients and connections to be made.
+ *
+ * @return success status: true - success, false - fail
+ */
+bool jackctl_server_load_session_file(
+ jackctl_server_t * server_ptr,
+ const char * file);
+
+/**
+ * Call this function to add a slave in the driver slave list.
+ * (cannot be used when the server is running that is between
+ * jackctl_server_start and jackctl_server_stop)
+ *
+ * @param server server object handle
+ * @param driver driver to add in the driver slave list.
+ *
+ * @return success status: true - success, false - fail
+ */
+bool
+jackctl_server_add_slave(jackctl_server_t * server,
+ jackctl_driver_t * driver);
+
+/**
+ * Call this function to remove a slave from the driver slave list.
+ * (cannot be used when the server is running that is between
+ * jackctl_server_start and jackctl_server_stop)
+ *
+ * @param server server object handle
+ * @param driver driver to remove from the driver slave list.
+ *
+ * @return success status: true - success, false - fail
+ */
+bool
+jackctl_server_remove_slave(jackctl_server_t * server,
+ jackctl_driver_t * driver);
+
+/**
+ * Call this function to switch master driver.
+ *
+ * @param server server object handle
+ * @param driver driver to switch to
+ *
+ * @return success status: true - success, false - fail
+ */
+bool
+jackctl_server_switch_master(jackctl_server_t * server,
+ jackctl_driver_t * driver);
+
+
+/**
+ * Call this function to get name of driver.
+ *
+ * @param driver driver object handle to get name of
+ *
+ * @return driver name. Must not be modified. Always same for same
+ * driver object.
+ */
+const char *
+jackctl_driver_get_name(
+ jackctl_driver_t * driver);
+
+/**
+ * Call this function to get type of driver.
+ *
+ * @param driver driver object handle to get name of
+ *
+ * @return driver type. Must not be modified. Always same for same
+ * driver object.
+ */
+jackctl_driver_type_t
+jackctl_driver_get_type(
+ jackctl_driver_t * driver);
+
+/**
+ * Call this function to get list of driver parameters. List node data
+ * pointers is a parameter object handle (::jackctl_parameter_t).
+ *
+ * @param driver driver object handle to get parameters for
+ *
+ * @return Single linked list of parameter object handles. Must not be
+ * modified. Always same for same driver object.
+ */
+const JSList *
+jackctl_driver_get_parameters(
+ jackctl_driver_t * driver);
+
+/**
+ * Call this function to parse parameters for a driver.
+ *
+ * @param driver driver object handle
+ * @param argc parameter list len
+ * @param argv parameter list, as an array of char*
+ *
+ * @return success status: true - success, false - fail
+ */
+int
+jackctl_driver_params_parse(
+ jackctl_driver_t * driver,
+ int argc,
+ char* argv[]);
+
+/**
+ * Call this function to get name of internal client.
+ *
+ * @param internal internal object handle to get name of
+ *
+ * @return internal name. Must not be modified. Always same for same
+ * internal object.
+ */
+const char *
+jackctl_internal_get_name(
+ jackctl_internal_t * internal);
+
+/**
+ * Call this function to get list of internal parameters. List node data
+ * pointers is a parameter object handle (::jackctl_parameter_t).
+ *
+ * @param internal internal object handle to get parameters for
+ *
+ * @return Single linked list of parameter object handles. Must not be
+ * modified. Always same for same internal object.
+ */
+const JSList *
+jackctl_internal_get_parameters(
+ jackctl_internal_t * internal);
+
+/**
+ * Call this function to get parameter name.
+ *
+ * @param parameter parameter object handle to get name of
+ *
+ * @return parameter name. Must not be modified. Always same for same
+ * parameter object.
+ */
+const char *
+jackctl_parameter_get_name(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function to get parameter short description.
+ *
+ * @param parameter parameter object handle to get short description of
+ *
+ * @return parameter short description. Must not be modified. Always
+ * same for same parameter object.
+ */
+const char *
+jackctl_parameter_get_short_description(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function to get parameter long description.
+ *
+ * @param parameter parameter object handle to get long description of
+ *
+ * @return parameter long description. Must not be modified. Always
+ * same for same parameter object.
+ */
+const char *
+jackctl_parameter_get_long_description(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function to get parameter type.
+ *
+ * @param parameter parameter object handle to get type of
+ *
+ * @return parameter type. Always same for same parameter object.
+ */
+jackctl_param_type_t
+jackctl_parameter_get_type(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function to get parameter character.
+ *
+ * @param parameter parameter object handle to get character of
+ *
+ * @return character.
+ */
+char
+jackctl_parameter_get_id(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function to check whether parameter has been set, or its
+ * default value is being used.
+ *
+ * @param parameter parameter object handle to check
+ *
+ * @return true - parameter is set, false - parameter is using default
+ * value.
+ */
+bool
+jackctl_parameter_is_set(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function to reset parameter to its default value.
+ *
+ * @param parameter parameter object handle to reset value of
+ *
+ * @return success status: true - success, false - fail
+ */
+bool
+jackctl_parameter_reset(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function to get parameter value.
+ *
+ * @param parameter parameter object handle to get value of
+ *
+ * @return parameter value.
+ */
+union jackctl_parameter_value
+jackctl_parameter_get_value(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function to set parameter value.
+ *
+ * @param parameter parameter object handle to get value of
+ * @param value_ptr pointer to variable containing parameter value
+ *
+ * @return success status: true - success, false - fail
+ */
+bool
+jackctl_parameter_set_value(
+ jackctl_parameter_t * parameter,
+ const union jackctl_parameter_value * value_ptr);
+
+/**
+ * Call this function to get parameter default value.
+ *
+ * @param parameter parameter object handle to get default value of
+ *
+ * @return parameter default value.
+ */
+union jackctl_parameter_value
+jackctl_parameter_get_default_value(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function check whether parameter has range constraint.
+ *
+ * @param parameter object handle of parameter to check
+ *
+ * @return whether parameter has range constraint.
+ */
+bool
+jackctl_parameter_has_range_constraint(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function check whether parameter has enumeration constraint.
+ *
+ * @param parameter object handle of parameter to check
+ *
+ * @return whether parameter has enumeration constraint.
+ */
+bool
+jackctl_parameter_has_enum_constraint(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function get how many enumeration values parameter has.
+ *
+ * @param parameter object handle of parameter
+ *
+ * @return number of enumeration values
+ */
+uint32_t
+jackctl_parameter_get_enum_constraints_count(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function to get parameter enumeration value.
+ *
+ * @param parameter object handle of parameter
+ * @param index index of parameter enumeration value
+ *
+ * @return enumeration value.
+ */
+union jackctl_parameter_value
+jackctl_parameter_get_enum_constraint_value(
+ jackctl_parameter_t * parameter,
+ uint32_t index);
+
+/**
+ * Call this function to get parameter enumeration value description.
+ *
+ * @param parameter object handle of parameter
+ * @param index index of parameter enumeration value
+ *
+ * @return enumeration value description.
+ */
+const char *
+jackctl_parameter_get_enum_constraint_description(
+ jackctl_parameter_t * parameter,
+ uint32_t index);
+
+/**
+ * Call this function to get parameter range.
+ *
+ * @param parameter object handle of parameter
+ * @param min_ptr pointer to variable receiving parameter minimum value
+ * @param max_ptr pointer to variable receiving parameter maximum value
+ */
+void
+jackctl_parameter_get_range_constraint(
+ jackctl_parameter_t * parameter,
+ union jackctl_parameter_value * min_ptr,
+ union jackctl_parameter_value * max_ptr);
+
+/**
+ * Call this function to check whether parameter constraint is strict,
+ * i.e. whether supplying non-matching value will not work for sure.
+ *
+ * @param parameter parameter object handle to check
+ *
+ * @return whether parameter constraint is strict.
+ */
+bool
+jackctl_parameter_constraint_is_strict(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function to check whether parameter has fake values,
+ * i.e. values have no user meaningful meaning and only value
+ * description is meaningful to user.
+ *
+ * @param parameter parameter object handle to check
+ *
+ * @return whether parameter constraint is strict.
+ */
+bool
+jackctl_parameter_constraint_is_fake_value(
+ jackctl_parameter_t * parameter);
+
+/**
+ * Call this function to log an error message.
+ *
+ * @param format string
+ */
+void
+jack_error(
+ const char *format,
+ ...);
+
+/**
+ * Call this function to log an information message.
+ *
+ * @param format string
+ */
+void
+jack_info(
+ const char *format,
+ ...);
+
+/**
+ * Call this function to log an information message but only when
+ * verbose mode is enabled.
+ *
+ * @param format string
+ */
+void
+jack_log(
+ const char *format,
+ ...);
+
+/* @} */
+
+#if 0
+{ /* Adjust editor indent */
+#endif
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* #ifndef JACKCTL_H__2EEDAD78_DF4C_4B26_83B7_4FF1A446A47E__INCLUDED */
diff --git a/pipewire-jack/jack/intclient.h b/pipewire-jack/jack/intclient.h
new file mode 100644
index 0000000..d4503ae
--- /dev/null
+++ b/pipewire-jack/jack/intclient.h
@@ -0,0 +1,130 @@
+/*
+* Copyright (C) 2004 Jack O'Quin
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 2.1 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+*/
+
+#ifndef __jack_intclient_h__
+#define __jack_intclient_h__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <jack/types.h>
+
+/**
+ * Get an internal client's name. This is useful when @ref
+ * JackUseExactName was not specified on jack_internal_client_load()
+ * and @ref JackNameNotUnique status was returned. In that case, the
+ * actual name will differ from the @a client_name requested.
+ *
+ * @param client requesting JACK client's handle.
+ *
+ * @param intclient handle returned from jack_internal_client_load()
+ * or jack_internal_client_handle().
+ *
+ * @return NULL if unsuccessful, otherwise pointer to the internal
+ * client name obtained from the heap via malloc(). The caller should
+ * jack_free() this storage when no longer needed.
+ */
+char *jack_get_internal_client_name (jack_client_t *client,
+ jack_intclient_t intclient);
+
+/**
+ * Return the @ref jack_intclient_t handle for an internal client
+ * running in the JACK server.
+ *
+ * @param client requesting JACK client's handle.
+ *
+ * @param client_name for the internal client of no more than
+ * jack_client_name_size() characters. The name scope is local to the
+ * current server.
+ *
+ * @param status (if non-NULL) an address for JACK to return
+ * information from this operation. This status word is formed by
+ * OR-ing together the relevant @ref JackStatus bits.
+ *
+ * @return Opaque internal client handle if successful. If 0, the
+ * internal client was not found, and @a *status includes the @ref
+ * JackNoSuchClient and @ref JackFailure bits.
+ */
+jack_intclient_t jack_internal_client_handle (jack_client_t *client,
+ const char *client_name,
+ jack_status_t *status);
+
+/**
+ * Load an internal client into the JACK server.
+ *
+ * Internal clients run inside the JACK server process. They can use
+ * most of the same functions as external clients. Each internal
+ * client is built as a shared object module, which must declare
+ * jack_initialize() and jack_finish() entry points called at load and
+ * unload times. See @ref inprocess.c for an example.
+ *
+ * @param client loading JACK client's handle.
+ *
+ * @param client_name of at most jack_client_name_size() characters
+ * for the internal client to load. The name scope is local to the
+ * current server.
+ *
+ * @param options formed by OR-ing together @ref JackOptions bits.
+ * Only the @ref JackLoadOptions bits are valid.
+ *
+ * @param status (if non-NULL) an address for JACK to return
+ * information from the load operation. This status word is formed by
+ * OR-ing together the relevant @ref JackStatus bits.
+ *
+ * <b>Optional parameters:</b> depending on corresponding [@a options
+ * bits] additional parameters may follow @a status (in this order).
+ *
+ * @arg [@ref JackLoadName] <em>(char *) load_name</em> is the shared
+ * object file from which to load the new internal client (otherwise
+ * use the @a client_name).
+ *
+ * @arg [@ref JackLoadInit] <em>(char *) load_init</em> an arbitrary
+ * string passed to the internal client's jack_initialize() routine
+ * (otherwise NULL), of no more than @ref JACK_LOAD_INIT_LIMIT bytes.
+ *
+ * @return Opaque internal client handle if successful. If this is 0,
+ * the load operation failed, the internal client was not loaded, and
+ * @a *status includes the @ref JackFailure bit.
+ */
+jack_intclient_t jack_internal_client_load (jack_client_t *client,
+ const char *client_name,
+ jack_options_t options,
+ jack_status_t *status, ...);
+/**
+ * Unload an internal client from a JACK server. This calls the
+ * intclient's jack_finish() entry point then removes it. See @ref
+ * inprocess.c for an example.
+ *
+ * @param client unloading JACK client's handle.
+ *
+ * @param intclient handle returned from jack_internal_client_load() or
+ * jack_internal_client_handle().
+ *
+ * @return 0 if successful, otherwise @ref JackStatus bits.
+ */
+jack_status_t jack_internal_client_unload (jack_client_t *client,
+ jack_intclient_t intclient);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __jack_intclient_h__ */
diff --git a/pipewire-jack/jack/jack.h b/pipewire-jack/jack/jack.h
new file mode 100644
index 0000000..2b83cb1
--- /dev/null
+++ b/pipewire-jack/jack/jack.h
@@ -0,0 +1,1477 @@
+/*
+ Copyright (C) 2001 Paul Davis
+ Copyright (C) 2004 Jack O'Quin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __jack_h__
+#define __jack_h__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <jack/systemdeps.h>
+#include <jack/types.h>
+#include <jack/transport.h>
+
+/**
+ * Note: More documentation can be found in jack/types.h.
+ */
+
+ /*************************************************************
+ * NOTE: JACK_WEAK_EXPORT ***MUST*** be used on every function
+ * added to the JACK API after the 0.116.2 release.
+ *
+ * Functions that predate this release are marked with
+ * JACK_WEAK_OPTIONAL_EXPORT which can be defined at compile
+ * time in a variety of ways. The default definition is empty,
+ * so that these symbols get normal linkage. If you wish to
+ * use all JACK symbols with weak linkage, include
+ * <jack/weakjack.h> before jack.h.
+ *************************************************************/
+
+#include <jack/weakmacros.h>
+
+/**
+ * Call this function to get version of the JACK, in form of several numbers
+ *
+ * @param major_ptr pointer to variable receiving major version of JACK.
+ *
+ * @param minor_ptr pointer to variable receiving minor version of JACK.
+ *
+ * @param major_ptr pointer to variable receiving micro version of JACK.
+ *
+ * @param major_ptr pointer to variable receiving protocol version of JACK.
+ *
+ */
+void
+jack_get_version(
+ int *major_ptr,
+ int *minor_ptr,
+ int *micro_ptr,
+ int *proto_ptr) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Call this function to get version of the JACK, in form of a string
+ *
+ * @return Human readable string describing JACK version being used.
+ *
+ */
+const char *
+jack_get_version_string(void) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @defgroup ClientFunctions Creating & manipulating clients
+ * @{
+ */
+
+/**
+ * Open an external client session with a JACK server. This interface
+ * is more complex but more powerful than jack_client_new(). With it,
+ * clients may choose which of several servers to connect, and control
+ * whether and how to start the server automatically, if it was not
+ * already running. There is also an option for JACK to generate a
+ * unique client name, when necessary.
+ *
+ * @param client_name of at most jack_client_name_size() characters.
+ * The name scope is local to each server. Unless forbidden by the
+ * @ref JackUseExactName option, the server will modify this name to
+ * create a unique variant, if needed.
+ *
+ * @param options formed by OR-ing together @ref JackOptions bits.
+ * Only the @ref JackOpenOptions bits are allowed.
+ *
+ * @param status (if non-NULL) an address for JACK to return
+ * information from the open operation. This status word is formed by
+ * OR-ing together the relevant @ref JackStatus bits.
+ *
+ *
+ * <b>Optional parameters:</b> depending on corresponding [@a options
+ * bits] additional parameters may follow @a status (in this order).
+ *
+ * @arg [@ref JackServerName] <em>(char *) server_name</em> selects
+ * from among several possible concurrent server instances. Server
+ * names are unique to each user. If unspecified, use "default"
+ * unless \$JACK_DEFAULT_SERVER is defined in the process environment.
+ *
+ * @return Opaque client handle if successful. If this is NULL, the
+ * open operation failed, @a *status includes @ref JackFailure and the
+ * caller is not a JACK client.
+ */
+jack_client_t * jack_client_open (const char *client_name,
+ jack_options_t options,
+ jack_status_t *status, ...) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+* \bold THIS FUNCTION IS DEPRECATED AND SHOULD NOT BE USED IN
+* NEW JACK CLIENTS
+*
+* @deprecated Please use jack_client_open().
+*/
+jack_client_t * jack_client_new (const char *client_name) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * Disconnects an external client from a JACK server.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_client_close (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the maximum number of characters in a JACK client name
+ * including the final NULL character. This value is a constant.
+ */
+int jack_client_name_size (void) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return pointer to actual client name. This is useful when @ref
+ * JackUseExactName is not specified on open and @ref
+ * JackNameNotUnique status was returned. In that case, the actual
+ * name will differ from the @a client_name requested.
+ */
+char * jack_get_client_name (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Get the session ID for a client name.
+ *
+ * The session manager needs this to reassociate a client name to the session_id.
+ *
+ * The caller is responsible for calling jack_free(3) on any non-NULL
+ * returned value.
+ */
+char *jack_get_uuid_for_client_name (jack_client_t *client,
+ const char *client_name) JACK_WEAK_EXPORT;
+
+/**
+ * Get the client name for a session_id.
+ *
+ * In order to snapshot the graph connections, the session manager needs to map
+ * session_ids to client names.
+ *
+ * The caller is responsible for calling jack_free(3) on any non-NULL
+ * returned value.
+ */
+char *jack_get_client_name_by_uuid (jack_client_t *client,
+ const char *client_uuid ) JACK_WEAK_EXPORT;
+
+/**
+ * Load an internal client into the Jack server.
+ *
+ * Internal clients run inside the JACK server process. They can use
+ * most of the same functions as external clients. Each internal
+ * client must declare jack_initialize() and jack_finish() entry
+ * points, called at load and unload times. See inprocess.c for an
+ * example of how to write an internal client.
+ *
+ * @deprecated Please use jack_internal_client_load().
+ *
+ * @param client_name of at most jack_client_name_size() characters.
+ *
+ * @param load_name of a shared object file containing the code for
+ * the new client.
+ *
+ * @param load_init an arbitrary string passed to the jack_initialize()
+ * routine of the new client (may be NULL).
+ *
+ * @return 0 if successful.
+ */
+int jack_internal_client_new (const char *client_name,
+ const char *load_name,
+ const char *load_init) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * Remove an internal client from a JACK server.
+ *
+ * @deprecated Please use jack_internal_client_unload().
+ */
+void jack_internal_client_close (const char *client_name) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * Tell the Jack server that the program is ready to start processing
+ * audio.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_activate (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Tell the Jack server to remove this @a client from the process
+ * graph. Also, disconnect all ports belonging to it, since inactive
+ * clients have no port connections.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_deactivate (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return pid of client. If not available, 0 will be returned.
+ */
+int jack_get_client_pid (const char *name) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the pthread ID of the thread running the JACK client side
+ * real-time code.
+ */
+jack_native_thread_t jack_client_thread_id (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/*@}*/
+
+/**
+ * @param client pointer to JACK client structure.
+ *
+ * Check if the JACK subsystem is running with -R (--realtime).
+ *
+ * @return 1 if JACK is running realtime, 0 otherwise
+ */
+int jack_is_realtime (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @defgroup NonCallbackAPI The non-callback API
+ * @{
+ */
+
+/**
+ * \bold THIS FUNCTION IS DEPRECATED AND SHOULD NOT BE USED IN
+ * NEW JACK CLIENTS.
+ *
+ * @deprecated Please use jack_cycle_wait() and jack_cycle_signal() functions.
+ */
+jack_nframes_t jack_thread_wait (jack_client_t *client, int status) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Wait until this JACK client should process data.
+ *
+ * @param client - pointer to a JACK client structure
+ *
+ * @return the number of frames of data to process
+ */
+jack_nframes_t jack_cycle_wait (jack_client_t* client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Signal next clients in the graph.
+ *
+ * @param client - pointer to a JACK client structure
+ * @param status - if non-zero, calling thread should exit
+ */
+void jack_cycle_signal (jack_client_t* client, int status) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Tell the Jack server to call @a thread_callback in the RT thread.
+ * Typical use are in conjunction with @a jack_cycle_wait and @a jack_cycle_signal functions.
+ * The code in the supplied function must be suitable for real-time
+ * execution. That means that it cannot call functions that might
+ * block for a long time. This includes malloc, free, printf,
+ * pthread_mutex_lock, sleep, wait, poll, select, pthread_join,
+ * pthread_cond_wait, etc, etc. See
+ * http://jackit.sourceforge.net/docs/design/design.html#SECTION00411000000000000000
+ * for more information.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+*/
+int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/*@}*/
+
+/**
+ * @defgroup ClientCallbacks Setting Client Callbacks
+ * @{
+ */
+
+/**
+ * Tell JACK to call @a thread_init_callback once just after
+ * the creation of the thread in which all other callbacks
+ * will be handled.
+ *
+ * The code in the supplied function does not need to be
+ * suitable for real-time execution.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @return 0 on success, otherwise a non-zero error code, causing JACK
+ * to remove that client from the process() graph.
+ */
+int jack_set_thread_init_callback (jack_client_t *client,
+ JackThreadInitCallback thread_init_callback,
+ void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @param client pointer to JACK client structure.
+ * @param function The jack_shutdown function pointer.
+ * @param arg The arguments for the jack_shutdown function.
+ *
+ * Register a function (and argument) to be called if and when the
+ * JACK server shuts down the client thread. The function must
+ * be written as if it were an asynchronous POSIX signal
+ * handler --- use only async-safe functions, and remember that it
+ * is executed from another thread. A typical function might
+ * set a flag or write to a pipe so that the rest of the
+ * application knows that the JACK client thread has shut
+ * down.
+ *
+ * NOTE: clients do not need to call this. It exists only
+ * to help more complex clients understand what is going
+ * on. It should be called before jack_client_activate().
+ *
+ * NOTE: if a client calls this AND jack_on_info_shutdown(), then
+ * in case of a client thread shutdown, the callback
+ * passed to this function will not be called, and the one passed to
+ * jack_on_info_shutdown() will.
+ *
+ * NOTE: application should typically signal another thread to correctly
+ * finish cleanup, that is by calling "jack_client_close"
+ * (since "jack_client_close" cannot be called directly in the context
+ * of the thread that calls the shutdown callback).
+ */
+void jack_on_shutdown (jack_client_t *client,
+ JackShutdownCallback shutdown_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @param client pointer to JACK client structure.
+ * @param function The jack_info_shutdown function pointer.
+ * @param arg The arguments for the jack_info_shutdown function.
+ *
+ * Register a function (and argument) to be called if and when the
+ * JACK server shuts down the client thread. The function must
+ * be written as if it were an asynchronous POSIX signal
+ * handler --- use only async-safe functions, and remember that it
+ * is executed from another thread. A typical function might
+ * set a flag or write to a pipe so that the rest of the
+ * application knows that the JACK client thread has shut
+ * down.
+ *
+ * NOTE: clients do not need to call this. It exists only
+ * to help more complex clients understand what is going
+ * on. It should be called before jack_client_activate().
+ *
+ * NOTE: if a client calls this AND jack_on_shutdown(), then
+ * in case of a client thread shutdown, the callback passed to
+ * jack_on_info_shutdown() will be called.
+ *
+ * NOTE: application should typically signal another thread to correctly
+ * finish cleanup, that is by calling "jack_client_close"
+ * (since "jack_client_close" cannot be called directly in the context
+ * of the thread that calls the shutdown callback).
+ */
+void jack_on_info_shutdown (jack_client_t *client,
+ JackInfoShutdownCallback shutdown_callback, void *arg) JACK_WEAK_EXPORT;
+
+/**
+ * Tell the Jack server to call @a process_callback whenever there is
+ * work be done, passing @a arg as the second argument.
+ *
+ * The code in the supplied function must be suitable for real-time
+ * execution. That means that it cannot call functions that might
+ * block for a long time. This includes malloc, free, printf,
+ * pthread_mutex_lock, sleep, wait, poll, select, pthread_join,
+ * pthread_cond_wait, etc, etc. See
+ * http://jackit.sourceforge.net/docs/design/design.html#SECTION00411000000000000000
+ * for more information.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+ */
+int jack_set_process_callback (jack_client_t *client,
+ JackProcessCallback process_callback,
+ void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Tell the Jack server to call @a freewheel_callback
+ * whenever we enter or leave "freewheel" mode, passing @a
+ * arg as the second argument. The first argument to the
+ * callback will be non-zero if JACK is entering freewheel
+ * mode, and zero otherwise.
+ *
+ * All "notification events" are received in a separated non RT thread,
+ * the code in the supplied function does not need to be
+ * suitable for real-time execution.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+ */
+int jack_set_freewheel_callback (jack_client_t *client,
+ JackFreewheelCallback freewheel_callback,
+ void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Tell JACK to call @a bufsize_callback whenever the size of the
+ * buffer that will be passed to the @a process_callback is about to
+ * change. Clients that depend on knowing the buffer size must supply
+ * a @a bufsize_callback before activating themselves.
+ *
+ * All "notification events" are received in a separated non RT thread,
+ * the code in the supplied function does not need to be
+ * suitable for real-time execution.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @param client pointer to JACK client structure.
+ * @param bufsize_callback function to call when the buffer size changes.
+ * @param arg argument for @a bufsize_callback.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_buffer_size_callback (jack_client_t *client,
+ JackBufferSizeCallback bufsize_callback,
+ void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Tell the Jack server to call @a srate_callback whenever the system
+ * sample rate changes.
+ *
+ * All "notification events" are received in a separated non RT thread,
+ * the code in the supplied function does not need to be
+ * suitable for real-time execution.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_sample_rate_callback (jack_client_t *client,
+ JackSampleRateCallback srate_callback,
+ void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Tell the JACK server to call @a client_registration_callback whenever a
+ * client is registered or unregistered, passing @a arg as a parameter.
+ *
+ * All "notification events" are received in a separated non RT thread,
+ * the code in the supplied function does not need to be
+ * suitable for real-time execution.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_client_registration_callback (jack_client_t *client,
+ JackClientRegistrationCallback
+ registration_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Tell the JACK server to call @a registration_callback whenever a
+ * port is registered or unregistered, passing @a arg as a parameter.
+ *
+ * All "notification events" are received in a separated non RT thread,
+ * the code in the supplied function does not need to be
+ * suitable for real-time execution.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+ int jack_set_port_registration_callback (jack_client_t *client,
+ JackPortRegistrationCallback
+ registration_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+ /**
+ * Tell the JACK server to call @a connect_callback whenever a
+ * port is connected or disconnected, passing @a arg as a parameter.
+ *
+ * All "notification events" are received in a separated non RT thread,
+ * the code in the supplied function does not need to be
+ * suitable for real-time execution.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_port_connect_callback (jack_client_t *client,
+ JackPortConnectCallback
+ connect_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+ /**
+ * Tell the JACK server to call @a rename_callback whenever a
+ * port is renamed, passing @a arg as a parameter.
+ *
+ * All "notification events" are received in a separated non RT thread,
+ * the code in the supplied function does not need to be
+ * suitable for real-time execution.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_port_rename_callback (jack_client_t *client,
+ JackPortRenameCallback
+ rename_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Tell the JACK server to call @a graph_callback whenever the
+ * processing graph is reordered, passing @a arg as a parameter.
+ *
+ * All "notification events" are received in a separated non RT thread,
+ * the code in the supplied function does not need to be
+ * suitable for real-time execution.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_graph_order_callback (jack_client_t *client,
+ JackGraphOrderCallback graph_callback,
+ void *) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Tell the JACK server to call @a xrun_callback whenever there is a
+ * xrun, passing @a arg as a parameter.
+ *
+ * All "notification events" are received in a separated non RT thread,
+ * the code in the supplied function does not need to be
+ * suitable for real-time execution.
+ *
+ * NOTE: this function cannot be called while the client is activated
+ * (after jack_activate has been called.)
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_xrun_callback (jack_client_t *client,
+ JackXRunCallback xrun_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/*@}*/
+
+/**
+ * Tell the Jack server to call @a latency_callback whenever it
+ * is necessary to recompute the latencies for some or all
+ * Jack ports.
+ *
+ * @a latency_callback will be called twice each time it is
+ * needed, once being passed JackCaptureLatency and once
+ * JackPlaybackLatency. See @ref LatencyFunctions for
+ * the definition of each type of latency and related functions.
+ *
+ * <b>IMPORTANT: Most JACK clients do NOT need to register a latency
+ * callback.</b>
+ *
+ * Clients that meet any of the following conditions do NOT
+ * need to register a latency callback:
+ *
+ * - have only input ports
+ * - have only output ports
+ * - their output is totally unrelated to their input
+ * - their output is not delayed relative to their input
+ * (i.e. data that arrives in a given process()
+ * callback is processed and output again in the
+ * same callback)
+ *
+ * Clients NOT registering a latency callback MUST also
+ * satisfy this condition:
+ *
+ * - have no multiple distinct internal signal pathways
+ *
+ * This means that if your client has more than 1 input and
+ * output port, and considers them always "correlated"
+ * (e.g. as a stereo pair), then there is only 1 (e.g. stereo)
+ * signal pathway through the client. This would be true,
+ * for example, of a stereo FX rack client that has a
+ * left/right input pair and a left/right output pair.
+ *
+ * However, this is somewhat a matter of perspective. The
+ * same FX rack client could be connected so that its
+ * two input ports were connected to entirely separate
+ * sources. Under these conditions, the fact that the client
+ * does not register a latency callback MAY result
+ * in port latency values being incorrect.
+ *
+ * Clients that do not meet any of those conditions SHOULD
+ * register a latency callback.
+ *
+ * Another case is when a client wants to use
+ * @ref jack_port_get_latency_range(), which only returns meaningful
+ * values when ports get connected and latency values change.
+ *
+ * See the documentation for @ref jack_port_set_latency_range()
+ * on how the callback should operate. Remember that the @a mode
+ * argument given to the latency callback will need to be
+ * passed into @ref jack_port_set_latency_range()
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_latency_callback (jack_client_t *client,
+ JackLatencyCallback latency_callback,
+ void *) JACK_WEAK_EXPORT;
+/*@}*/
+
+/**
+ * @defgroup ServerClientControl Controlling & querying JACK server operation
+ * @{
+ */
+
+/**
+ * Start/Stop JACK's "freewheel" mode.
+ *
+ * When in "freewheel" mode, JACK no longer waits for
+ * any external event to begin the start of the next process
+ * cycle.
+ *
+ * As a result, freewheel mode causes "faster than realtime"
+ * execution of a JACK graph. If possessed, real-time
+ * scheduling is dropped when entering freewheel mode, and
+ * if appropriate it is reacquired when stopping.
+ *
+ * IMPORTANT: on systems using capabilities to provide real-time
+ * scheduling (i.e. Linux kernel 2.4), if onoff is zero, this function
+ * must be called from the thread that originally called jack_activate().
+ * This restriction does not apply to other systems (e.g. Linux kernel 2.6
+ * or OS X).
+ *
+ * @param client pointer to JACK client structure
+ * @param onoff if non-zero, freewheel mode starts. Otherwise
+ * freewheel mode ends.
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+ */
+int jack_set_freewheel(jack_client_t* client, int onoff) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Change the buffer size passed to the @a process_callback.
+ *
+ * This operation stops the JACK engine process cycle, then calls all
+ * registered @a bufsize_callback functions before restarting the
+ * process cycle. This will cause a gap in the audio flow, so it
+ * should only be done at appropriate stopping points.
+ *
+ * @see jack_set_buffer_size_callback()
+ *
+ * @param client pointer to JACK client structure.
+ * @param nframes new buffer size. Must be a power of two.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the sample rate of the jack system, as set by the user when
+ * jackd was started.
+ */
+jack_nframes_t jack_get_sample_rate (jack_client_t *) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the current maximum size that will ever be passed to the @a
+ * process_callback. It should only be used *before* the client has
+ * been activated. This size may change, clients that depend on it
+ * must register a @a bufsize_callback so they will be notified if it
+ * does.
+ *
+ * @see jack_set_buffer_size_callback()
+ */
+jack_nframes_t jack_get_buffer_size (jack_client_t *) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Old-style interface to become the timebase for the entire JACK
+ * subsystem.
+ *
+ * @deprecated This function still exists for compatibility with the
+ * earlier transport interface, but it does nothing. Instead, see
+ * transport.h and use jack_set_timebase_callback().
+ *
+ * @return ENOSYS, function not implemented.
+ */
+int jack_engine_takeover_timebase (jack_client_t *) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * @return the current CPU load estimated by JACK. This is a running
+ * average of the time it takes to execute a full process cycle for
+ * all clients as a percentage of the real time available per cycle
+ * determined by the buffer size and sample rate.
+ */
+float jack_cpu_load (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/*@}*/
+
+/**
+ * @defgroup PortFunctions Creating & manipulating ports
+ * @{
+ */
+
+/**
+ * Create a new port for the client. This is an object used for moving
+ * data of any type in or out of the client. Ports may be connected
+ * in various ways.
+ *
+ * Each port has a short name. The port's full name contains the name
+ * of the client concatenated with a colon (:) followed by its short
+ * name. The jack_port_name_size() is the maximum length of this full
+ * name. Exceeding that will cause the port registration to fail and
+ * return NULL.
+ *
+ * The @a port_name must be unique among all ports owned by this client.
+ * If the name is not unique, the registration will fail.
+ *
+ * All ports have a type, which may be any non-NULL and non-zero
+ * length string, passed as an argument. Some port types are built
+ * into the JACK API, currently only JACK_DEFAULT_AUDIO_TYPE.
+ *
+ * @param client pointer to JACK client structure.
+ * @param port_name non-empty short name for the new port (not
+ * including the leading @a "client_name:"). Must be unique.
+ * @param port_type port type name. If longer than
+ * jack_port_type_size(), only that many characters are significant.
+ * @param flags @ref JackPortFlags bit mask.
+ * @param buffer_size must be non-zero if this is not a built-in @a
+ * port_type. Otherwise, it is ignored.
+ *
+ * @return jack_port_t pointer on success, otherwise NULL.
+ */
+jack_port_t * jack_port_register (jack_client_t *client,
+ const char *port_name,
+ const char *port_type,
+ unsigned long flags,
+ unsigned long buffer_size) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Remove the port from the client, disconnecting any existing
+ * connections.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_port_unregister (jack_client_t *client, jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * This returns a pointer to the memory area associated with the
+ * specified port. For an output port, it will be a memory area
+ * that can be written to; for an input port, it will be an area
+ * containing the data from the port's connection(s), or
+ * zero-filled. if there are multiple inbound connections, the data
+ * will be mixed appropriately.
+ *
+ * FOR OUTPUT PORTS ONLY : DEPRECATED in Jack 2.0 !!
+ * ---------------------------------------------------
+ * You may cache the value returned, but only between calls to
+ * your "blocksize" callback. For this reason alone, you should
+ * either never cache the return value or ensure you have
+ * a "blocksize" callback and be sure to invalidate the cached
+ * address from there.
+ *
+ * Caching output ports is DEPRECATED in Jack 2.0, due to some new optimization (like "pipelining").
+ * Port buffers have to be retrieved in each callback for proper functioning.
+ */
+void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the UUID of the jack_port_t
+ *
+ * @see jack_uuid_to_string() to convert into a string representation
+ */
+jack_uuid_t jack_port_uuid (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the full name of the jack_port_t (including the @a
+ * "client_name:" prefix).
+ *
+ * @see jack_port_name_size().
+ */
+const char * jack_port_name (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the short name of the jack_port_t (not including the @a
+ * "client_name:" prefix).
+ *
+ * @see jack_port_name_size().
+ */
+const char * jack_port_short_name (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the @ref JackPortFlags of the jack_port_t.
+ */
+int jack_port_flags (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the @a port type, at most jack_port_type_size() characters
+ * including a final NULL.
+ */
+const char * jack_port_type (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+ /**
+ * @return the @a port type id.
+ */
+jack_port_type_id_t jack_port_type_id (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return TRUE if the jack_port_t belongs to the jack_client_t.
+ */
+int jack_port_is_mine (const jack_client_t *client, const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return number of connections to or from @a port.
+ *
+ * @pre The calling client must own @a port.
+ */
+int jack_port_connected (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return TRUE if the locally-owned @a port is @b directly connected
+ * to the @a port_name.
+ *
+ * @see jack_port_name_size()
+ */
+int jack_port_connected_to (const jack_port_t *port,
+ const char *port_name) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return a null-terminated array of full port names to which the @a
+ * port is connected. If none, returns NULL.
+ *
+ * The caller is responsible for calling jack_free() on any non-NULL
+ * returned value.
+ *
+ * @param port locally owned jack_port_t pointer.
+ *
+ * @see jack_port_name_size(), jack_port_get_all_connections()
+ */
+const char ** jack_port_get_connections (const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return a null-terminated array of full port names to which the @a
+ * port is connected. If none, returns NULL.
+ *
+ * The caller is responsible for calling jack_free() on any non-NULL
+ * returned value.
+ *
+ * This differs from jack_port_get_connections() in two important
+ * respects:
+ *
+ * 1) You may not call this function from code that is
+ * executed in response to a JACK event. For example,
+ * you cannot use it in a GraphReordered handler.
+ *
+ * 2) You need not be the owner of the port to get information
+ * about its connections.
+ *
+ * @see jack_port_name_size()
+ */
+const char ** jack_port_get_all_connections (const jack_client_t *client,
+ const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ *
+ * @deprecated This function will be removed from a future version
+ * of JACK. Do not use it. There is no replacement. It has
+ * turned out to serve essentially no purpose in real-life
+ * JACK clients.
+ */
+int jack_port_tie (jack_port_t *src, jack_port_t *dst) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ *
+ * @deprecated This function will be removed from a future version
+ * of JACK. Do not use it. There is no replacement. It has
+ * turned out to serve essentially no purpose in real-life
+ * JACK clients.
+ */
+int jack_port_untie (jack_port_t *port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * \bold THIS FUNCTION IS DEPRECATED AND SHOULD NOT BE USED IN
+ * NEW JACK CLIENTS
+ *
+ * Modify a port's short name. May be called at any time. If the
+ * resulting full name (including the @a "client_name:" prefix) is
+ * longer than jack_port_name_size(), it will be truncated.
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+ */
+int jack_port_set_name (jack_port_t *port, const char *port_name) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * Modify a port's short name. May NOT be called from a callback handling a server event.
+ * If the resulting full name (including the @a "client_name:" prefix) is
+ * longer than jack_port_name_size(), it will be truncated.
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+ *
+ * This differs from jack_port_set_name() by triggering PortRename notifications to
+ * clients that have registered a port rename handler.
+ */
+int jack_port_rename (jack_client_t* client, jack_port_t *port, const char *port_name) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Set @a alias as an alias for @a port. May be called at any time.
+ * If the alias is longer than jack_port_name_size(), it will be truncated.
+ *
+ * After a successful call, and until JACK exits or
+ * @function jack_port_unset_alias() is called, @alias may be
+ * used as a alternate name for the port.
+ *
+ * Ports can have up to two aliases - if both are already
+ * set, this function will return an error.
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+ */
+int jack_port_set_alias (jack_port_t *port, const char *alias) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Remove @a alias as an alias for @a port. May be called at any time.
+ *
+ * After a successful call, @a alias can no longer be
+ * used as a alternate name for the port.
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+ */
+int jack_port_unset_alias (jack_port_t *port, const char *alias) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Get any aliases known for @port.
+ *
+ * @return the number of aliases discovered for the port
+ */
+int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2]) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * If @ref JackPortCanMonitor is set for this @a port, turn input
+ * monitoring on or off. Otherwise, do nothing.
+ */
+int jack_port_request_monitor (jack_port_t *port, int onoff) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * If @ref JackPortCanMonitor is set for this @a port_name, turn input
+ * monitoring on or off. Otherwise, do nothing.
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+ *
+ * @see jack_port_name_size()
+ */
+int jack_port_request_monitor_by_name (jack_client_t *client,
+ const char *port_name, int onoff) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * If @ref JackPortCanMonitor is set for a port, this function turns
+ * on input monitoring if it was off, and turns it off if only one
+ * request has been made to turn it on. Otherwise it does nothing.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_port_ensure_monitor (jack_port_t *port, int onoff) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return TRUE if input monitoring has been requested for @a port.
+ */
+int jack_port_monitoring_input (jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Establish a connection between two ports.
+ *
+ * When a connection exists, data written to the source port will
+ * be available to be read at the destination port.
+ *
+ * @pre The port types must be identical.
+ *
+ * @pre The @ref JackPortFlags of the @a source_port must include @ref
+ * JackPortIsOutput.
+ *
+ * @pre The @ref JackPortFlags of the @a destination_port must include
+ * @ref JackPortIsInput.
+ *
+ * @return 0 on success, EEXIST if the connection is already made,
+ * otherwise a non-zero error code
+ */
+int jack_connect (jack_client_t *client,
+ const char *source_port,
+ const char *destination_port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Remove a connection between two ports.
+ *
+ * @pre The port types must be identical.
+ *
+ * @pre The @ref JackPortFlags of the @a source_port must include @ref
+ * JackPortIsOutput.
+ *
+ * @pre The @ref JackPortFlags of the @a destination_port must include
+ * @ref JackPortIsInput.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_disconnect (jack_client_t *client,
+ const char *source_port,
+ const char *destination_port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Perform the same function as jack_disconnect() using port handles
+ * rather than names. This avoids the name lookup inherent in the
+ * name-based version.
+ *
+ * Clients connecting their own ports are likely to use this function,
+ * while generic connection clients (e.g. patchbays) would use
+ * jack_disconnect().
+ */
+int jack_port_disconnect (jack_client_t *client, jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the maximum number of characters in a full JACK port name
+ * including the final NULL character. This value is a constant.
+ *
+ * A port's full name contains the owning client name concatenated
+ * with a colon (:) followed by its short name and a NULL
+ * character.
+ */
+int jack_port_name_size(void) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the maximum number of characters in a JACK port type name
+ * including the final NULL character. This value is a constant.
+ */
+int jack_port_type_size(void) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the buffersize of a port of type @arg port_type.
+ *
+ * this function may only be called in a buffer_size callback.
+ */
+size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_type) JACK_WEAK_EXPORT;
+
+/*@}*/
+
+/**
+ * @defgroup LatencyFunctions Managing and determining latency
+ *
+ * The purpose of JACK's latency API is to allow clients to
+ * easily answer two questions:
+ *
+ * - How long has it been since the data read from a port arrived
+ * at the edge of the JACK graph (either via a physical port
+ * or being synthesized from scratch)?
+ *
+ * - How long will it be before the data written to a port arrives
+ * at the edge of a JACK graph?
+
+ * To help answering these two questions, all JACK ports have two
+ * latency values associated with them, both measured in frames:
+ *
+ * <b>capture latency</b>: how long since the data read from
+ * the buffer of a port arrived at
+ * a port marked with JackPortIsTerminal.
+ * The data will have come from the "outside
+ * world" if the terminal port is also
+ * marked with JackPortIsPhysical, or
+ * will have been synthesized by the client
+ * that owns the terminal port.
+ *
+ * <b>playback latency</b>: how long until the data
+ * written to the buffer of port will reach a port
+ * marked with JackPortIsTerminal.
+ *
+ * Both latencies might potentially have more than one value
+ * because there may be multiple pathways to/from a given port
+ * and a terminal port. Latency is therefore generally
+ * expressed a min/max pair.
+ *
+ * In most common setups, the minimum and maximum latency
+ * are the same, but this design accommodates more complex
+ * routing, and allows applications (and thus users) to
+ * detect cases where routing is creating an anomalous
+ * situation that may either need fixing or more
+ * sophisticated handling by clients that care about
+ * latency.
+ *
+ * See also @ref jack_set_latency_callback for details on how
+ * clients that add latency to the signal path should interact
+ * with JACK to ensure that the correct latency figures are
+ * used.
+ * @{
+ */
+
+/**
+ * The port latency is zero by default. Clients that control
+ * physical hardware with non-zero latency should call this
+ * to set the latency to its correct value. Note that the value
+ * should include any systemic latency present "outside" the
+ * physical hardware controlled by the client. For example,
+ * for a client controlling a digital audio interface connected
+ * to an external digital converter, the latency setting should
+ * include both buffering by the audio interface *and* the converter.
+ *
+ * @deprecated This method will be removed in the next major
+ * release of JACK. It should not be used in new code, and should
+ * be replaced by a latency callback that calls @ref
+ * jack_port_set_latency_range().
+ */
+void jack_port_set_latency (jack_port_t *port, jack_nframes_t) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * return the latency range defined by @a mode for
+ * @a port, in frames.
+ *
+ * See @ref LatencyFunctions for the definition of each latency value.
+ *
+ * This function is best used from callbacks, specifically the latency callback.
+ * Before a port is connected, this returns the default latency: zero.
+ * Therefore it only makes sense to call jack_port_get_latency_range() when
+ * the port is connected, and that gets signalled by the latency callback.
+ * See @ref jack_set_latency_callback() for details.
+ */
+void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) JACK_WEAK_EXPORT;
+
+/**
+ * set the minimum and maximum latencies defined by
+ * @a mode for @a port, in frames.
+ *
+ * See @ref LatencyFunctions for the definition of each latency value.
+ *
+ * This function should ONLY be used inside a latency
+ * callback. The client should determine the current
+ * value of the latency using @ref jack_port_get_latency_range()
+ * (called using the same mode as @a mode)
+ * and then add some number of frames to that reflects
+ * latency added by the client.
+ *
+ * How much latency a client adds will vary
+ * dramatically. For most clients, the answer is zero
+ * and there is no reason for them to register a latency
+ * callback and thus they should never call this
+ * function.
+ *
+ * More complex clients that take an input signal,
+ * transform it in some way and output the result but
+ * not during the same process() callback will
+ * generally know a single constant value to add
+ * to the value returned by @ref jack_port_get_latency_range().
+ *
+ * Such clients would register a latency callback (see
+ * @ref jack_set_latency_callback) and must know what input
+ * ports feed which output ports as part of their
+ * internal state. Their latency callback will update
+ * the ports' latency values appropriately.
+ *
+ * A pseudo-code example will help. The @a mode argument to the latency
+ * callback will determine whether playback or capture
+ * latency is being set. The callback will use
+ * @ref jack_port_set_latency_range() as follows:
+ *
+ * \code
+ * jack_latency_range_t range;
+ * if (mode == JackPlaybackLatency) {
+ * foreach input_port in (all self-registered port) {
+ * jack_port_get_latency_range (port_feeding_input_port, JackPlaybackLatency, &range);
+ * range.min += min_delay_added_as_signal_flows_from port_feeding to input_port;
+ * range.max += max_delay_added_as_signal_flows_from port_feeding to input_port;
+ * jack_port_set_latency_range (input_port, JackPlaybackLatency, &range);
+ * }
+ * } else if (mode == JackCaptureLatency) {
+ * foreach output_port in (all self-registered port) {
+ * jack_port_get_latency_range (port_fed_by_output_port, JackCaptureLatency, &range);
+ * range.min += min_delay_added_as_signal_flows_from_output_port_to_fed_by_port;
+ * range.max += max_delay_added_as_signal_flows_from_output_port_to_fed_by_port;
+ * jack_port_set_latency_range (output_port, JackCaptureLatency, &range);
+ * }
+ * }
+ * \endcode
+ *
+ * In this relatively simple pseudo-code example, it is assumed that
+ * each input port or output is connected to only 1 output or input
+ * port respectively.
+ *
+ * If a port is connected to more than 1 other port, then the
+ * range.min and range.max values passed to @ref
+ * jack_port_set_latency_range() should reflect the minimum and
+ * maximum values across all connected ports.
+ *
+ * See the description of @ref jack_set_latency_callback for more
+ * information.
+ */
+void jack_port_set_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range) JACK_WEAK_EXPORT;
+
+/**
+ * Request a complete recomputation of all port latencies. This
+ * can be called by a client that has just changed the internal
+ * latency of its port using jack_port_set_latency
+ * and wants to ensure that all signal pathways in the graph
+ * are updated with respect to the values that will be returned
+ * by jack_port_get_total_latency. It allows a client
+ * to change multiple port latencies without triggering a
+ * recompute for each change.
+ *
+ * @return zero for successful execution of the request. non-zero
+ * otherwise.
+ */
+int jack_recompute_total_latencies (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the time (in frames) between data being available or
+ * delivered at/to a port, and the time at which it arrived at or is
+ * delivered to the "other side" of the port. E.g. for a physical
+ * audio output port, this is the time between writing to the port and
+ * when the signal will leave the connector. For a physical audio
+ * input port, this is the time between the sound arriving at the
+ * connector and the corresponding frames being readable from the
+ * port.
+ *
+ * @deprecated This method will be removed in the next major
+ * release of JACK. It should not be used in new code, and should
+ * be replaced by jack_port_get_latency_range() in any existing
+ * use cases.
+ */
+jack_nframes_t jack_port_get_latency (jack_port_t *port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * The maximum of the sum of the latencies in every
+ * connection path that can be drawn between the port and other
+ * ports with the @ref JackPortIsTerminal flag set.
+ *
+ * @deprecated This method will be removed in the next major
+ * release of JACK. It should not be used in new code, and should
+ * be replaced by jack_port_get_latency_range() in any existing
+ * use cases.
+ */
+jack_nframes_t jack_port_get_total_latency (jack_client_t *client,
+ jack_port_t *port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * Request a complete recomputation of a port's total latency. This
+ * can be called by a client that has just changed the internal
+ * latency of its port using jack_port_set_latency
+ * and wants to ensure that all signal pathways in the graph
+ * are updated with respect to the values that will be returned
+ * by jack_port_get_total_latency.
+ *
+ * @return zero for successful execution of the request. non-zero
+ * otherwise.
+ *
+ * @deprecated This method will be removed in the next major
+ * release of JACK. It should not be used in new code, and should
+ * be replaced by jack_recompute_total_latencies() in any existing
+ * use cases.
+ */
+int jack_recompute_total_latency (jack_client_t*, jack_port_t* port) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/*@}*/
+
+/**
+ * @defgroup PortSearching Looking up ports
+ * @{
+ */
+
+/**
+ * @param port_name_pattern A regular expression used to select
+ * ports by name. If NULL or of zero length, no selection based
+ * on name will be carried out.
+ * @param type_name_pattern A regular expression used to select
+ * ports by type. If NULL or of zero length, no selection based
+ * on type will be carried out.
+ * @param flags A value used to select ports by their flags.
+ * If zero, no selection based on flags will be carried out.
+ *
+ * @return a NULL-terminated array of ports that match the specified
+ * arguments. The caller is responsible for calling jack_free() any
+ * non-NULL returned value.
+ *
+ * @see jack_port_name_size(), jack_port_type_size()
+ */
+const char ** jack_get_ports (jack_client_t *client,
+ const char *port_name_pattern,
+ const char *type_name_pattern,
+ unsigned long flags) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return address of the jack_port_t named @a port_name.
+ *
+ * @see jack_port_name_size()
+ */
+jack_port_t * jack_port_by_name (jack_client_t *client, const char *port_name) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return address of the jack_port_t of a @a port_id.
+ */
+jack_port_t * jack_port_by_id (jack_client_t *client,
+ jack_port_id_t port_id) JACK_OPTIONAL_WEAK_EXPORT;
+
+/*@}*/
+
+/**
+ * @defgroup TimeFunctions Handling time
+ * @{
+ *
+ * JACK time is in units of 'frames', according to the current sample rate.
+ * The absolute value of frame times is meaningless, frame times have meaning
+ * only relative to each other.
+ */
+
+/**
+ * @return the estimated time in frames that has passed since the JACK
+ * server began the current process cycle.
+ */
+jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the estimated current time in frames.
+ * This function is intended for use in other threads (not the process
+ * callback). The return value can be compared with the value of
+ * jack_last_frame_time to relate time in other threads to JACK time.
+ */
+jack_nframes_t jack_frame_time (const jack_client_t *) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the precise time at the start of the current process cycle.
+ * This function may only be used from the process callback, and can
+ * be used to interpret timestamps generated by jack_frame_time() in
+ * other threads with respect to the current process cycle.
+ *
+ * This is the only jack time function that returns exact time:
+ * when used during the process callback it always returns the same
+ * value (until the next process callback, where it will return
+ * that value + nframes, etc). The return value is guaranteed to be
+ * monotonic and linear in this fashion unless an xrun occurs.
+ * If an xrun occurs, clients must check this value again, as time
+ * may have advanced in a non-linear way (e.g. cycles may have been skipped).
+ */
+jack_nframes_t jack_last_frame_time (const jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * This function may only be used from the process callback.
+ * It provides the internal cycle timing information as used by
+ * most of the other time related functions. This allows the
+ * caller to map between frame counts and microseconds with full
+ * precision (i.e. without rounding frame times to integers),
+ * and also provides e.g. the microseconds time of the start of
+ * the current cycle directly (it has to be computed otherwise).
+ *
+ * If the return value is zero, the following information is
+ * provided in the variables pointed to by the arguments:
+ *
+ * current_frames: the frame time counter at the start of the
+ * current cycle, same as jack_last_frame_time().
+ * current_usecs: the microseconds time at the start of the
+ * current cycle.
+ * next_usecs: the microseconds time of the start of the next
+ * next cycle as computed by the DLL.
+ * period_usecs: the current best estimate of the period time in
+ * microseconds.
+ *
+ * NOTES:
+ *
+ * Because of the types used, all the returned values except period_usecs
+ * are unsigned. In computations mapping between frames and microseconds
+ * *signed* differences are required. The easiest way is to compute those
+ * separately and assign them to the appropriate signed variables,
+ * int32_t for frames and int64_t for usecs. See the implementation of
+ * jack_frames_to_time() and Jack_time_to_frames() for an example.
+ *
+ * Unless there was an xrun, skipped cycles, or the current cycle is the
+ * first after freewheeling or starting Jack, the value of current_usecs
+ * will always be the value of next_usecs of the previous cycle.
+ *
+ * The value of period_usecs will in general NOT be exactly equal to
+ * the difference of next_usecs and current_usecs. This is because to
+ * ensure stability of the DLL and continuity of the mapping, a fraction
+ * of the loop error must be included in next_usecs. For an accurate
+ * mapping between frames and microseconds, the difference of next_usecs
+ * and current_usecs should be used, and not period_usecs.
+ *
+ * @return zero if OK, non-zero otherwise.
+ */
+int jack_get_cycle_times(const jack_client_t *client,
+ jack_nframes_t *current_frames,
+ jack_time_t *current_usecs,
+ jack_time_t *next_usecs,
+ float *period_usecs) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the estimated time in microseconds of the specified frame time
+ */
+jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return the estimated time in frames for the specified system time.
+ */
+jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @return return JACK's current system time in microseconds,
+ * using the JACK clock source.
+ *
+ * The value returned is guaranteed to be monotonic, but not linear.
+ */
+jack_time_t jack_get_time(void) JACK_OPTIONAL_WEAK_EXPORT;
+
+/*@}*/
+
+/**
+ * @defgroup ErrorOutput Controlling error/information output
+ */
+/*@{*/
+
+/**
+ * Display JACK error message.
+ *
+ * Set via jack_set_error_function(), otherwise a JACK-provided
+ * default will print @a msg (plus a newline) to stderr.
+ *
+ * @param msg error message text (no newline at end).
+ */
+extern void (*jack_error_callback)(const char *msg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Set the @ref jack_error_callback for error message display.
+ * Set it to NULL to restore default_jack_error_callback function.
+ *
+ * The JACK library provides two built-in callbacks for this purpose:
+ * default_jack_error_callback() and silent_jack_error_callback().
+ */
+void jack_set_error_function (void (*func)(const char *)) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Display JACK info message.
+ *
+ * Set via jack_set_info_function(), otherwise a JACK-provided
+ * default will print @a msg (plus a newline) to stdout.
+ *
+ * @param msg info message text (no newline at end).
+ */
+extern void (*jack_info_callback)(const char *msg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Set the @ref jack_info_callback for info message display.
+ * Set it to NULL to restore default_jack_info_callback function.
+ *
+ * The JACK library provides two built-in callbacks for this purpose:
+ * default_jack_info_callback() and silent_jack_info_callback().
+ */
+void jack_set_info_function (void (*func)(const char *)) JACK_OPTIONAL_WEAK_EXPORT;
+
+/*@}*/
+
+/**
+ * The free function to be used on memory returned by jack_port_get_connections,
+ * jack_port_get_all_connections, jack_get_ports and jack_get_internal_client_name functions.
+ * This is MANDATORY on Windows when otherwise all nasty runtime version related crashes can occur.
+ * Developers are strongly encouraged to use this function instead of the standard "free" function in new code.
+ *
+ * @param ptr the memory pointer to be deallocated.
+ */
+void jack_free(void* ptr) JACK_OPTIONAL_WEAK_EXPORT;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __jack_h__ */
diff --git a/pipewire-jack/jack/jslist.h b/pipewire-jack/jack/jslist.h
new file mode 100644
index 0000000..3ec0ce9
--- /dev/null
+++ b/pipewire-jack/jack/jslist.h
@@ -0,0 +1,293 @@
+/*
+ Based on gslist.c from glib-1.2.9 (LGPL).
+
+ Adaption to JACK, Copyright (C) 2002 Kai Vehmanen.
+ - replaced use of gtypes with normal ANSI C types
+ - glib's memory allocation routines replaced with
+ malloc/free calls
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __jack_jslist_h__
+#define __jack_jslist_h__
+
+#include <stdlib.h>
+#include <jack/systemdeps.h>
+
+#ifdef sun
+#define __inline__
+#endif
+
+typedef struct _JSList JSList;
+
+typedef int (*JCompareFunc) (void* a, void* b);
+struct _JSList
+{
+ void *data;
+ JSList *next;
+};
+
+static __inline__
+JSList*
+jack_slist_alloc (void)
+{
+ JSList *new_list;
+
+ new_list = (JSList*)malloc(sizeof(JSList));
+ if (new_list) {
+ new_list->data = NULL;
+ new_list->next = NULL;
+ }
+
+ return new_list;
+}
+
+static __inline__
+JSList*
+jack_slist_prepend (JSList* list, void* data)
+{
+ JSList *new_list;
+
+ new_list = (JSList*)malloc(sizeof(JSList));
+ if (new_list) {
+ new_list->data = data;
+ new_list->next = list;
+ }
+
+ return new_list;
+}
+
+#define jack_slist_next(slist) ((slist) ? (((JSList *)(slist))->next) : NULL)
+static __inline__
+JSList*
+jack_slist_last (JSList *list)
+{
+ if (list) {
+ while (list->next)
+ list = list->next;
+ }
+
+ return list;
+}
+
+static __inline__
+JSList*
+jack_slist_remove_link (JSList *list,
+ JSList *link)
+{
+ JSList *tmp;
+ JSList *prev;
+
+ prev = NULL;
+ tmp = list;
+
+ while (tmp) {
+ if (tmp == link) {
+ if (prev)
+ prev->next = tmp->next;
+ if (list == tmp)
+ list = list->next;
+
+ tmp->next = NULL;
+ break;
+ }
+
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ return list;
+}
+
+static __inline__
+void
+jack_slist_free (JSList *list)
+{
+ while (list) {
+ JSList *next = list->next;
+ free(list);
+ list = next;
+ }
+}
+
+static __inline__
+void
+jack_slist_free_1 (JSList *list)
+{
+ if (list) {
+ free(list);
+ }
+}
+
+static __inline__
+JSList*
+jack_slist_remove (JSList *list,
+ void *data)
+{
+ JSList *tmp;
+ JSList *prev;
+
+ prev = NULL;
+ tmp = list;
+
+ while (tmp) {
+ if (tmp->data == data) {
+ if (prev)
+ prev->next = tmp->next;
+ if (list == tmp)
+ list = list->next;
+
+ tmp->next = NULL;
+ jack_slist_free (tmp);
+
+ break;
+ }
+
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ return list;
+}
+
+static __inline__
+unsigned int
+jack_slist_length (JSList *list)
+{
+ unsigned int length;
+
+ length = 0;
+ while (list) {
+ length++;
+ list = list->next;
+ }
+
+ return length;
+}
+
+static __inline__
+JSList*
+jack_slist_find (JSList *list,
+ void *data)
+{
+ while (list) {
+ if (list->data == data)
+ break;
+ list = list->next;
+ }
+
+ return list;
+}
+
+static __inline__
+JSList*
+jack_slist_copy (JSList *list)
+{
+ JSList *new_list = NULL;
+
+ if (list) {
+ JSList *last;
+
+ new_list = jack_slist_alloc ();
+ new_list->data = list->data;
+ last = new_list;
+ list = list->next;
+ while (list) {
+ last->next = jack_slist_alloc ();
+ last = last->next;
+ last->data = list->data;
+ list = list->next;
+ }
+ }
+
+ return new_list;
+}
+
+static __inline__
+JSList*
+jack_slist_append (JSList *list,
+ void *data)
+{
+ JSList *new_list;
+ JSList *last;
+
+ new_list = jack_slist_alloc ();
+ new_list->data = data;
+
+ if (list) {
+ last = jack_slist_last (list);
+ last->next = new_list;
+
+ return list;
+ } else
+ return new_list;
+}
+
+static __inline__
+JSList*
+jack_slist_sort_merge (JSList *l1,
+ JSList *l2,
+ JCompareFunc compare_func)
+{
+ JSList list, *l;
+
+ l = &list;
+
+ while (l1 && l2) {
+ if (compare_func(l1->data, l2->data) < 0) {
+ l = l->next = l1;
+ l1 = l1->next;
+ } else {
+ l = l->next = l2;
+ l2 = l2->next;
+ }
+ }
+ l->next = l1 ? l1 : l2;
+
+ return list.next;
+}
+
+static __inline__
+JSList*
+jack_slist_sort (JSList *list,
+ JCompareFunc compare_func)
+{
+ JSList *l1, *l2;
+
+ if (!list)
+ return NULL;
+ if (!list->next)
+ return list;
+
+ l1 = list;
+ l2 = list->next;
+
+ while ((l2 = l2->next) != NULL) {
+ if ((l2 = l2->next) == NULL)
+ break;
+ l1 = l1->next;
+ }
+ l2 = l1->next;
+ l1->next = NULL;
+
+ return jack_slist_sort_merge (jack_slist_sort (list, compare_func),
+ jack_slist_sort (l2, compare_func),
+ compare_func);
+}
+
+#endif /* __jack_jslist_h__ */
+
diff --git a/pipewire-jack/jack/metadata.h b/pipewire-jack/jack/metadata.h
new file mode 100644
index 0000000..75a4105
--- /dev/null
+++ b/pipewire-jack/jack/metadata.h
@@ -0,0 +1,322 @@
+/*
+ Copyright (C) 2011-2014 David Robillard
+ Copyright (C) 2013 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file jack/metadata.h
+ * @ingroup publicheader
+ * @brief JACK Metadata API
+ *
+ */
+
+#ifndef __jack_metadata_h__
+#define __jack_metadata_h__
+
+#include <jack/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup Metadata Metadata API.
+ * @{
+ */
+
+/**
+ * A single property (key:value pair).
+ *
+ * Although there is no semantics imposed on metadata keys and values, it is
+ * much less useful to use it to associate highly structured data with a port
+ * (or client), since this then implies the need for some (presumably
+ * library-based) code to parse the structure and be able to use it.
+ *
+ * The real goal of the metadata API is to be able to tag ports (and clients)
+ * with small amounts of data that is outside of the core JACK API but
+ * nevertheless useful.
+ */
+typedef struct {
+ /** The key of this property (URI string). */
+ const char* key;
+
+ /** The property value (null-terminated string). */
+ const char* data;
+
+ /**
+ * Type of data, either a MIME type or URI.
+ *
+ * If type is NULL or empty, the data is assumed to be a UTF-8 encoded
+ * string (text/plain). The data is a null-terminated string regardless of
+ * type, so values can always be copied, but clients should not try to
+ * interpret values of an unknown type.
+ *
+ * Example values:
+ * - image/png;base64 (base64 encoded PNG image)
+ * - http://www.w3.org/2001/XMLSchema#int (integer)
+ *
+ * Official types are preferred, but clients may use any syntactically
+ * valid MIME type (which start with a type and slash, like "text/...").
+ * If a URI type is used, it must be a complete absolute URI
+ * (which start with a scheme and colon, like "http:").
+ */
+ const char* type;
+} jack_property_t;
+
+/**
+ * Set a property on @p subject.
+ *
+ * See the above documentation for rules about @p subject and @p key.
+ * @param subject The subject to set the property on.
+ * @param key The key of the property.
+ * @param value The value of the property.
+ * @param type The type of the property. See the discussion of
+ * types in the definition of jack_property_t above.
+ * @return 0 on success.
+ */
+int
+jack_set_property(jack_client_t*,
+ jack_uuid_t subject,
+ const char* key,
+ const char* value,
+ const char* type);
+
+/**
+ * Get a property on @p subject.
+ *
+ * @param subject The subject to get the property from.
+ * @param key The key of the property.
+ * @param value Set to the value of the property if found, or NULL otherwise.
+ * The caller must free this value with jack_free().
+ * @param type The type of the property if set, or NULL. See the discussion
+ * of types in the definition of jack_property_t above.
+ * If non-null, the caller must free this value with jack_free().
+ *
+ * @return 0 on success, -1 if the @p subject has no @p key property.
+ */
+int
+jack_get_property(jack_uuid_t subject,
+ const char* key,
+ char** value,
+ char** type);
+
+/**
+ * A description of a subject (a set of properties).
+ */
+typedef struct {
+ jack_uuid_t subject; /**< Subject being described. */
+ uint32_t property_cnt; /**< Number of elements in "properties". */
+ jack_property_t* properties; /**< Array of properties. */
+ uint32_t property_size; /**< Private, do not use. */
+} jack_description_t;
+
+/**
+ * Free a description.
+ *
+ * @param desc a jack_description_t whose associated memory will all be released
+ * @param free_description_itself if non-zero, then @param desc will also be passed to free()
+ */
+void
+jack_free_description (jack_description_t* desc, int free_description_itself);
+
+/**
+ * Get a description of @p subject.
+ * @param subject The subject to get all properties of.
+ * @param desc Set to the description of subject if found, or NULL otherwise.
+ * The caller must free this value with jack_free_description().
+ * @return the number of properties, -1 if no @p subject with any properties exists.
+ */
+int
+jack_get_properties (jack_uuid_t subject,
+ jack_description_t* desc);
+
+/**
+ * Get descriptions for all subjects with metadata.
+ * @param descs Set to an array of descriptions.
+ * The caller must free each of these with jack_free_description(),
+ * and the array itself with jack_free().
+ * @return the number of descriptions, or -1 on error.
+ */
+int
+jack_get_all_properties (jack_description_t** descs);
+
+/**
+ * Remove a single property on a subject.
+ *
+ * @param client The JACK client making the request to remove the property.
+ * @param subject The subject to remove the property from.
+ * @param key The key of the property to be removed.
+ *
+ * @return 0 on success, -1 otherwise
+ */
+int jack_remove_property (jack_client_t* client, jack_uuid_t subject, const char* key);
+
+/**
+ * Remove all properties on a subject.
+ *
+ * @param client The JACK client making the request to remove some properties.
+ * @param subject The subject to remove all properties from.
+ *
+ * @return a count of the number of properties removed, or -1 on error.
+ */
+int jack_remove_properties (jack_client_t* client, jack_uuid_t subject);
+
+/**
+ * Remove all properties.
+ *
+ * WARNING!! This deletes all metadata managed by a running JACK server.
+ * Data lost cannot be recovered (though it can be recreated by new calls
+ * to jack_set_property()).
+ *
+ * @param client The JACK client making the request to remove all properties
+ *
+ * @return 0 on success, -1 otherwise
+ */
+int jack_remove_all_properties (jack_client_t* client);
+
+typedef enum {
+ PropertyCreated,
+ PropertyChanged,
+ PropertyDeleted
+} jack_property_change_t;
+
+/**
+ * Prototype for the client supplied function that is called by the
+ * engine anytime a property or properties have been modified.
+ *
+ * Note that when the key is empty, it means all properties have been
+ * modified. This is often used to indicate that the removal of all keys.
+ *
+ * @param subject The subject the change relates to, this can be either a client or port
+ * @param key The key of the modified property (URI string)
+ * @param change Wherever the key has been created, changed or deleted
+ * @param arg pointer to a client supplied structure
+ */
+typedef void (*JackPropertyChangeCallback)(jack_uuid_t subject,
+ const char* key,
+ jack_property_change_t change,
+ void* arg);
+
+/**
+ * Arrange for @p client to call @p callback whenever a property is created,
+ * changed or deleted.
+ *
+ * @param client the JACK client making the request
+ * @param callback the function to be invoked when a property change occurs
+ * @param arg the argument to be passed to @param callback when it is invoked
+ *
+ * @return 0 success, -1 otherwise.
+ */
+int jack_set_property_change_callback (jack_client_t* client,
+ JackPropertyChangeCallback callback,
+ void* arg);
+
+/**
+ * A value that identifies what the hardware port is connected to (an external
+ * device of some kind). Possible values might be "E-Piano" or "Master 2 Track".
+ */
+extern const char* JACK_METADATA_CONNECTED;
+
+/**
+ * The supported event types of an event port.
+ *
+ * This is a kludge around Jack only supporting MIDI, particularly for OSC.
+ * This property is a comma-separated list of event types, currently "MIDI" or
+ * "OSC". If this contains "OSC", the port may carry OSC bundles (first byte
+ * '#') or OSC messages (first byte '/'). Note that the "status byte" of both
+ * OSC events is not a valid MIDI status byte, so MIDI clients that check the
+ * status byte will gracefully ignore OSC messages if the user makes an
+ * inappropriate connection.
+ */
+extern const char* JACK_METADATA_EVENT_TYPES;
+
+/**
+ * A value that should be shown when attempting to identify the
+ * specific hardware outputs of a client. Typical values might be
+ * "ADAT1", "S/PDIF L" or "MADI 43".
+ */
+extern const char* JACK_METADATA_HARDWARE;
+
+/**
+ * A value with a MIME type of "image/png;base64" that is an encoding of an
+ * NxN (with 32 < N <= 128) image to be used when displaying a visual
+ * representation of that client or port.
+ */
+extern const char* JACK_METADATA_ICON_LARGE;
+
+/**
+ * The name of the icon for the subject (typically client).
+ *
+ * This is used for looking up icons on the system, possibly with many sizes or
+ * themes. Icons should be searched for according to the freedesktop Icon
+ *
+ * Theme Specification:
+ * https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
+ */
+extern const char* JACK_METADATA_ICON_NAME;
+
+/**
+ * A value with a MIME type of "image/png;base64" that is an encoding of an
+ * NxN (with N <=32) image to be used when displaying a visual representation
+ * of that client or port.
+ */
+extern const char* JACK_METADATA_ICON_SMALL;
+
+/**
+ * Order for a port.
+ *
+ * This is used to specify the best order to show ports in user interfaces.
+ * The value MUST be an integer. There are no other requirements, so there may
+ * be gaps in the orders for several ports. Applications should compare the
+ * orders of ports to determine their relative order, but must not assign any
+ * other relevance to order values.
+ *
+ * It is encouraged to use http://www.w3.org/2001/XMLSchema#int as the type.
+ */
+extern const char* JACK_METADATA_ORDER;
+
+/**
+ * A value that should be shown to the user when displaying a port to the user,
+ * unless the user has explicitly overridden that a request to show the port
+ * name, or some other key value.
+ */
+extern const char* JACK_METADATA_PRETTY_NAME;
+
+/**
+ */
+extern const char* JACK_METADATA_PORT_GROUP;
+
+/**
+ * The type of an audio signal.
+ *
+ * This property allows audio ports to be tagged with a "meaning". The value
+ * is a simple string. Currently, the only type is "CV", for "control voltage"
+ * ports. Hosts SHOULD be take care to not treat CV ports as audible and send
+ * their output directly to speakers. In particular, CV ports are not
+ * necessarily periodic at all and may have very high DC.
+ */
+extern const char* JACK_METADATA_SIGNAL_TYPE;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+} /* namespace */
+#endif
+
+#endif /* __jack_metadata_h__ */
diff --git a/pipewire-jack/jack/midiport.h b/pipewire-jack/jack/midiport.h
new file mode 100644
index 0000000..0ae5e79
--- /dev/null
+++ b/pipewire-jack/jack/midiport.h
@@ -0,0 +1,197 @@
+/*
+ Copyright (C) 2004 Ian Esten
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+
+#ifndef __JACK_MIDIPORT_H
+#define __JACK_MIDIPORT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <jack/weakmacros.h>
+#include <jack/types.h>
+#include <stdlib.h>
+
+
+/** Type for raw event data contained in @ref jack_midi_event_t. */
+typedef unsigned char jack_midi_data_t;
+
+
+/** A Jack MIDI event. */
+typedef struct _jack_midi_event
+{
+ jack_nframes_t time; /**< Sample index at which event is valid */
+ size_t size; /**< Number of bytes of data in \a buffer */
+ jack_midi_data_t *buffer; /**< Raw MIDI data */
+} jack_midi_event_t;
+
+
+/**
+ * @defgroup MIDIAPI Reading and writing MIDI data
+ * @{
+ */
+
+/** Get number of events in a port buffer.
+ *
+ * @param port_buffer Port buffer from which to retrieve event.
+ * @return number of events inside @a port_buffer
+ */
+uint32_t
+jack_midi_get_event_count(void* port_buffer) JACK_OPTIONAL_WEAK_EXPORT;
+
+
+/** Get a MIDI event from an event port buffer.
+ *
+ * Jack MIDI is normalised, the MIDI event returned by this function is
+ * guaranteed to be a complete MIDI event (the status byte will always be
+ * present, and no realtime events will interspersed with the event).
+ *
+ * This rule does not apply to System Exclusive MIDI messages
+ * since they can be of arbitrary length.
+ * To maintain smooth realtime operation such events CAN be delivered
+ * as multiple, non-normalised events.
+ * The maximum size of one event "chunk" depends on the MIDI backend in use.
+ * For example the midiseq driver will create chunks of 256 bytes.
+ * The first SysEx "chunked" event starts with 0xF0 and the last
+ * delivered chunk ends with 0xF7.
+ * To receive the full SysEx message, a caller of jack_midi_event_get()
+ * must concatenate chunks until a chunk ends with 0xF7.
+ *
+ * @param event Event structure to store retrieved event in.
+ * @param port_buffer Port buffer from which to retrieve event.
+ * @param event_index Index of event to retrieve.
+ * @return 0 on success, ENODATA if buffer is empty.
+ */
+int
+jack_midi_event_get(jack_midi_event_t *event,
+ void *port_buffer,
+ uint32_t event_index) JACK_OPTIONAL_WEAK_EXPORT;
+
+
+/** Clear an event buffer.
+ *
+ * This should be called at the beginning of each process cycle before calling
+ * @ref jack_midi_event_reserve or @ref jack_midi_event_write. This
+ * function may not be called on an input port's buffer.
+ *
+ * @param port_buffer Port buffer to clear (must be an output port buffer).
+ */
+void
+jack_midi_clear_buffer(void *port_buffer) JACK_OPTIONAL_WEAK_EXPORT;
+
+/** Reset an event buffer (from data allocated outside of JACK).
+ *
+ * This should be called at the beginning of each process cycle before calling
+ * @ref jack_midi_event_reserve or @ref jack_midi_event_write. This
+ * function may not be called on an input port's buffer.
+ *
+ * @deprecated Please use jack_midi_clear_buffer().
+ *
+ * @param port_buffer Port buffer to reset.
+ */
+void
+jack_midi_reset_buffer(void *port_buffer) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+
+/** Get the size of the largest event that can be stored by the port.
+ *
+ * This function returns the current space available, taking into account
+ * events already stored in the port.
+ *
+ * @param port_buffer Port buffer to check size of.
+ */
+size_t
+jack_midi_max_event_size(void* port_buffer) JACK_OPTIONAL_WEAK_EXPORT;
+
+
+/** Allocate space for an event to be written to an event port buffer.
+ *
+ * Clients are to write the actual event data to be written starting at the
+ * pointer returned by this function. Clients must not write more than
+ * @a data_size bytes into this buffer. Clients must write normalised
+ * MIDI data to the port - no running status and no (1-byte) realtime
+ * messages interspersed with other messages (realtime messages are fine
+ * when they occur on their own, like other messages).
+ *
+ * Events must be written in order, sorted by their sample offsets.
+ * JACK will not sort the events for you, and will refuse to store
+ * out-of-order events.
+ *
+ * @param port_buffer Buffer to write event to.
+ * @param time Sample offset of event.
+ * @param data_size Length of event's raw data in bytes.
+ * @return Pointer to the beginning of the reserved event's data buffer, or
+ * NULL on error (ie not enough space).
+ */
+jack_midi_data_t*
+jack_midi_event_reserve(void *port_buffer,
+ jack_nframes_t time,
+ size_t data_size) JACK_OPTIONAL_WEAK_EXPORT;
+
+
+/** Write an event into an event port buffer.
+ *
+ * This function is simply a wrapper for @ref jack_midi_event_reserve
+ * which writes the event data into the space reserved in the buffer.
+ *
+ * Clients must not write more than
+ * @a data_size bytes into this buffer. Clients must write normalised
+ * MIDI data to the port - no running status and no (1-byte) realtime
+ * messages interspersed with other messages (realtime messages are fine
+ * when they occur on their own, like other messages).
+ *
+ * Events must be written in order, sorted by their sample offsets.
+ * JACK will not sort the events for you, and will refuse to store
+ * out-of-order events.
+ *
+ * @param port_buffer Buffer to write event to.
+ * @param time Sample offset of event.
+ * @param data Message data to be written.
+ * @param data_size Length of @a data in bytes.
+ * @return 0 on success, ENOBUFS if there's not enough space in buffer for event.
+ */
+int
+jack_midi_event_write(void *port_buffer,
+ jack_nframes_t time,
+ const jack_midi_data_t *data,
+ size_t data_size) JACK_OPTIONAL_WEAK_EXPORT;
+
+
+/** Get the number of events that could not be written to @a port_buffer.
+ *
+ * This function returning a non-zero value implies @a port_buffer is full.
+ * Currently the only way this can happen is if events are lost on port mixdown.
+ *
+ * @param port_buffer Port to receive count for.
+ * @returns Number of events that could not be written to @a port_buffer.
+ */
+uint32_t
+jack_midi_get_lost_event_count(void *port_buffer) JACK_OPTIONAL_WEAK_EXPORT;
+
+/*@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __JACK_MIDIPORT_H */
+
+
diff --git a/pipewire-jack/jack/net.h b/pipewire-jack/jack/net.h
new file mode 100644
index 0000000..3f214be
--- /dev/null
+++ b/pipewire-jack/jack/net.h
@@ -0,0 +1,429 @@
+/*
+ Copyright (C) 2009-2010 Grame
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __net_h__
+#define __net_h__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <jack/systemdeps.h>
+#include <jack/types.h>
+#include <jack/weakmacros.h>
+
+#define DEFAULT_MULTICAST_IP "225.3.19.154"
+#define DEFAULT_PORT 19000
+#define DEFAULT_MTU 1500
+#define MASTER_NAME_SIZE 256
+
+// Possible error codes
+
+#define NO_ERROR 0
+#define SOCKET_ERROR -1
+#define SYNC_PACKET_ERROR -2
+#define DATA_PACKET_ERROR -3
+
+#define RESTART_CB_API 1
+
+enum JackNetEncoder {
+
+ JackFloatEncoder = 0, // samples are transmitted as float
+ JackIntEncoder = 1, // samples are transmitted as 16 bits integer
+ JackCeltEncoder = 2, // samples are transmitted using CELT codec (http://www.celt-codec.org/)
+ JackOpusEncoder = 3, // samples are transmitted using OPUS codec (http://www.opus-codec.org/)
+};
+
+typedef struct {
+
+ int audio_input; // from master or to slave (-1 to take master audio physical inputs)
+ int audio_output; // to master or from slave (-1 to take master audio physical outputs)
+ int midi_input; // from master or to slave (-1 to take master MIDI physical inputs)
+ int midi_output; // to master or from slave (-1 to take master MIDI physical outputs)
+ int mtu; // network Maximum Transmission Unit
+ int time_out; // in second, -1 means infinite
+ int encoder; // encoder type (one of JackNetEncoder)
+ int kbps; // KB per second for CELT or OPUS codec
+ int latency; // network latency in number of buffers
+
+} jack_slave_t;
+
+typedef struct {
+
+ int audio_input; // master audio physical outputs (-1 to take slave wanted audio inputs)
+ int audio_output; // master audio physical inputs (-1 to take slave wanted audio outputs)
+ int midi_input; // master MIDI physical outputs (-1 to take slave wanted MIDI inputs)
+ int midi_output; // master MIDI physical inputs (-1 to take slave wanted MIDI outputs)
+ jack_nframes_t buffer_size; // master buffer size
+ jack_nframes_t sample_rate; // master sample rate
+ char master_name[MASTER_NAME_SIZE]; // master machine name
+ int time_out; // in second, -1 means infinite
+ int partial_cycle; // if 'true', partial buffers will be used
+
+} jack_master_t;
+
+/**
+ * jack_net_slave_t is an opaque type. You may only access it using the
+ * API provided.
+ */
+typedef struct _jack_net_slave jack_net_slave_t;
+
+ /**
+ * Open a network connection with the master machine.
+ *
+ * @param ip the multicast address of the master
+ * @param port the connection port
+ * @param name the JACK client name
+ * @param request a connection request structure
+ * @param result a connection result structure
+ *
+ * @return Opaque net handle if successful or NULL in case of error.
+ */
+jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name, jack_slave_t* request, jack_master_t* result);
+
+/**
+ * Close the network connection with the master machine.
+ *
+ * @param net the network connection to be closed
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_net_slave_close(jack_net_slave_t* net);
+
+/**
+ * Prototype for Process callback.
+ *
+ * @param nframes buffer size
+ * @param audio_input number of audio inputs
+ * @param audio_input_buffer an array of audio input buffers (from master)
+ * @param midi_input number of MIDI inputs
+ * @param midi_input_buffer an array of MIDI input buffers (from master)
+ * @param audio_output number of audio outputs
+ * @param audio_output_buffer an array of audio output buffers (to master)
+ * @param midi_output number of MIDI outputs
+ * @param midi_output_buffer an array of MIDI output buffers (to master)
+ * @param arg pointer to a client supplied structure supplied by jack_set_net_process_callback()
+ *
+ * @return zero on success, non-zero on error
+ */
+typedef int (* JackNetSlaveProcessCallback) (jack_nframes_t buffer_size,
+ int audio_input,
+ float** audio_input_buffer,
+ int midi_input,
+ void** midi_input_buffer,
+ int audio_output,
+ float** audio_output_buffer,
+ int midi_output,
+ void** midi_output_buffer,
+ void* data);
+
+/**
+ * Set network process callback.
+ *
+ * @param net the network connection
+ * @param net_callback the process callback
+ * @param arg pointer to a client supplied structure
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_net_slave_process_callback(jack_net_slave_t * net, JackNetSlaveProcessCallback net_callback, void *arg);
+
+/**
+ * Start processing thread, the net_callback will start to be called.
+ *
+ * @param net the network connection
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_net_slave_activate(jack_net_slave_t* net);
+
+/**
+ * Stop processing thread.
+ *
+ * @param net the network connection
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_net_slave_deactivate(jack_net_slave_t* net);
+
+/**
+ * Test if slave is still active.
+ *
+ * @param net the network connection
+ *
+ * @return a boolean
+ */
+int jack_net_slave_is_active(jack_net_slave_t* net);
+
+/**
+ * Prototype for BufferSize callback.
+ *
+ * @param nframes buffer size
+ * @param arg pointer to a client supplied structure supplied by jack_set_net_buffer_size_callback()
+ *
+ * @return zero on success, non-zero on error
+ */
+typedef int (*JackNetSlaveBufferSizeCallback)(jack_nframes_t nframes, void *arg);
+
+/**
+ * Set network buffer size callback.
+ *
+ * @param net the network connection
+ * @param bufsize_callback the buffer size callback
+ * @param arg pointer to a client supplied structure
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_net_slave_buffer_size_callback(jack_net_slave_t *net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg);
+
+/**
+ * Prototype for SampleRate callback.
+ *
+ * @param nframes sample rate
+ * @param arg pointer to a client supplied structure supplied by jack_set_net_sample_rate_callback()
+ *
+ * @return zero on success, non-zero on error
+ */
+typedef int (*JackNetSlaveSampleRateCallback)(jack_nframes_t nframes, void *arg);
+
+/**
+ * Set network sample rate callback.
+ *
+ * @param net the network connection
+ * @param samplerate_callback the sample rate callback
+ * @param arg pointer to a client supplied structure
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_net_slave_sample_rate_callback(jack_net_slave_t *net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg);
+
+/**
+ * Prototype for server Shutdown callback (if not set, the client will just restart, waiting for an available master again).
+ *
+ * @param arg pointer to a client supplied structure supplied by jack_set_net_shutdown_callback()
+ */
+typedef void (*JackNetSlaveShutdownCallback)(void* arg);
+
+/**
+ * Set network shutdown callback.
+ *
+ * @param net the network connection
+ * @param shutdown_callback the shutdown callback
+ * @param arg pointer to a client supplied structure
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_net_slave_shutdown_callback(jack_net_slave_t *net, JackNetSlaveShutdownCallback shutdown_callback, void *arg) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * Prototype for server Restart callback : this is the new preferable way to be notified when the master has disappeared.
+ * The client may want to retry connecting a certain number of time (which will be done using the time_out value given in jack_net_slave_open)
+ * by returning 0. Otherwise returning a non-zero error code will definitively close the connection
+ * (and jack_net_slave_is_active will later on return false).
+ * If both Shutdown and Restart are supplied, Restart callback will be used.
+ *
+ * @param arg pointer to a client supplied structure supplied by jack_set_net_restart_callback()
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+typedef int (*JackNetSlaveRestartCallback)(void* arg);
+
+/**
+ * Set network restart callback.
+ *
+ * @param net the network connection
+ * @param restart_callback the shutdown callback
+ * @param arg pointer to a client supplied structure
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_net_slave_restart_callback(jack_net_slave_t *net, JackNetSlaveRestartCallback restart_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Prototype for server Error callback.
+ *
+ * @param error_code an error code (see "Possible error codes")
+ * @param arg pointer to a client supplied structure supplied by jack_set_net_error_callback()
+ */
+typedef void (*JackNetSlaveErrorCallback) (int error_code, void* arg);
+
+/**
+ * Set error restart callback.
+ *
+ * @param net the network connection
+ * @param error_callback the error callback
+ * @param arg pointer to a client supplied structure
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_net_slave_error_callback(jack_net_slave_t *net, JackNetSlaveErrorCallback error_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * jack_net_master_t is an opaque type, you may only access it using the API provided.
+ */
+typedef struct _jack_net_master jack_net_master_t;
+
+ /**
+ * Open a network connection with the slave machine.
+ *
+ * @param ip the multicast address of the master
+ * @param port the connection port
+ * @param request a connection request structure
+ * @param result a connection result structure
+ *
+ * @return Opaque net handle if successful or NULL in case of error.
+ */
+jack_net_master_t* jack_net_master_open(const char* ip, int port, jack_master_t* request, jack_slave_t* result);
+
+/**
+ * Close the network connection with the slave machine.
+ *
+ * @param net the network connection to be closed
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_net_master_close(jack_net_master_t* net);
+
+/**
+ * Receive sync and data from the network (complete buffer).
+ *
+ * @param net the network connection
+ * @param audio_input number of audio inputs
+ * @param audio_input_buffer an array of audio input buffers
+ * @param midi_input number of MIDI inputs
+ * @param midi_input_buffer an array of MIDI input buffers
+ *
+ * @return zero on success, non-zero on error
+ */
+int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer);
+
+/**
+ * Receive sync and data from the network (incomplete buffer).
+ *
+ * @param net the network connection
+ * @param audio_input number of audio inputs
+ * @param audio_input_buffer an array of audio input buffers
+ * @param midi_input number of MIDI inputs
+ * @param midi_input_buffer an array of MIDI input buffers
+ * @param frames the number of frames to receive
+ *
+ * @return zero on success, non-zero on error
+ */
+int jack_net_master_recv_slice(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int frames);
+
+/**
+ * Send sync and data to the network (complete buffer).
+ *
+ * @param net the network connection
+ * @param audio_output number of audio outputs
+ * @param audio_output_buffer an array of audio output buffers
+ * @param midi_output number of MIDI outputs
+ * @param midi_output_buffer an array of MIDI output buffers
+ *
+ * @return zero on success, non-zero on error
+ */
+int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer);
+
+/**
+ * Send sync and data to the network (incomplete buffer).
+ *
+ * @param net the network connection
+ * @param audio_output number of audio outputs
+ * @param audio_output_buffer an array of audio output buffers
+ * @param midi_output number of MIDI outputs
+ * @param midi_output_buffer an array of MIDI output buffers
+ * @param frames the number of frames to send
+ *
+ * @return zero on success, non-zero on error
+ */
+int jack_net_master_send_slice(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, int frames);
+
+// Experimental Adapter API
+
+/**
+ * jack_adapter_t is an opaque type, you may only access it using the API provided.
+ */
+typedef struct _jack_adapter jack_adapter_t;
+
+/**
+ * Create an adapter.
+ *
+ * @param input number of audio inputs
+ * @param output of audio outputs
+ * @param host_buffer_size the host buffer size in frames
+ * @param host_sample_rate the host buffer sample rate
+ * @param adapted_buffer_size the adapted buffer size in frames
+ * @param adapted_sample_rate the adapted buffer sample rate
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+jack_adapter_t* jack_create_adapter(int input, int output,
+ jack_nframes_t host_buffer_size,
+ jack_nframes_t host_sample_rate,
+ jack_nframes_t adapted_buffer_size,
+ jack_nframes_t adapted_sample_rate);
+
+/**
+ * Destroy an adapter.
+ *
+ * @param adapter the adapter to be destroyed
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_destroy_adapter(jack_adapter_t* adapter);
+
+/**
+ * Flush internal state of an adapter.
+ *
+ * @param adapter the adapter to be flushed
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+void jack_flush_adapter(jack_adapter_t* adapter);
+
+/**
+ * Push input to and pull output from adapter ringbuffer.
+ *
+ * @param adapter the adapter
+ * @param input an array of audio input buffers
+ * @param output an array of audio output buffers
+ * @param frames number of frames
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames);
+
+/**
+ * Pull input from and push output to adapter ringbuffer.
+ *
+ * @param adapter the adapter
+ * @param input an array of audio input buffers
+ * @param output an array of audio output buffers
+ * @param frames number of frames
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __net_h__ */
diff --git a/pipewire-jack/jack/ringbuffer.h b/pipewire-jack/jack/ringbuffer.h
new file mode 100644
index 0000000..5204383
--- /dev/null
+++ b/pipewire-jack/jack/ringbuffer.h
@@ -0,0 +1,243 @@
+/*
+ Copyright (C) 2000 Paul Davis
+ Copyright (C) 2003 Rohan Drape
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef _RINGBUFFER_H
+#define _RINGBUFFER_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <sys/types.h>
+
+/** @file ringbuffer.h
+ *
+ * A set of library functions to make lock-free ringbuffers available
+ * to JACK clients. The `capture_client.c' (in the example_clients
+ * directory) is a fully functioning user of this API.
+ *
+ * The key attribute of a ringbuffer is that it can be safely accessed
+ * by two threads simultaneously -- one reading from the buffer and
+ * the other writing to it -- without using any synchronization or
+ * mutual exclusion primitives. For this to work correctly, there can
+ * only be a single reader and a single writer thread. Their
+ * identities cannot be interchanged.
+ */
+
+typedef struct {
+ char *buf;
+ size_t len;
+}
+jack_ringbuffer_data_t ;
+
+typedef struct {
+ char *buf;
+ volatile size_t write_ptr;
+ volatile size_t read_ptr;
+ size_t size;
+ size_t size_mask;
+ int mlocked;
+}
+jack_ringbuffer_t ;
+
+/**
+ * Allocates a ringbuffer data structure of a specified size. The
+ * caller must arrange for a call to jack_ringbuffer_free() to release
+ * the memory associated with the ringbuffer.
+ *
+ * @param sz the ringbuffer size in bytes.
+ *
+ * @return a pointer to a new jack_ringbuffer_t, if successful; NULL
+ * otherwise.
+ */
+jack_ringbuffer_t *jack_ringbuffer_create(size_t sz);
+
+/**
+ * Frees the ringbuffer data structure allocated by an earlier call to
+ * jack_ringbuffer_create().
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ */
+void jack_ringbuffer_free(jack_ringbuffer_t *rb);
+
+/**
+ * Fill a data structure with a description of the current readable
+ * data held in the ringbuffer. This description is returned in a two
+ * element array of jack_ringbuffer_data_t. Two elements are needed
+ * because the data to be read may be split across the end of the
+ * ringbuffer.
+ *
+ * The first element will always contain a valid @a len field, which
+ * may be zero or greater. If the @a len field is non-zero, then data
+ * can be read in a contiguous fashion using the address given in the
+ * corresponding @a buf field.
+ *
+ * If the second element has a non-zero @a len field, then a second
+ * contiguous stretch of data can be read from the address given in
+ * its corresponding @a buf field.
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ * @param vec a pointer to a 2 element array of jack_ringbuffer_data_t.
+ *
+ */
+void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb,
+ jack_ringbuffer_data_t *vec);
+
+/**
+ * Fill a data structure with a description of the current writable
+ * space in the ringbuffer. The description is returned in a two
+ * element array of jack_ringbuffer_data_t. Two elements are needed
+ * because the space available for writing may be split across the end
+ * of the ringbuffer.
+ *
+ * The first element will always contain a valid @a len field, which
+ * may be zero or greater. If the @a len field is non-zero, then data
+ * can be written in a contiguous fashion using the address given in
+ * the corresponding @a buf field.
+ *
+ * If the second element has a non-zero @a len field, then a second
+ * contiguous stretch of data can be written to the address given in
+ * the corresponding @a buf field.
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ * @param vec a pointer to a 2 element array of jack_ringbuffer_data_t.
+ */
+void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb,
+ jack_ringbuffer_data_t *vec);
+
+/**
+ * Read data from the ringbuffer.
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ * @param dest a pointer to a buffer where data read from the
+ * ringbuffer will go.
+ * @param cnt the number of bytes to read.
+ *
+ * @return the number of bytes read, which may range from 0 to cnt.
+ */
+size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt);
+
+/**
+ * Read data from the ringbuffer. Opposed to jack_ringbuffer_read()
+ * this function does not move the read pointer. Thus it's
+ * a convenient way to inspect data in the ringbuffer in a
+ * continuous fashion. The price is that the data is copied
+ * into a user provided buffer. For "raw" non-copy inspection
+ * of the data in the ringbuffer use jack_ringbuffer_get_read_vector().
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ * @param dest a pointer to a buffer where data read from the
+ * ringbuffer will go.
+ * @param cnt the number of bytes to read.
+ *
+ * @return the number of bytes read, which may range from 0 to cnt.
+ */
+size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt);
+
+/**
+ * Advance the read pointer.
+ *
+ * After data have been read from the ringbuffer using the pointers
+ * returned by jack_ringbuffer_get_read_vector(), use this function to
+ * advance the buffer pointers, making that space available for future
+ * write operations.
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ * @param cnt the number of bytes read.
+ */
+void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt);
+
+/**
+ * Return the number of bytes available for reading.
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ *
+ * @return the number of bytes available to read.
+ */
+size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb);
+
+/**
+ * Lock a ringbuffer data block into memory.
+ *
+ * Uses the mlock() system call. This is not a realtime operation.
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ */
+int jack_ringbuffer_mlock(jack_ringbuffer_t *rb);
+
+/**
+ * Reset the read and write pointers, making an empty buffer.
+ *
+ * This is not thread safe.
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ */
+void jack_ringbuffer_reset(jack_ringbuffer_t *rb);
+
+/**
+ * Reset the internal "available" size, and read and write pointers, making an empty buffer.
+ *
+ * This is not thread safe.
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ * @param sz the new size, that must be less than allocated size.
+ */
+void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz);
+
+/**
+ * Write data into the ringbuffer.
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ * @param src a pointer to the data to be written to the ringbuffer.
+ * @param cnt the number of bytes to write.
+ *
+ * @return the number of bytes write, which may range from 0 to cnt
+ */
+size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src,
+ size_t cnt);
+
+/**
+ * Advance the write pointer.
+ *
+ * After data have been written the ringbuffer using the pointers
+ * returned by jack_ringbuffer_get_write_vector(), use this function
+ * to advance the buffer pointer, making the data available for future
+ * read operations.
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ * @param cnt the number of bytes written.
+ */
+void jack_ringbuffer_write_advance(jack_ringbuffer_t *rb, size_t cnt);
+
+/**
+ * Return the number of bytes available for writing.
+ *
+ * @param rb a pointer to the ringbuffer structure.
+ *
+ * @return the amount of free space (in bytes) available for writing.
+ */
+size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pipewire-jack/jack/session.h b/pipewire-jack/jack/session.h
new file mode 100644
index 0000000..1198a09
--- /dev/null
+++ b/pipewire-jack/jack/session.h
@@ -0,0 +1,302 @@
+/*
+ Copyright (C) 2001 Paul Davis
+ Copyright (C) 2004 Jack O'Quin
+ Copyright (C) 2010 Torben Hohn
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __jack_session_h__
+#define __jack_session_h__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <jack/types.h>
+#include <jack/weakmacros.h>
+
+/**
+ * @defgroup SessionClientFunctions Session API for clients.
+ *
+ * @deprecated Use of JACK-Session is currently deprecated and unsupported.
+ * JACK developers recommend the use of NSM instead.
+ * See https://github.com/linuxaudio/new-session-manager
+ * @{
+ */
+
+
+/**
+ * Session event type.
+ *
+ * If a client can't save templates, i might just do a normal save.
+ *
+ * There is no "quit without saving" event because a client might refuse to
+ * quit when it has unsaved data, but other clients may have already quit.
+ * This results in too much confusion, so it is unsupported.
+ */
+enum JackSessionEventType {
+ /**
+ * Save the session completely.
+ *
+ * The client may save references to data outside the provided directory,
+ * but it must do so by creating a link inside the provided directory and
+ * referring to that in any save files. The client must not refer to data
+ * files outside the provided directory directly in save files, because
+ * this makes it impossible for the session manager to create a session
+ * archive for distribution or archival.
+ */
+ JackSessionSave = 1,
+
+ /**
+ * Save the session completely, then quit.
+ *
+ * The rules for saving are exactly the same as for JackSessionSave.
+ */
+ JackSessionSaveAndQuit = 2,
+
+ /**
+ * Save a session template.
+ *
+ * A session template is a "skeleton" of the session, but without any data.
+ * Clients must save a session that, when restored, will create the same
+ * ports as a full save would have. However, the actual data contained in
+ * the session may not be saved (e.g. a DAW would create the necessary
+ * tracks, but not save the actual recorded data).
+ */
+ JackSessionSaveTemplate = 3
+};
+
+typedef enum JackSessionEventType jack_session_event_type_t;
+
+/**
+ * @ref jack_session_flags_t bits
+ */
+enum JackSessionFlags {
+ /**
+ * An error occurred while saving.
+ */
+ JackSessionSaveError = 0x01,
+
+ /**
+ * Client needs to be run in a terminal.
+ */
+ JackSessionNeedTerminal = 0x02
+};
+
+/**
+ * Session flags.
+ */
+typedef enum JackSessionFlags jack_session_flags_t;
+
+struct _jack_session_event {
+ /**
+ * The type of this session event.
+ */
+ jack_session_event_type_t type;
+
+ /**
+ * Session directory path, with trailing separator.
+ *
+ * This directory is exclusive to the client; when saving the client may
+ * create any files it likes in this directory.
+ */
+ const char *session_dir;
+
+ /**
+ * Client UUID which must be passed to jack_client_open on session load.
+ *
+ * The client can specify this in the returned command line, or save it
+ * in a state file within the session directory.
+ */
+ const char *client_uuid;
+
+ /**
+ * Reply (set by client): the command line needed to restore the client.
+ *
+ * This is a platform dependent command line. It must contain
+ * ${SESSION_DIR} instead of the actual session directory path. More
+ * generally, just as in session files, clients should not include any
+ * paths outside the session directory here as this makes
+ * archival/distribution impossible.
+ *
+ * This field is set to NULL by Jack when the event is delivered to the
+ * client. The client must set to allocated memory that is safe to
+ * free(). This memory will be freed by jack_session_event_free.
+ */
+ char *command_line;
+
+ /**
+ * Reply (set by client): Session flags.
+ */
+ jack_session_flags_t flags;
+
+ /**
+ * Future flags. Set to zero for now.
+ */
+ uint32_t future;
+};
+
+typedef struct _jack_session_event jack_session_event_t;
+
+/**
+ * Prototype for the client supplied function that is called
+ * whenever a session notification is sent via jack_session_notify().
+ *
+ * Ownership of the memory of @a event is passed to the application.
+ * It must be freed using jack_session_event_free when it's not used anymore.
+ *
+ * The client must promptly call jack_session_reply for this event.
+ *
+ * @deprecated Use of JACK-Session is currently deprecated and unsupported.
+ * JACK developers recommend the use of NSM instead.
+ * See https://github.com/linuxaudio/new-session-manager
+ *
+ * @param event The event structure.
+ * @param arg Pointer to a client supplied structure.
+ */
+typedef void (*JackSessionCallback)(jack_session_event_t *event,
+ void *arg);
+
+/**
+ * Tell the JACK server to call @a session_callback when a session event
+ * is to be delivered.
+ *
+ * setting more than one session_callback per process is probably a design
+ * error. if you have a multiclient application its more sensible to create
+ * a jack_client with only a session callback set.
+ *
+ * @deprecated Use of JACK-Session is currently deprecated and unsupported.
+ * JACK developers recommend the use of NSM instead.
+ * See https://github.com/linuxaudio/new-session-manager
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_set_session_callback (jack_client_t *client,
+ JackSessionCallback session_callback,
+ void *arg) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * Reply to a session event.
+ *
+ * This can either be called directly from the callback, or later from a
+ * different thread. For example, it is possible to push the event through a
+ * queue and execute the save code from the GUI thread.
+ *
+ * @deprecated Use of JACK-Session is currently deprecated and unsupported.
+ * JACK developers recommend the use of NSM instead.
+ * See https://github.com/linuxaudio/new-session-manager
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int jack_session_reply (jack_client_t *client,
+ jack_session_event_t *event) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+
+/**
+ * Free memory used by a jack_session_event_t.
+ *
+ * This also frees the memory used by the command_line pointer, if its non NULL.
+ *
+ * @deprecated Use of JACK-Session is currently deprecated and unsupported.
+ * JACK developers recommend the use of NSM instead.
+ * See https://github.com/linuxaudio/new-session-manager
+ */
+void jack_session_event_free (jack_session_event_t *event) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+
+/**
+ * Get the assigned uuid for client.
+ * Safe to call from callback and all other threads.
+ *
+ * The caller is responsible for calling jack_free(3) on any non-NULL
+ * returned value.
+ */
+char *jack_client_get_uuid (jack_client_t *client) JACK_WEAK_EXPORT;
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup JackSessionManagerAPI API for a session manager.
+ *
+ * @{
+ */
+
+typedef struct {
+ const char *uuid;
+ const char *client_name;
+ const char *command;
+ jack_session_flags_t flags;
+} jack_session_command_t;
+
+/**
+ * Send an event to all clients listening for session callbacks.
+ *
+ * The returned strings of the clients are accumulated and returned as an array
+ * of jack_session_command_t. its terminated by ret[i].uuid == NULL target ==
+ * NULL means send to all interested clients. otherwise a clientname
+ */
+jack_session_command_t *jack_session_notify (
+ jack_client_t* client,
+ const char *target,
+ jack_session_event_type_t type,
+ const char *path) JACK_WEAK_EXPORT;
+
+/**
+ * Free the memory allocated by a session command.
+ *
+ * @deprecated Use of JACK-Session is currently deprecated and unsupported.
+ * JACK developers recommend the use of NSM instead.
+ * See https://github.com/linuxaudio/new-session-manager
+ */
+void jack_session_commands_free (jack_session_command_t *cmds) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * Reserve a client name and associate it with a UUID.
+ *
+ * When a client later calls jack_client_open() and specifies the UUID, jackd
+ * will assign the reserved name. This allows a session manager to know in
+ * advance under which client name its managed clients will appear.
+ *
+ * @return 0 on success, otherwise a non-zero error code
+ */
+int
+jack_reserve_client_name (jack_client_t *client,
+ const char *name,
+ const char *uuid) JACK_WEAK_EXPORT;
+
+/**
+ * Find out whether a client has set up a session callback.
+ *
+ * @deprecated Use of JACK-Session is currently deprecated and unsupported.
+ * JACK developers recommend the use of NSM instead.
+ * See https://github.com/linuxaudio/new-session-manager
+ *
+ * @return 0 when the client has no session callback, 1 when it has one.
+ * -1 on error.
+ */
+int
+jack_client_has_session_callback (jack_client_t *client, const char *client_name) JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/pipewire-jack/jack/statistics.h b/pipewire-jack/jack/statistics.h
new file mode 100644
index 0000000..28c270d
--- /dev/null
+++ b/pipewire-jack/jack/statistics.h
@@ -0,0 +1,57 @@
+/*
+* Copyright (C) 2004 Rui Nuno Capela, Lee Revell
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public License
+* as published by the Free Software Foundation; either version 2.1
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this program; if not, write to the Free
+* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+* 02111-1307, USA.
+*
+*/
+
+#ifndef __statistics_h__
+#define __statistics_h__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <jack/types.h>
+
+/**
+ * @return the maximum delay reported by the backend since
+ * startup or reset. When compared to the period size in usecs, this
+ * can be used to estimate the ideal period size for a given setup.
+ */
+float jack_get_max_delayed_usecs (jack_client_t *client);
+
+/**
+ * @return the delay in microseconds due to the most recent XRUN
+ * occurrence. This probably only makes sense when called from a @ref
+ * JackXRunCallback defined using jack_set_xrun_callback().
+ */
+float jack_get_xrun_delayed_usecs (jack_client_t *client);
+
+/**
+ * Reset the maximum delay counter. This would be useful
+ * to estimate the effect that a change to the configuration of a running
+ * system (e.g. toggling kernel preemption) has on the delay
+ * experienced by JACK, without having to restart the JACK engine.
+ */
+void jack_reset_max_delayed_usecs (jack_client_t *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __statistics_h__ */
diff --git a/pipewire-jack/jack/systemdeps.h b/pipewire-jack/jack/systemdeps.h
new file mode 100644
index 0000000..84b4ce3
--- /dev/null
+++ b/pipewire-jack/jack/systemdeps.h
@@ -0,0 +1,141 @@
+/*
+Copyright (C) 2004-2012 Grame
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __jack_systemdeps_h__
+#define __jack_systemdeps_h__
+
+#ifndef POST_PACKED_STRUCTURE
+
+ #ifdef __GNUC__
+ /* POST_PACKED_STRUCTURE needs to be a macro which
+ expands into a compiler directive. The directive must
+ tell the compiler to arrange the preceding structure
+ declaration so that it is packed on byte-boundaries rather
+ than use the natural alignment of the processor and/or
+ compiler.
+ */
+
+ #define PRE_PACKED_STRUCTURE
+ #define POST_PACKED_STRUCTURE __attribute__((__packed__))
+
+ #else
+
+ #ifdef _MSC_VER
+ #define PRE_PACKED_STRUCTURE1 __pragma(pack(push,1))
+ #define PRE_PACKED_STRUCTURE PRE_PACKED_STRUCTURE1
+ /* PRE_PACKED_STRUCTURE needs to be a macro which
+ expands into a compiler directive. The directive must
+ tell the compiler to arrange the following structure
+ declaration so that it is packed on byte-boundaries rather
+ than use the natural alignment of the processor and/or
+ compiler.
+ */
+ #define POST_PACKED_STRUCTURE ;__pragma(pack(pop))
+ /* and POST_PACKED_STRUCTURE needs to be a macro which
+ restores the packing to its previous setting */
+ #else
+ #define PRE_PACKED_STRUCTURE
+ #define POST_PACKED_STRUCTURE
+ #endif /* _MSC_VER */
+
+ #endif /* __GNUC__ */
+
+#endif
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(GNU_WIN32)
+
+ #ifdef __MINGW32__
+ # include <winsock2.h> // mingw gives warning if we include windows.h before winsock2.h
+ #endif
+
+ #include <windows.h>
+
+ #ifdef _MSC_VER /* Microsoft compiler */
+ #define __inline__ inline
+ #if (!defined(int8_t) && !defined(_STDINT_H))
+ #define __int8_t_defined
+ typedef INT8 int8_t;
+ typedef UINT8 uint8_t;
+ typedef INT16 int16_t;
+ typedef UINT16 uint16_t;
+ typedef INT32 int32_t;
+ typedef UINT32 uint32_t;
+ typedef INT64 int64_t;
+ typedef UINT64 uint64_t;
+ #endif
+ #elif __MINGW32__ /* MINGW */
+ #include <stdint.h>
+ #include <sys/types.h>
+ #else /* other compilers ...*/
+ #include <inttypes.h>
+ #include <pthread.h>
+ #include <sys/types.h>
+ #endif
+
+ #if !defined(_PTHREAD_H) && !defined(PTHREAD_WIN32)
+ /**
+ * to make jack API independent of different thread implementations,
+ * we define jack_native_thread_t to HANDLE here.
+ */
+ typedef HANDLE jack_native_thread_t;
+ #else
+ #ifdef PTHREAD_WIN32 // Added by JE - 10-10-2011
+ #include <ptw32/pthread.h> // Makes sure we #include the ptw32 version !
+ #endif
+ /**
+ * to make jack API independent of different thread implementations,
+ * we define jack_native_thread_t to pthread_t here.
+ */
+ typedef pthread_t jack_native_thread_t;
+ #endif
+
+#endif /* _WIN32 && !__CYGWIN__ && !GNU_WIN32 */
+
+#if defined(__APPLE__) || defined(__linux__) || defined(__sun__) || defined(sun) || defined(__unix__) || defined(__CYGWIN__) || defined(GNU_WIN32)
+
+ #if defined(__CYGWIN__) || defined(GNU_WIN32)
+ #include <stdint.h>
+ #endif
+ #include <inttypes.h>
+ #include <pthread.h>
+ #include <sys/types.h>
+
+ /**
+ * to make jack API independent of different thread implementations,
+ * we define jack_native_thread_t to pthread_t here.
+ */
+ typedef pthread_t jack_native_thread_t;
+
+#endif /* __APPLE__ || __linux__ || __sun__ || sun */
+
+#if (defined(__arm__) || defined(__aarch64__) || defined(__mips__) || defined(__ppc__) || defined(__powerpc__)) && !defined(__APPLE__)
+ #undef POST_PACKED_STRUCTURE
+ #define POST_PACKED_STRUCTURE
+#endif /* __arm__ || __aarch64__ || __mips__ || __ppc__ || __powerpc__ */
+
+/** define JACK_LIB_EXPORT, useful for internal clients */
+#if defined(_WIN32)
+ #define JACK_LIB_EXPORT __declspec(dllexport)
+#elif defined(__GNUC__)
+ #define JACK_LIB_EXPORT __attribute__((visibility("default")))
+#else
+ #define JACK_LIB_EXPORT
+#endif
+
+#endif /* __jack_systemdeps_h__ */
diff --git a/pipewire-jack/jack/thread.h b/pipewire-jack/jack/thread.h
new file mode 100644
index 0000000..776c520
--- /dev/null
+++ b/pipewire-jack/jack/thread.h
@@ -0,0 +1,160 @@
+/*
+ Copyright (C) 2004 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __jack_thread_h__
+#define __jack_thread_h__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <jack/systemdeps.h>
+#include <jack/weakmacros.h>
+
+/* use 512KB stack per thread - the default is way too high to be feasible
+ * with mlockall() on many systems */
+#define THREAD_STACK 524288
+
+/** @file thread.h
+ *
+ * Library functions to standardize thread creation for JACK and its
+ * clients. These interfaces hide some system variations in the
+ * handling of realtime scheduling and associated privileges.
+ */
+
+/**
+ * @defgroup ClientThreads Creating and managing client threads
+ * @{
+ */
+
+ /**
+ * @returns if JACK is running with realtime scheduling, this returns
+ * the priority that any JACK-created client threads will run at.
+ * Otherwise returns -1.
+ */
+
+int jack_client_real_time_priority (jack_client_t*) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * @returns if JACK is running with realtime scheduling, this returns
+ * the maximum priority that a JACK client thread should use if the thread
+ * is subject to realtime scheduling. Otherwise returns -1.
+ */
+
+int jack_client_max_real_time_priority (jack_client_t*) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Attempt to enable realtime scheduling for a thread. On some
+ * systems that may require special privileges.
+ *
+ * @param thread POSIX thread ID.
+ * @param priority requested thread priority.
+ *
+ * @returns 0, if successful; EPERM, if the calling process lacks
+ * required realtime privileges; otherwise some other error number.
+ */
+int jack_acquire_real_time_scheduling (jack_native_thread_t thread, int priority) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Create a thread for JACK or one of its clients. The thread is
+ * created executing @a start_routine with @a arg as its sole
+ * argument.
+ *
+ * @param client the JACK client for whom the thread is being created. May be
+ * NULL if the client is being created within the JACK server.
+ * @param thread place to return POSIX thread ID.
+ * @param priority thread priority, if realtime.
+ * @param realtime true for the thread to use realtime scheduling. On
+ * some systems that may require special privileges.
+ * @param start_routine function the thread calls when it starts.
+ * @param arg parameter passed to the @a start_routine.
+ *
+ * @returns 0, if successful; otherwise some error number.
+ */
+int jack_client_create_thread (jack_client_t* client,
+ jack_native_thread_t *thread,
+ int priority,
+ int realtime, /* boolean */
+ void *(*start_routine)(void*),
+ void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Drop realtime scheduling for a thread.
+ *
+ * @param thread POSIX thread ID.
+ *
+ * @returns 0, if successful; otherwise an error number.
+ */
+int jack_drop_real_time_scheduling (jack_native_thread_t thread) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Stop the thread, waiting for the thread handler to terminate.
+ *
+ * @param thread POSIX thread ID.
+ *
+ * @returns 0, if successful; otherwise an error number.
+ */
+int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Kill the thread.
+ *
+ * @param thread POSIX thread ID.
+ *
+ * @returns 0, if successful; otherwise an error number.
+ */
+ int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread) JACK_OPTIONAL_WEAK_EXPORT;
+
+#ifndef _WIN32
+
+ typedef int (*jack_thread_creator_t)(pthread_t*,
+ const pthread_attr_t*,
+ void* (*function)(void*),
+ void* arg);
+/**
+ * This function can be used in very very specialized cases
+ * where it is necessary that client threads created by JACK
+ * are created by something other than pthread_create(). After
+ * it is used, any threads that JACK needs for the client will
+ * will be created by calling the function passed to this
+ * function.
+ *
+ * No normal application/client should consider calling this.
+ * The specific case for which it was created involves running
+ * win32/x86 plugins under Wine on Linux, where it is necessary
+ * that all threads that might call win32 functions are known
+ * to Wine.
+ *
+ * Set it to NULL to restore thread creation function.
+ *
+ * @param creator a function that creates a new thread
+ *
+ */
+void jack_set_thread_creator (jack_thread_creator_t creator) JACK_OPTIONAL_WEAK_EXPORT;
+
+#endif
+
+/* @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __jack_thread_h__ */
diff --git a/pipewire-jack/jack/transport.h b/pipewire-jack/jack/transport.h
new file mode 100644
index 0000000..4cec6e0
--- /dev/null
+++ b/pipewire-jack/jack/transport.h
@@ -0,0 +1,247 @@
+/*
+ Copyright (C) 2002 Paul Davis
+ Copyright (C) 2003 Jack O'Quin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __jack_transport_h__
+#define __jack_transport_h__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <jack/types.h>
+#include <jack/weakmacros.h>
+
+/**
+ * @defgroup TransportControl Transport and Timebase control
+ * @{
+ */
+
+/**
+ * Called by the timebase master to release itself from that
+ * responsibility.
+ *
+ * If the timebase master releases the timebase or leaves the JACK
+ * graph for any reason, the JACK engine takes over at the start of
+ * the next process cycle. The transport state does not change. If
+ * rolling, it continues to play, with frame numbers as the only
+ * available position information.
+ *
+ * @see jack_set_timebase_callback
+ *
+ * @param client the JACK client structure.
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+ */
+int jack_release_timebase (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Register (or unregister) as a slow-sync client, one that cannot
+ * respond immediately to transport position changes.
+ *
+ * The @a sync_callback will be invoked at the first available
+ * opportunity after its registration is complete. If the client is
+ * currently active this will be the following process cycle,
+ * otherwise it will be the first cycle after calling jack_activate().
+ * After that, it runs according to the ::JackSyncCallback rules.
+ * Clients that don't set a @a sync_callback are assumed to be ready
+ * immediately any time the transport wants to start.
+ *
+ * @param client the JACK client structure.
+ * @param sync_callback is a realtime function that returns TRUE when
+ * the client is ready. Setting @a sync_callback to NULL declares that
+ * this client no longer requires slow-sync processing.
+ * @param arg an argument for the @a sync_callback function.
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+ */
+int jack_set_sync_callback (jack_client_t *client,
+ JackSyncCallback sync_callback,
+ void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Set the timeout value for slow-sync clients.
+ *
+ * This timeout prevents unresponsive slow-sync clients from
+ * completely halting the transport mechanism. The default is two
+ * seconds. When the timeout expires, the transport starts rolling,
+ * even if some slow-sync clients are still unready. The @a
+ * sync_callbacks of these clients continue being invoked, giving them
+ * a chance to catch up.
+ *
+ * @see jack_set_sync_callback
+ *
+ * @param client the JACK client structure.
+ * @param timeout is delay (in microseconds) before the timeout expires.
+ *
+ * @return 0 on success, otherwise a non-zero error code.
+ */
+int jack_set_sync_timeout (jack_client_t *client,
+ jack_time_t timeout) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * 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 @a timebase_callback is no longer called.
+ * Taking over the timebase may be done conditionally, so it fails if
+ * there was a master already.
+ *
+ * @param client the JACK client structure.
+ * @param conditional non-zero for a conditional request.
+ * @param timebase_callback is a realtime function that returns
+ * position information.
+ * @param arg an argument for the @a timebase_callback function.
+ *
+ * @return
+ * - 0 on success;
+ * - EBUSY if a conditional request fails because there was already a
+ * timebase master;
+ * - other non-zero error code.
+ */
+int jack_set_timebase_callback (jack_client_t *client,
+ int conditional,
+ JackTimebaseCallback timebase_callback,
+ void *arg) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Reposition the transport to a new frame number.
+ *
+ * May be called at any time by any client. The new position takes
+ * effect in two process cycles. If there are slow-sync clients and
+ * the transport is already rolling, it will enter the
+ * ::JackTransportStarting state and begin invoking their @a
+ * sync_callbacks until ready. This function is realtime-safe.
+ *
+ * @see jack_transport_reposition, jack_set_sync_callback
+ *
+ * @param client the JACK client structure.
+ * @param frame frame number of new transport position.
+ *
+ * @return 0 if valid request, non-zero otherwise.
+ */
+int jack_transport_locate (jack_client_t *client,
+ jack_nframes_t frame) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Query the current transport state and position.
+ *
+ * This function is realtime-safe, and can be called from any thread.
+ * If called from the process thread, @a pos corresponds to the first
+ * frame of the current cycle and the state returned is valid for the
+ * entire cycle.
+ *
+ * @param client the JACK client structure.
+ * @param pos pointer to structure for returning current transport
+ * position; @a pos->valid will show which fields contain valid data.
+ * If @a pos is NULL, do not return position information.
+ *
+ * @return Current transport state.
+ */
+jack_transport_state_t jack_transport_query (const jack_client_t *client,
+ jack_position_t *pos) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Return an estimate of the current transport frame,
+ * including any time elapsed since the last transport
+ * positional update.
+ *
+ * @param client the JACK client structure
+ */
+jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Request a new transport position.
+ *
+ * May be called at any time by any client. The new position takes
+ * effect in two process cycles. If there are slow-sync clients and
+ * the transport is already rolling, it will enter the
+ * ::JackTransportStarting state and begin invoking their @a
+ * sync_callbacks until ready. This function is realtime-safe.
+ *
+ * @see jack_transport_locate, jack_set_sync_callback
+ *
+ * @param client the JACK client structure.
+ * @param pos requested new transport position.
+ *
+ * @return 0 if valid request, EINVAL if position structure rejected.
+ */
+int jack_transport_reposition (jack_client_t *client,
+ const jack_position_t *pos) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Start the JACK transport rolling.
+ *
+ * Any client can make this request at any time. It takes effect no
+ * sooner than the next process cycle, perhaps later if there are
+ * slow-sync clients. This function is realtime-safe.
+ *
+ * @see jack_set_sync_callback
+ *
+ * @param client the JACK client structure.
+ */
+void jack_transport_start (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Stop the JACK transport.
+ *
+ * Any client can make this request at any time. It takes effect on
+ * the next process cycle. This function is realtime-safe.
+ *
+ * @param client the JACK client structure.
+ */
+void jack_transport_stop (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Gets the current transport info structure (deprecated).
+ *
+ * @param client the JACK client structure.
+ * @param tinfo current transport info structure. The "valid" field
+ * describes which fields contain valid data.
+ *
+ * @deprecated This is for compatibility with the earlier transport
+ * interface. Use jack_transport_query(), instead.
+ *
+ * @pre Must be called from the process thread.
+ */
+void jack_get_transport_info (jack_client_t *client,
+ jack_transport_info_t *tinfo) JACK_OPTIONAL_WEAK_EXPORT;
+
+/**
+ * Set the transport info structure (deprecated).
+ *
+ * @deprecated This function still exists for compatibility with the
+ * earlier transport interface, but it does nothing. Instead, define
+ * a ::JackTimebaseCallback.
+ */
+void jack_set_transport_info (jack_client_t *client,
+ jack_transport_info_t *tinfo) JACK_OPTIONAL_WEAK_EXPORT;
+
+/*@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __jack_transport_h__ */
diff --git a/pipewire-jack/jack/types.h b/pipewire-jack/jack/types.h
new file mode 100644
index 0000000..b62af96
--- /dev/null
+++ b/pipewire-jack/jack/types.h
@@ -0,0 +1,740 @@
+/*
+ Copyright (C) 2001 Paul Davis
+ Copyright (C) 2004 Jack O'Quin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __jack_types_h__
+#define __jack_types_h__
+
+#include <jack/systemdeps.h>
+
+typedef uint64_t jack_uuid_t;
+
+typedef int32_t jack_shmsize_t;
+
+/**
+ * Type used to represent sample frame counts.
+ */
+typedef uint32_t jack_nframes_t;
+
+/**
+ * Maximum value that can be stored in jack_nframes_t
+ */
+#define JACK_MAX_FRAMES (4294967295U) /* This should be UINT32_MAX, but C++ has a problem with that. */
+
+/**
+ * Type used to represent the value of free running
+ * monotonic clock with units of microseconds.
+ */
+typedef uint64_t jack_time_t;
+
+/**
+ * Maximum size of @a load_init string passed to an internal client
+ * jack_initialize() function via jack_internal_client_load().
+ */
+#define JACK_LOAD_INIT_LIMIT 1024
+
+/**
+ * jack_intclient_t is an opaque type representing a loaded internal
+ * client. You may only access it using the API provided in @ref
+ * intclient.h "<jack/intclient.h>".
+ */
+typedef uint64_t jack_intclient_t;
+
+/**
+ * jack_port_t is an opaque type. You may only access it using the
+ * API provided.
+ */
+typedef struct _jack_port jack_port_t;
+
+/**
+ * jack_client_t is an opaque type. You may only access it using the
+ * API provided.
+ */
+typedef struct _jack_client jack_client_t;
+
+/**
+ * Ports have unique ids. A port registration callback is the only
+ * place you ever need to know their value.
+ */
+typedef uint32_t jack_port_id_t;
+
+typedef uint32_t jack_port_type_id_t;
+
+/**
+ * @ref jack_options_t bits
+ */
+enum JackOptions {
+
+ /**
+ * Null value to use when no option bits are needed.
+ */
+ JackNullOption = 0x00,
+
+ /**
+ * Do not automatically start the JACK server when it is not
+ * already running. This option is always selected if
+ * \$JACK_NO_START_SERVER is defined in the calling process
+ * environment.
+ */
+ JackNoStartServer = 0x01,
+
+ /**
+ * Use the exact client name requested. Otherwise, JACK
+ * automatically generates a unique one, if needed.
+ */
+ JackUseExactName = 0x02,
+
+ /**
+ * Open with optional <em>(char *) server_name</em> parameter.
+ */
+ JackServerName = 0x04,
+
+ /**
+ * Load internal client from optional <em>(char *)
+ * load_name</em>. Otherwise use the @a client_name.
+ */
+ JackLoadName = 0x08,
+
+ /**
+ * Pass optional <em>(char *) load_init</em> string to the
+ * jack_initialize() entry point of an internal client.
+ */
+ JackLoadInit = 0x10,
+
+ /**
+ * pass a SessionID Token this allows the sessionmanager to identify the client again.
+ */
+ JackSessionID = 0x20
+};
+
+/** Valid options for opening an external client. */
+#define JackOpenOptions (JackSessionID|JackServerName|JackNoStartServer|JackUseExactName)
+
+/** Valid options for loading an internal client. */
+#define JackLoadOptions (JackLoadInit|JackLoadName|JackUseExactName)
+
+/**
+ * Options for several JACK operations, formed by OR-ing together the
+ * relevant @ref JackOptions bits.
+ */
+typedef enum JackOptions jack_options_t;
+
+/**
+ * @ref jack_status_t bits
+ */
+enum JackStatus {
+
+ /**
+ * Overall operation failed.
+ */
+ JackFailure = 0x01,
+
+ /**
+ * The operation contained an invalid or unsupported option.
+ */
+ JackInvalidOption = 0x02,
+
+ /**
+ * The desired client name was not unique. With the @ref
+ * JackUseExactName option this situation is fatal. Otherwise,
+ * the name was modified by appending a dash and a two-digit
+ * number in the range "-01" to "-99". The
+ * jack_get_client_name() function will return the exact string
+ * that was used. If the specified @a client_name plus these
+ * extra characters would be too long, the open fails instead.
+ */
+ JackNameNotUnique = 0x04,
+
+ /**
+ * The JACK server was started as a result of this operation.
+ * Otherwise, it was running already. In either case the caller
+ * is now connected to jackd, so there is no race condition.
+ * When the server shuts down, the client will find out.
+ */
+ JackServerStarted = 0x08,
+
+ /**
+ * Unable to connect to the JACK server.
+ */
+ JackServerFailed = 0x10,
+
+ /**
+ * Communication error with the JACK server.
+ */
+ JackServerError = 0x20,
+
+ /**
+ * Requested client does not exist.
+ */
+ JackNoSuchClient = 0x40,
+
+ /**
+ * Unable to load internal client
+ */
+ JackLoadFailure = 0x80,
+
+ /**
+ * Unable to initialize client
+ */
+ JackInitFailure = 0x100,
+
+ /**
+ * Unable to access shared memory
+ */
+ JackShmFailure = 0x200,
+
+ /**
+ * Client's protocol version does not match
+ */
+ JackVersionError = 0x400,
+
+ /**
+ * Backend error
+ */
+ JackBackendError = 0x800,
+
+ /**
+ * Client zombified failure
+ */
+ JackClientZombie = 0x1000
+};
+
+/**
+ * Status word returned from several JACK operations, formed by
+ * OR-ing together the relevant @ref JackStatus bits.
+ */
+typedef enum JackStatus jack_status_t;
+
+/**
+ * @ref jack_latency_callback_mode_t
+ */
+enum JackLatencyCallbackMode {
+
+ /**
+ * Latency Callback for Capture Latency.
+ * Input Ports have their latency value setup.
+ * In the Callback the client needs to set the latency of the output ports
+ */
+ JackCaptureLatency,
+
+ /**
+ * Latency Callback for Playback Latency.
+ * Output Ports have their latency value setup.
+ * In the Callback the client needs to set the latency of the input ports
+ */
+ JackPlaybackLatency
+
+};
+
+/**
+ * Type of Latency Callback (Capture or Playback)
+ */
+typedef enum JackLatencyCallbackMode jack_latency_callback_mode_t;
+
+/**
+ * Prototype for the client supplied function that is called
+ * by the engine when port latencies need to be recalculated
+ *
+ * @param mode playback or capture latency
+ * @param arg pointer to a client supplied data
+ *
+ * @return zero on success, non-zero on error
+ */
+typedef void (*JackLatencyCallback)(jack_latency_callback_mode_t mode, void *arg);
+
+/**
+ * the new latency API operates on Ranges.
+ */
+PRE_PACKED_STRUCTURE
+struct _jack_latency_range
+{
+ /**
+ * minimum latency
+ */
+ jack_nframes_t min;
+ /**
+ * maximum latency
+ */
+ jack_nframes_t max;
+} POST_PACKED_STRUCTURE;
+
+typedef struct _jack_latency_range jack_latency_range_t;
+
+/**
+ * Prototype for the client supplied function that is called
+ * by the engine anytime there is work to be done.
+ *
+ * @pre nframes == jack_get_buffer_size()
+ * @pre nframes == pow(2,x)
+ *
+ * @param nframes number of frames to process
+ * @param arg pointer to a client supplied structure
+ *
+ * @return zero on success, non-zero on error
+ */
+typedef int (*JackProcessCallback)(jack_nframes_t nframes, void *arg);
+
+/**
+ * Prototype for the client thread routine called
+ * by the engine when the client is inserted in the graph.
+ *
+ * @param arg pointer to a client supplied structure
+ *
+ */
+typedef void *(*JackThreadCallback)(void* arg);
+
+/**
+ * Prototype for the client supplied function that is called
+ * once after the creation of the thread in which other
+ * callbacks will be made. Special thread characteristics
+ * can be set from this callback, for example. This is a
+ * highly specialized callback and most clients will not
+ * and should not use it.
+ *
+ * @param arg pointer to a client supplied structure
+ *
+ * @return void
+ */
+typedef void (*JackThreadInitCallback)(void *arg);
+
+/**
+ * Prototype for the client supplied function that is called
+ * whenever the processing graph is reordered.
+ *
+ * @param arg pointer to a client supplied structure
+ *
+ * @return zero on success, non-zero on error
+ */
+typedef int (*JackGraphOrderCallback)(void *arg);
+
+/**
+ * Prototype for the client-supplied function that is called whenever
+ * an xrun has occurred.
+ *
+ * @see jack_get_xrun_delayed_usecs()
+ *
+ * @param arg pointer to a client supplied structure
+ *
+ * @return zero on success, non-zero on error
+ */
+typedef int (*JackXRunCallback)(void *arg);
+
+/**
+ * Prototype for the @a bufsize_callback that is invoked whenever the
+ * JACK engine buffer size changes. Although this function is called
+ * in the JACK process thread, the normal process cycle is suspended
+ * during its operation, causing a gap in the audio flow. So, the @a
+ * bufsize_callback can allocate storage, touch memory not previously
+ * referenced, and perform other operations that are not realtime
+ * safe.
+ *
+ * @param nframes buffer size
+ * @param arg pointer supplied by jack_set_buffer_size_callback().
+ *
+ * @return zero on success, non-zero on error
+ */
+typedef int (*JackBufferSizeCallback)(jack_nframes_t nframes, void *arg);
+
+/**
+ * Prototype for the client supplied function that is called
+ * when the engine sample rate changes.
+ *
+ * @param nframes new engine sample rate
+ * @param arg pointer to a client supplied structure
+ *
+ * @return zero on success, non-zero on error
+ */
+typedef int (*JackSampleRateCallback)(jack_nframes_t nframes, void *arg);
+
+/**
+ * Prototype for the client supplied function that is called
+ * whenever a port is registered or unregistered.
+ *
+ * @param port the ID of the port
+ * @param arg pointer to a client supplied data
+ * @param register non-zero if the port is being registered,
+ * zero if the port is being unregistered
+ */
+typedef void (*JackPortRegistrationCallback)(jack_port_id_t port, int /* register */, void *arg);
+
+/**
+ * Prototype for the client supplied function that is called
+ * whenever a client is registered or unregistered.
+ *
+ * @param name a null-terminated string containing the client name
+ * @param register non-zero if the client is being registered,
+ * zero if the client is being unregistered
+ * @param arg pointer to a client supplied structure
+ */
+typedef void (*JackClientRegistrationCallback)(const char* name, int /* register */, void *arg);
+
+/**
+ * Prototype for the client supplied function that is called
+ * whenever a port is connected or disconnected.
+ *
+ * @param a one of two ports connected or disconnected
+ * @param b one of two ports connected or disconnected
+ * @param connect non-zero if ports were connected
+ * zero if ports were disconnected
+ * @param arg pointer to a client supplied data
+ */
+typedef void (*JackPortConnectCallback)(jack_port_id_t a, jack_port_id_t b, int connect, void* arg);
+
+/**
+ * Prototype for the client supplied function that is called
+ * whenever the port name has been changed.
+ *
+ * @param port the port that has been renamed
+ * @param new_name the new name
+ * @param arg pointer to a client supplied structure
+ */
+typedef void (*JackPortRenameCallback)(jack_port_id_t port, const char* old_name, const char* new_name, void *arg);
+
+/**
+ * Prototype for the client supplied function that is called
+ * whenever jackd starts or stops freewheeling.
+ *
+ * @param starting non-zero if we start starting to freewheel, zero otherwise
+ * @param arg pointer to a client supplied structure
+ */
+typedef void (*JackFreewheelCallback)(int starting, void *arg);
+
+/**
+ * Prototype for the client supplied function that is called
+ * whenever jackd is shutdown. Note that after server shutdown,
+ * the client pointer is *not* deallocated by libjack,
+ * the application is responsible to properly use jack_client_close()
+ * to release client resources. Warning: jack_client_close() cannot be
+ * safely used inside the shutdown callback and has to be called outside of
+ * the callback context.
+ *
+ * @param arg pointer to a client supplied structure
+ */
+typedef void (*JackShutdownCallback)(void *arg);
+
+/**
+ * Prototype for the client supplied function that is called
+ * whenever jackd is shutdown. Note that after server shutdown,
+ * the client pointer is *not* deallocated by libjack,
+ * the application is responsible to properly use jack_client_close()
+ * to release client resources. Warning: jack_client_close() cannot be
+ * safely used inside the shutdown callback and has to be called outside of
+ * the callback context.
+
+ * @param code a status word, formed by OR-ing together the relevant @ref JackStatus bits.
+ * @param reason a string describing the shutdown reason (backend failure, server crash... etc...).
+ * Note that this string will not be available anymore after the callback returns, so possibly copy it.
+ * @param arg pointer to a client supplied structure
+ */
+typedef void (*JackInfoShutdownCallback)(jack_status_t code, const char* reason, void *arg);
+
+/**
+ * Used for the type argument of jack_port_register() for default
+ * audio ports and midi ports.
+ */
+#define JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
+#define JACK_DEFAULT_MIDI_TYPE "8 bit raw midi"
+
+/**
+ * For convenience, use this typedef if you want to be able to change
+ * between float and double. You may want to typedef sample_t to
+ * jack_default_audio_sample_t in your application.
+ */
+typedef float jack_default_audio_sample_t;
+
+/**
+ * A port has a set of flags that are formed by AND-ing together the
+ * desired values from the list below. The flags "JackPortIsInput" and
+ * "JackPortIsOutput" are mutually exclusive and it is an error to use
+ * them both.
+ */
+enum JackPortFlags {
+
+ /**
+ * if JackPortIsInput is set, then the port can receive
+ * data.
+ */
+ JackPortIsInput = 0x1,
+
+ /**
+ * if JackPortIsOutput is set, then data can be read from
+ * the port.
+ */
+ JackPortIsOutput = 0x2,
+
+ /**
+ * if JackPortIsPhysical is set, then the port corresponds
+ * to some kind of physical I/O connector.
+ */
+ JackPortIsPhysical = 0x4,
+
+ /**
+ * if JackPortCanMonitor is set, then a call to
+ * jack_port_request_monitor() makes sense.
+ *
+ * Precisely what this means is dependent on the client. A typical
+ * result of it being called with TRUE as the second argument is
+ * that data that would be available from an output port (with
+ * JackPortIsPhysical set) is sent to a physical output connector
+ * as well, so that it can be heard/seen/whatever.
+ *
+ * Clients that do not control physical interfaces
+ * should never create ports with this bit set.
+ */
+ JackPortCanMonitor = 0x8,
+
+ /**
+ * JackPortIsTerminal means:
+ *
+ * for an input port: the data received by the port
+ * will not be passed on or made
+ * available at any other port
+ *
+ * for an output port: the data available at the port
+ * does not originate from any other port
+ *
+ * Audio synthesizers, I/O hardware interface clients, HDR
+ * systems are examples of clients that would set this flag for
+ * their ports.
+ */
+ JackPortIsTerminal = 0x10,
+
+};
+
+/**
+ * Transport states.
+ */
+typedef enum {
+
+ /* the order matters for binary compatibility */
+ JackTransportStopped = 0, /**< Transport halted */
+ JackTransportRolling = 1, /**< Transport playing */
+ JackTransportLooping = 2, /**< For OLD_TRANSPORT, now ignored */
+ JackTransportStarting = 3, /**< Waiting for sync ready */
+ JackTransportNetStarting = 4, /**< Waiting for sync ready on the network*/
+
+} jack_transport_state_t;
+
+typedef uint64_t jack_unique_t; /**< Unique ID (opaque) */
+
+/**
+ * Optional struct jack_position_t fields.
+ */
+typedef enum {
+
+ JackPositionBBT = 0x10, /**< Bar, Beat, Tick */
+ JackPositionTimecode = 0x20, /**< External timecode */
+ JackBBTFrameOffset = 0x40, /**< Frame offset of BBT information */
+ JackAudioVideoRatio = 0x80, /**< audio frames per video frame */
+ JackVideoFrameOffset = 0x100 /**< frame offset of first video frame */
+
+} jack_position_bits_t;
+
+/** all valid position bits */
+#define JACK_POSITION_MASK (JackPositionBBT|JackPositionTimecode)
+#define EXTENDED_TIME_INFO
+
+PRE_PACKED_STRUCTURE
+struct _jack_position {
+
+ /* these four cannot be set from clients: the server sets them */
+ jack_unique_t unique_1; /**< unique ID */
+ jack_time_t usecs; /**< monotonic, free-rolling */
+ jack_nframes_t frame_rate; /**< current frame rate (per second) */
+ jack_nframes_t frame; /**< frame number, always present */
+
+ jack_position_bits_t valid; /**< which other fields are valid */
+
+ /* JackPositionBBT fields: */
+ int32_t bar; /**< current bar */
+ int32_t beat; /**< current beat-within-bar */
+ int32_t tick; /**< current tick-within-beat */
+ double bar_start_tick;
+
+ float beats_per_bar; /**< time signature "numerator" */
+ float beat_type; /**< time signature "denominator" */
+ double ticks_per_beat;
+ double beats_per_minute;
+
+ /* JackPositionTimecode fields: (EXPERIMENTAL: could change) */
+ double frame_time; /**< current time in seconds */
+ double next_time; /**< next sequential frame_time
+ (unless repositioned) */
+
+ /* JackBBTFrameOffset fields: */
+ jack_nframes_t bbt_offset; /**< frame offset for the BBT fields
+ (the given bar, beat, and tick
+ values actually refer to a time
+ frame_offset frames before the
+ start of the cycle), should
+ be assumed to be 0 if
+ JackBBTFrameOffset is not
+ set. If JackBBTFrameOffset is
+ set and this value is zero, the BBT
+ time refers to the first frame of this
+ cycle. If the value is positive,
+ the BBT time refers to a frame that
+ many frames before the start of the
+ cycle. */
+
+ /* JACK video positional data (experimental) */
+
+ float audio_frames_per_video_frame; /**< number of audio frames
+ per video frame. Should be assumed
+ zero if JackAudioVideoRatio is not
+ set. If JackAudioVideoRatio is set
+ and the value is zero, no video
+ data exists within the JACK graph */
+
+ jack_nframes_t video_offset; /**< audio frame at which the first video
+ frame in this cycle occurs. Should
+ be assumed to be 0 if JackVideoFrameOffset
+ is not set. If JackVideoFrameOffset is
+ set, but the value is zero, there is
+ no video frame within this cycle. */
+
+ /* For binary compatibility, new fields should be allocated from
+ * this padding area with new valid bits controlling access, so
+ * the existing structure size and offsets are preserved. */
+ int32_t padding[7];
+
+ /* When (unique_1 == unique_2) the contents are consistent. */
+ jack_unique_t unique_2; /**< unique ID */
+
+} POST_PACKED_STRUCTURE;
+
+typedef struct _jack_position jack_position_t;
+
+/**
+ * Prototype for the @a sync_callback defined by slow-sync clients.
+ * When the client is active, this callback is invoked just before
+ * process() in the same thread. This occurs once after registration,
+ * then subsequently whenever some client requests a new position, or
+ * the transport enters the ::JackTransportStarting state. This
+ * realtime function must not wait.
+ *
+ * The transport @a state will be:
+ *
+ * - ::JackTransportStopped when a new position is requested;
+ * - ::JackTransportStarting when the transport is waiting to start;
+ * - ::JackTransportRolling when the timeout has expired, and the
+ * position is now a moving target.
+ *
+ * @param state current transport state.
+ * @param pos new transport position.
+ * @param arg the argument supplied by jack_set_sync_callback().
+ *
+ * @return TRUE (non-zero) when ready to roll.
+ */
+typedef int (*JackSyncCallback)(jack_transport_state_t state,
+ jack_position_t *pos,
+ void *arg);
+
+
+/**
+ * Prototype for the @a timebase_callback used to provide 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 jack_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 @a pos argument to set @a
+ * pos->frame. To change position, use jack_transport_reposition() or
+ * jack_transport_locate(). These functions are realtime-safe, the @a
+ * timebase_callback can call them directly.
+ *
+ * @param state current transport state.
+ * @param nframes number of frames in current period.
+ * @param pos address of the position structure for the next cycle; @a
+ * pos->frame will be its frame number. If @a 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 @a timebase_callback's task is to update the extended
+ * information here.
+ * @param new_pos TRUE (non-zero) for a newly requested @a pos, or for
+ * the first cycle after the @a timebase_callback is defined.
+ * @param arg the argument supplied by jack_set_timebase_callback().
+ */
+typedef void (*JackTimebaseCallback)(jack_transport_state_t state,
+ jack_nframes_t nframes,
+ jack_position_t *pos,
+ int new_pos,
+ void *arg);
+
+/*********************************************************************
+ * The following interfaces are DEPRECATED. They are only provided
+ * for compatibility with the earlier JACK transport implementation.
+ *********************************************************************/
+
+/**
+ * Optional struct jack_transport_info_t fields.
+ *
+ * @see jack_position_bits_t.
+ */
+typedef enum {
+
+ JackTransportState = 0x1, /**< Transport state */
+ JackTransportPosition = 0x2, /**< Frame number */
+ JackTransportLoop = 0x4, /**< Loop boundaries (ignored) */
+ JackTransportSMPTE = 0x8, /**< SMPTE (ignored) */
+ JackTransportBBT = 0x10 /**< Bar, Beat, Tick */
+
+} jack_transport_bits_t;
+
+/**
+ * Deprecated struct for transport position information.
+ *
+ * @deprecated This is for compatibility with the earlier transport
+ * interface. Use the jack_position_t struct, instead.
+ */
+typedef struct {
+
+ /* these two cannot be set from clients: the server sets them */
+
+ jack_nframes_t frame_rate; /**< current frame rate (per second) */
+ jack_time_t usecs; /**< monotonic, free-rolling */
+
+ jack_transport_bits_t valid; /**< which fields are legal to read */
+ jack_transport_state_t transport_state;
+ jack_nframes_t frame;
+ jack_nframes_t loop_start;
+ jack_nframes_t loop_end;
+
+ long smpte_offset; /**< SMPTE offset (from frame 0) */
+ float smpte_frame_rate; /**< 29.97, 30, 24 etc. */
+
+ 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;
+
+
+#endif /* __jack_types_h__ */
diff --git a/pipewire-jack/jack/uuid.h b/pipewire-jack/jack/uuid.h
new file mode 100644
index 0000000..406c119
--- /dev/null
+++ b/pipewire-jack/jack/uuid.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2013 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __jack_uuid_h__
+#define __jack_uuid_h__
+
+#include <jack/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define JACK_UUID_SIZE 36
+#define JACK_UUID_STRING_SIZE (JACK_UUID_SIZE+1) /* includes trailing null */
+#define JACK_UUID_EMPTY_INITIALIZER 0
+
+extern jack_uuid_t jack_client_uuid_generate (void);
+extern jack_uuid_t jack_port_uuid_generate (uint32_t port_id);
+
+extern uint32_t jack_uuid_to_index (jack_uuid_t);
+
+extern int jack_uuid_compare (jack_uuid_t, jack_uuid_t);
+extern void jack_uuid_copy (jack_uuid_t* dst, jack_uuid_t src);
+extern void jack_uuid_clear (jack_uuid_t*);
+extern int jack_uuid_parse (const char *buf, jack_uuid_t*);
+extern void jack_uuid_unparse (jack_uuid_t, char buf[JACK_UUID_STRING_SIZE]);
+extern int jack_uuid_empty (jack_uuid_t);
+
+#ifdef __cplusplus
+} /* namespace */
+#endif
+
+#endif /* __jack_uuid_h__ */
+
diff --git a/pipewire-jack/jack/weakjack.h b/pipewire-jack/jack/weakjack.h
new file mode 100644
index 0000000..c253c63
--- /dev/null
+++ b/pipewire-jack/jack/weakjack.h
@@ -0,0 +1,125 @@
+/*
+ Copyright (C) 2010 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __weakjack_h__
+#define __weakjack_h__
+
+/**
+ * @defgroup WeakLinkage Managing support for newer/older versions of JACK
+ * @{ One challenge faced by developers is that of taking
+ * advantage of new features introduced in new versions
+ * of [ JACK ] while still supporting older versions of
+ * the system. Normally, if an application uses a new
+ * feature in a library/API, it is unable to run on
+ * earlier versions of the library/API that do not
+ * support that feature. Such applications would either
+ * fail to launch or crash when an attempt to use the
+ * feature was made. This problem cane be solved using
+ * weakly-linked symbols.
+ *
+ * When a symbol in a framework is defined as weakly
+ * linked, the symbol does not have to be present at
+ * runtime for a process to continue running. The static
+ * linker identifies a weakly linked symbol as such in
+ * any code module that references the symbol. The
+ * dynamic linker uses this same information at runtime
+ * to determine whether a process can continue
+ * running. If a weakly linked symbol is not present in
+ * the framework, the code module can continue to run as
+ * long as it does not reference the symbol. However, if
+ * the symbol is present, the code can use it normally.
+ *
+ * (adapted from: http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WeakLinking.html)
+ *
+ * A concrete example will help. Suppose that someone uses a version
+ * of a JACK client we'll call "Jill". Jill was linked against a version
+ * of JACK that contains a newer part of the API (say, jack_set_latency_callback())
+ * and would like to use it if it is available.
+ *
+ * When Jill is run on a system that has a suitably "new" version of
+ * JACK, this function will be available entirely normally. But if Jill
+ * is run on a system with an old version of JACK, the function isn't
+ * available.
+ *
+ * With normal symbol linkage, this would create a startup error whenever
+ * someone tries to run Jill with the "old" version of JACK. However, functions
+ * added to JACK after version 0.116.2 are all declared to have "weak" linkage
+ * which means that their absence doesn't cause an error during program
+ * startup. Instead, Jill can test whether or not the symbol jack_set_latency_callback
+ * is null or not. If its null, it means that the JACK installed on this machine
+ * is too old to support this function. If it's not null, then Jill can use it
+ * just like any other function in the API. For example:
+ *
+ * \code
+ * if (jack_set_latency_callback) {
+ * jack_set_latency_callback (jill_client, jill_latency_callback, arg);
+ * }
+ * \endcode
+ *
+ * However, there are clients that may want to use this approach to parts of the
+ * the JACK API that predate 0.116.2. For example, they might want to see if even
+ * really old basic parts of the API like jack_client_open() exist at runtime.
+ *
+ * Such clients should include <jack/weakjack.h> before any other JACK header.
+ * This will make the \b entire JACK API be subject to weak linkage, so that any
+ * and all functions can be checked for existence at runtime. It is important
+ * to understand that very few clients need to do this - if you use this
+ * feature you should have a clear reason to do so.
+ *
+ *
+ */
+
+#ifdef __APPLE__
+#define WEAK_ATTRIBUTE weak_import
+#else
+#define WEAK_ATTRIBUTE __weak__
+#endif
+
+#ifndef JACK_OPTIONAL_WEAK_EXPORT
+/* JACK_OPTIONAL_WEAK_EXPORT needs to be a macro which
+ expands into a compiler directive. If non-null, the directive
+ must tell the compiler to arrange for weak linkage of
+ the symbol it used with. For this to work fully may
+ require linker arguments for the client as well.
+*/
+#ifdef __GNUC__
+#define JACK_OPTIONAL_WEAK_EXPORT __attribute__((WEAK_ATTRIBUTE))
+#else
+/* Add other things here for non-gcc platforms */
+#endif
+#endif
+
+#ifndef JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT
+/* JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT needs to be a macro
+ which expands into a compiler directive. If non-null, the directive
+ must tell the compiler to arrange for weak linkage of the
+ symbol it is used with AND optionally to mark the symbol
+ as deprecated. For this to work fully may require
+ linker arguments for the client as well.
+*/
+#ifdef __GNUC__
+#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT __attribute__((WEAK_ATTRIBUTE,__deprecated__))
+#else
+/* Add other things here for non-gcc platforms */
+#endif
+#endif
+
+/*@}*/
+
+#endif /* weakjack */
diff --git a/pipewire-jack/jack/weakmacros.h b/pipewire-jack/jack/weakmacros.h
new file mode 100644
index 0000000..944fddb
--- /dev/null
+++ b/pipewire-jack/jack/weakmacros.h
@@ -0,0 +1,97 @@
+/*
+ Copyright (C) 2010 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef __weakmacros_h__
+#define __weakmacros_h__
+
+/*************************************************************
+ * NOTE: JACK_WEAK_EXPORT ***MUST*** be used on every function
+ * added to the JACK API after the 0.116.2 release.
+ *
+ * Functions that predate this release are marked with
+ * JACK_WEAK_OPTIONAL_EXPORT which can be defined at compile
+ * time in a variety of ways. The default definition is empty,
+ * so that these symbols get normal linkage. If you wish to
+ * use all JACK symbols with weak linkage, include
+ * <jack/weakjack.h> before jack.h.
+ *************************************************************/
+
+#ifdef __APPLE__
+#define WEAK_ATTRIBUTE weak_import
+#else
+#define WEAK_ATTRIBUTE __weak__
+#endif
+
+#ifndef JACK_WEAK_EXPORT
+#ifdef __GNUC__
+/* JACK_WEAK_EXPORT needs to be a macro which
+ expands into a compiler directive. If non-null, the directive
+ must tell the compiler to arrange for weak linkage of
+ the symbol it used with. For this to work full may
+ require linker arguments in the client as well.
+*/
+
+#ifdef _WIN32
+ /*
+ Not working with __declspec(dllexport) so normal linking
+ Linking with JackWeakAPI.cpp will be the preferred way.
+ */
+ #define JACK_WEAK_EXPORT
+#else
+ #define JACK_WEAK_EXPORT __attribute__((WEAK_ATTRIBUTE))
+#endif
+
+#else
+/* Add other things here for non-gcc platforms */
+
+#ifdef _WIN32
+#define JACK_WEAK_EXPORT
+#endif
+
+#endif
+#endif
+
+#ifndef JACK_WEAK_EXPORT
+#define JACK_WEAK_EXPORT
+#endif
+
+#ifndef JACK_OPTIONAL_WEAK_EXPORT
+#define JACK_OPTIONAL_WEAK_EXPORT
+#endif
+
+#ifndef JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT
+#ifdef __GNUC__
+#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT __attribute__((__deprecated__))
+#else
+/* Add other things here for non-gcc platforms */
+
+#ifdef _WIN32
+#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT
+#endif
+
+#endif /* __GNUC__ */
+
+#ifndef JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT
+#define JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT
+#endif
+
+#endif
+
+#endif /* __weakmacros_h__ */
+
diff --git a/pipewire-jack/meson.build b/pipewire-jack/meson.build
new file mode 100644
index 0000000..fecc282
--- /dev/null
+++ b/pipewire-jack/meson.build
@@ -0,0 +1,5 @@
+jack_inc = include_directories('.')
+if get_option('jack-devel') == true
+ install_subdir('jack', install_dir: get_option('includedir'), strip_directory: false)
+endif
+subdir('src')
diff --git a/pipewire-jack/src/control.c b/pipewire-jack/src/control.c
new file mode 100644
index 0000000..da06e2c
--- /dev/null
+++ b/pipewire-jack/src/control.c
@@ -0,0 +1,472 @@
+/* PipeWire
+ *
+ * Copyright © 2021 Florian Hülsmann <fh@cbix.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <jack/control.h>
+#include <jack/jslist.h>
+
+#include <pipewire/pipewire.h>
+
+struct jackctl_sigmask
+{
+ sigset_t signals;
+};
+
+struct jackctl_sigmask sigmask;
+
+SPA_EXPORT
+jackctl_sigmask_t * jackctl_setup_signals(unsigned int flags)
+{
+ // stub
+ pw_log_warn("not implemented %d", flags);
+ sigemptyset(&sigmask.signals);
+ return &sigmask;
+}
+
+SPA_EXPORT
+void jackctl_wait_signals(jackctl_sigmask_t * signals)
+{
+ // stub
+ pw_log_warn("not implemented %p", signals);
+}
+
+SPA_EXPORT
+jackctl_server_t * jackctl_server_create(
+ bool (* on_device_acquire)(const char * device_name),
+ void (* on_device_release)(const char * device_name))
+{
+ pw_log_error("deprecated");
+ return jackctl_server_create2(on_device_acquire, on_device_release, NULL);
+}
+
+struct jackctl_server
+{
+ // stub
+ JSList * empty;
+ JSList * drivers;
+};
+
+struct jackctl_driver
+{
+ // stub
+};
+
+SPA_EXPORT
+jackctl_server_t * jackctl_server_create2(
+ bool (* on_device_acquire)(const char * device_name),
+ void (* on_device_release)(const char * device_name),
+ void (* on_device_reservation_loop)(void))
+{
+ // stub
+ pw_log_warn("not implemented %p %p %p", on_device_acquire, on_device_release, on_device_reservation_loop);
+
+ // setup server
+ jackctl_server_t * server;
+ server = (jackctl_server_t *)malloc(sizeof(jackctl_server_t));
+ if (server == NULL) {
+ return NULL;
+ }
+ server->empty = NULL;
+ server->drivers = NULL;
+
+ // setup dummy (default) driver
+ jackctl_driver_t * dummy;
+ dummy = (jackctl_driver_t *)malloc(sizeof(jackctl_driver_t));
+ if (dummy == NULL) {
+ free(server);
+ return NULL;
+ }
+ server->drivers = jack_slist_append (server->drivers, dummy);
+
+ return server;
+}
+
+SPA_EXPORT
+void jackctl_server_destroy(jackctl_server_t * server)
+{
+ // stub
+ pw_log_warn("%p: not implemented", server);
+
+ if (server) {
+ if (server->drivers) {
+ free(server->drivers->data);
+ }
+ jack_slist_free(server->empty);
+ jack_slist_free(server->drivers);
+ free(server);
+ }
+}
+
+SPA_EXPORT
+bool jackctl_server_open(jackctl_server_t * server, jackctl_driver_t * driver)
+{
+ // stub
+ pw_log_warn("%p: not implemented %p", server, driver);
+ return true;
+}
+
+SPA_EXPORT
+bool jackctl_server_start(jackctl_server_t * server)
+{
+ // stub
+ pw_log_warn("%p: not implemented", server);
+ return true;
+}
+
+SPA_EXPORT
+bool jackctl_server_stop(jackctl_server_t * server)
+{
+ // stub
+ pw_log_warn("%p: not implemented", server);
+ return false;
+}
+
+SPA_EXPORT
+bool jackctl_server_close(jackctl_server_t * server)
+{
+ // stub
+ pw_log_warn("%p: not implemented", server);
+ return false;
+}
+
+SPA_EXPORT
+const JSList * jackctl_server_get_drivers_list(jackctl_server_t * server)
+{
+ // stub
+ pw_log_warn("%p: not implemented", server);
+ if (server == NULL) {
+ pw_log_warn("server == NULL");
+ return NULL;
+ }
+ return server->drivers;
+}
+
+SPA_EXPORT
+const JSList * jackctl_server_get_parameters(jackctl_server_t * server)
+{
+ // stub
+ pw_log_warn("%p: not implemented", server);
+ if (server == NULL) {
+ return NULL;
+ }
+ return server->empty;
+}
+
+SPA_EXPORT
+const JSList * jackctl_server_get_internals_list(jackctl_server_t * server)
+{
+ // stub
+ pw_log_warn("%p: not implemented", server);
+ if (server == NULL) {
+ return NULL;
+ }
+ return server->empty;
+}
+
+SPA_EXPORT
+bool jackctl_server_load_internal(jackctl_server_t * server, jackctl_internal_t * internal)
+{
+ // stub
+ pw_log_warn("%p: not implemented %p", server, internal);
+ return true;
+}
+
+SPA_EXPORT
+bool jackctl_server_unload_internal(jackctl_server_t * server, jackctl_internal_t * internal)
+{
+ // stub
+ pw_log_warn("%p: not implemented %p", server, internal);
+ return true;
+}
+
+SPA_EXPORT
+bool jackctl_server_load_session_file(jackctl_server_t * server_ptr, const char * file)
+{
+ // stub
+ pw_log_warn("%p: not implemented %s", server_ptr, file);
+ return false;
+}
+
+SPA_EXPORT
+bool jackctl_server_add_slave(jackctl_server_t * server, jackctl_driver_t * driver)
+{
+ // stub
+ pw_log_warn("%p: not implemented %p", server, driver);
+ return false;
+}
+
+SPA_EXPORT
+bool jackctl_server_remove_slave(jackctl_server_t * server, jackctl_driver_t * driver)
+{
+ // stub
+ pw_log_warn("%p: not implemented %p", server, driver);
+ return false;
+}
+
+SPA_EXPORT
+bool jackctl_server_switch_master(jackctl_server_t * server, jackctl_driver_t * driver)
+{
+ // stub
+ pw_log_warn("%p: not implemented %p", server, driver);
+ return false;
+}
+
+
+SPA_EXPORT
+const char * jackctl_driver_get_name(jackctl_driver_t * driver)
+{
+ // stub
+ pw_log_warn("%p: not implemented", driver);
+ return "dummy";
+}
+
+SPA_EXPORT
+jackctl_driver_type_t jackctl_driver_get_type(jackctl_driver_t * driver)
+{
+ // stub
+ pw_log_warn("%p: not implemented", driver);
+ return (jackctl_driver_type_t)0;
+}
+
+SPA_EXPORT
+const JSList * jackctl_driver_get_parameters(jackctl_driver_t * driver)
+{
+ // stub
+ pw_log_warn("%p: not implemented", driver);
+ return NULL;
+}
+
+SPA_EXPORT
+int jackctl_driver_params_parse(jackctl_driver_t * driver, int argc, char* argv[])
+{
+ // stub
+ pw_log_warn("%p: not implemented %d %p", driver, argc, argv);
+ return 1;
+}
+
+SPA_EXPORT
+const char * jackctl_internal_get_name(jackctl_internal_t * internal)
+{
+ // stub
+ pw_log_warn("not implemented %p", internal);
+ return "pipewire-jack-stub";
+}
+
+SPA_EXPORT
+const JSList * jackctl_internal_get_parameters(jackctl_internal_t * internal)
+{
+ // stub
+ pw_log_warn("not implemented %p", internal);
+ return NULL;
+}
+
+SPA_EXPORT
+const char * jackctl_parameter_get_name(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ return "pipewire-jack-stub";
+}
+
+SPA_EXPORT
+const char * jackctl_parameter_get_short_description(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ return "pipewire-jack-stub";
+}
+
+SPA_EXPORT
+const char * jackctl_parameter_get_long_description(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ return "pipewire-jack-stub";
+}
+
+SPA_EXPORT
+jackctl_param_type_t jackctl_parameter_get_type(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ return (jackctl_param_type_t)0;
+}
+
+SPA_EXPORT
+char jackctl_parameter_get_id(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ return 0;
+}
+
+SPA_EXPORT
+bool jackctl_parameter_is_set(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ return false;
+}
+
+SPA_EXPORT
+bool jackctl_parameter_reset(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ return false;
+}
+
+SPA_EXPORT
+union jackctl_parameter_value jackctl_parameter_get_value(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ union jackctl_parameter_value value;
+ memset(&value, 0, sizeof(value));
+ return value;
+}
+
+SPA_EXPORT
+bool jackctl_parameter_set_value(
+ jackctl_parameter_t * parameter,
+ const union jackctl_parameter_value * value_ptr)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ return false;
+}
+
+SPA_EXPORT
+union jackctl_parameter_value jackctl_parameter_get_default_value(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ union jackctl_parameter_value value;
+ memset(&value, 0, sizeof(value));
+ return value;
+}
+
+SPA_EXPORT
+bool jackctl_parameter_has_range_constraint(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ return false;
+}
+
+SPA_EXPORT
+bool jackctl_parameter_has_enum_constraint(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ return false;
+}
+
+SPA_EXPORT
+uint32_t jackctl_parameter_get_enum_constraints_count(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("%p: not implemented", parameter);
+ return 0;
+}
+
+SPA_EXPORT
+union jackctl_parameter_value jackctl_parameter_get_enum_constraint_value(
+ jackctl_parameter_t * parameter,
+ uint32_t index)
+{
+ // stub
+ pw_log_warn("%p: not implemented %d", parameter, index);
+ union jackctl_parameter_value value;
+ memset(&value, 0, sizeof(value));
+ return value;
+}
+
+SPA_EXPORT
+const char * jackctl_parameter_get_enum_constraint_description(
+ jackctl_parameter_t * parameter,
+ uint32_t index)
+{
+ // stub
+ pw_log_warn("%p: not implemented %d", parameter, index);
+ return "pipewire-jack-stub";
+}
+
+SPA_EXPORT
+void jackctl_parameter_get_range_constraint(
+ jackctl_parameter_t * parameter,
+ union jackctl_parameter_value * min_ptr,
+ union jackctl_parameter_value * max_ptr)
+{
+ // stub
+ pw_log_warn("%p: not implemented %p %p", parameter, min_ptr, max_ptr);
+}
+
+SPA_EXPORT
+bool jackctl_parameter_constraint_is_strict(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("not implemented %p", parameter);
+ return false;
+}
+
+SPA_EXPORT
+bool jackctl_parameter_constraint_is_fake_value(jackctl_parameter_t * parameter)
+{
+ // stub
+ pw_log_warn("not implemented %p", parameter);
+ return false;
+}
+
+SPA_EXPORT SPA_PRINTF_FUNC(1, 2)
+void jack_error(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ pw_log_logv(SPA_LOG_LEVEL_ERROR, "", 0, "", format, args);
+ va_end(args);
+}
+
+SPA_EXPORT SPA_PRINTF_FUNC(1, 2)
+void jack_info(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ pw_log_logv(SPA_LOG_LEVEL_INFO, "", 0, "", format, args);
+ va_end(args);
+}
+
+SPA_EXPORT SPA_PRINTF_FUNC(1, 2)
+void jack_log(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ pw_log_logv(SPA_LOG_LEVEL_DEBUG, "", 0, "", format, args);
+ va_end(args);
+}
diff --git a/pipewire-jack/src/dummy.c b/pipewire-jack/src/dummy.c
new file mode 100644
index 0000000..1d6bb57
--- /dev/null
+++ b/pipewire-jack/src/dummy.c
@@ -0,0 +1,39 @@
+/* PipeWire
+ *
+ * Copyright © 2020 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <regex.h>
+#include <math.h>
+
+#include <pipewire/pipewire.h>
+
+static void reg(void) __attribute__ ((constructor));
+static void reg(void)
+{
+ pw_init(NULL, NULL);
+}
diff --git a/pipewire-jack/src/export.c b/pipewire-jack/src/export.c
new file mode 100644
index 0000000..4c9daf0
--- /dev/null
+++ b/pipewire-jack/src/export.c
@@ -0,0 +1,36 @@
+/* PipeWire
+ *
+ * Copyright © 2020 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ */
+
+#include <spa/utils/defs.h>
+
+#define JACK_METADATA_PREFIX "http://jackaudio.org/metadata/"
+SPA_EXPORT const char *JACK_METADATA_CONNECTED = JACK_METADATA_PREFIX "connected";
+SPA_EXPORT const char *JACK_METADATA_EVENT_TYPES = JACK_METADATA_PREFIX "event-types";
+SPA_EXPORT const char *JACK_METADATA_HARDWARE = JACK_METADATA_PREFIX "hardware";
+SPA_EXPORT const char *JACK_METADATA_ICON_LARGE = JACK_METADATA_PREFIX "icon-large";
+SPA_EXPORT const char *JACK_METADATA_ICON_NAME = JACK_METADATA_PREFIX "icon-name";
+SPA_EXPORT const char *JACK_METADATA_ICON_SMALL = JACK_METADATA_PREFIX "icon-small";
+SPA_EXPORT const char *JACK_METADATA_ORDER = JACK_METADATA_PREFIX "order";
+SPA_EXPORT const char *JACK_METADATA_PORT_GROUP = JACK_METADATA_PREFIX "port-group";
+SPA_EXPORT const char *JACK_METADATA_PRETTY_NAME = JACK_METADATA_PREFIX "pretty-name";
+SPA_EXPORT const char *JACK_METADATA_SIGNAL_TYPE = JACK_METADATA_PREFIX "signal-type";
+#undef JACK_METADATA_PREFIX
diff --git a/pipewire-jack/src/meson.build b/pipewire-jack/src/meson.build
new file mode 100644
index 0000000..20d1ccf
--- /dev/null
+++ b/pipewire-jack/src/meson.build
@@ -0,0 +1,98 @@
+pipewire_jack_sources = [
+ 'export.c',
+ 'pipewire-jack.c',
+ 'ringbuffer.c',
+ 'uuid.c',
+]
+
+pipewire_jackserver_sources = pipewire_jack_sources
+pipewire_jackserver_sources += [
+ 'control.c',
+]
+
+pipewire_net_sources = [
+ 'net.c',
+]
+pipewire_jack_c_args = [
+ '-DPIC',
+]
+
+libjack_path = get_option('libjack-path')
+if libjack_path == ''
+ libjack_path = modules_install_dir / 'jack'
+ libjack_path_dlopen = modules_install_dir_dlopen / 'jack'
+else
+ libjack_path_dlopen = libjack_path
+endif
+
+tools_config = configuration_data()
+tools_config.set('LIBJACK_PATH', libjack_path_dlopen)
+
+configure_file(input : 'pw-jack.in',
+ output : 'pw-jack',
+ configuration : tools_config,
+ install_dir : pipewire_bindir)
+
+pipewire_jack = shared_library('jack',
+ pipewire_jack_sources,
+ soversion : soversion,
+ version : libversion,
+ c_args : pipewire_jack_c_args,
+ include_directories : [configinc, jack_inc],
+ dependencies : [pipewire_dep, mathlib],
+ install : true,
+ install_dir : libjack_path,
+)
+
+pipewire_jackserver = shared_library('jackserver',
+ pipewire_jackserver_sources,
+ soversion : soversion,
+ version : libversion,
+ c_args : pipewire_jack_c_args,
+ include_directories : [configinc, jack_inc],
+ dependencies : [pipewire_dep, mathlib],
+ install : true,
+ install_dir : libjack_path,
+)
+
+pipewire_jacknet = shared_library('jacknet',
+ pipewire_net_sources,
+ soversion : soversion,
+ version : libversion,
+ c_args : pipewire_jack_c_args,
+ include_directories : [configinc, jack_inc],
+ dependencies : [pipewire_dep, mathlib],
+ install : true,
+ install_dir : libjack_path,
+)
+
+
+if get_option('jack-devel') == true
+ if meson.version().version_compare('<0.59.0')
+ error(
+ '''
+ Before version 0.59.0 Meson creates a wrong jack pkg-config file.
+ For that reason this is now an error. Please update Meson,
+ if you want to have JACK development files.
+ ''')
+ endif
+
+ pkgconfig.generate(filebase : 'jack',
+ libraries : [pipewire_jack],
+ name : 'jack',
+ description : 'PipeWire JACK API',
+ version : '1.9.17',
+ extra_cflags : '-D_REENTRANT',
+ unescaped_variables: ['server_libs=-L${libdir} -ljackserver', 'jack_implementation=pipewire'])
+endif
+
+if sdl_dep.found()
+ executable('video-dsp-play',
+ '../examples/video-dsp-play.c',
+ include_directories : [jack_inc],
+ install : installed_tests_enabled,
+ install_dir : installed_tests_execdir / 'examples' / 'jack',
+ dependencies : [sdl_dep, mathlib],
+ link_with: pipewire_jack,
+ )
+endif
diff --git a/pipewire-jack/src/metadata.c b/pipewire-jack/src/metadata.c
new file mode 100644
index 0000000..da3d75f
--- /dev/null
+++ b/pipewire-jack/src/metadata.c
@@ -0,0 +1,421 @@
+/* PipeWire
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <spa/utils/string.h>
+
+#include <jack/metadata.h>
+#include <jack/uuid.h>
+
+#include <pipewire/pipewire.h>
+#include <pipewire/extensions/metadata.h>
+
+static jack_description_t *find_description(jack_uuid_t subject)
+{
+ jack_description_t *desc;
+ pw_array_for_each(desc, &globals.descriptions) {
+ if (jack_uuid_compare(desc->subject, subject) == 0)
+ return desc;
+ }
+ return NULL;
+}
+
+static void set_property(jack_property_t *prop, const char *key, const char *value, const char *type)
+{
+ prop->key = strdup(key);
+ prop->data = strdup(value);
+ prop->type = strdup(type);
+}
+
+static void clear_property(jack_property_t *prop)
+{
+ free((char*)prop->key);
+ free((char*)prop->data);
+ free((char*)prop->type);
+}
+
+static jack_property_t *copy_properties(jack_property_t *src, uint32_t cnt)
+{
+ jack_property_t *dst;
+ uint32_t i;
+ dst = malloc(sizeof(jack_property_t) * cnt);
+ if (dst != NULL) {
+ for (i = 0; i < cnt; i++)
+ set_property(&dst[i], src[i].key, src[i].data, src[i].type);
+ }
+ return dst;
+}
+
+static int copy_description(jack_description_t *dst, jack_description_t *src)
+{
+ dst->properties = copy_properties(src->properties, src->property_cnt);
+ if (dst->properties == NULL)
+ return -errno;
+ jack_uuid_copy(&dst->subject, src->subject);
+ dst->property_cnt = src->property_cnt;
+ dst->property_size = src->property_size;
+ return dst->property_cnt;
+}
+
+static jack_description_t *add_description(jack_uuid_t subject)
+{
+ jack_description_t *desc;
+ desc = pw_array_add(&globals.descriptions, sizeof(*desc));
+ if (desc != NULL) {
+ spa_zero(*desc);
+ jack_uuid_copy(&desc->subject, subject);
+ }
+ return desc;
+}
+
+static void remove_description(jack_description_t *desc)
+{
+ jack_free_description(desc, false);
+ pw_array_remove(&globals.descriptions, desc);
+}
+
+static jack_property_t *find_property(jack_description_t *desc, const char *key)
+{
+ uint32_t i;
+ for (i = 0; i < desc->property_cnt; i++) {
+ jack_property_t *prop = &desc->properties[i];
+ if (spa_streq(prop->key, key))
+ return prop;
+ }
+ return NULL;
+}
+
+static jack_property_t *add_property(jack_description_t *desc, const char *key,
+ const char *value, const char *type)
+{
+ jack_property_t *prop;
+ void *np;
+ size_t ns;
+
+ if (desc->property_cnt == desc->property_size) {
+ ns = desc->property_size > 0 ? desc->property_size * 2 : 8;
+ np = pw_reallocarray(desc->properties, ns, sizeof(*prop));
+ if (np == NULL)
+ return NULL;
+ desc->property_size = ns;
+ desc->properties = np;
+ }
+ prop = &desc->properties[desc->property_cnt++];
+ set_property(prop, key, value, type);
+ return prop;
+}
+
+static void remove_property(jack_description_t *desc, jack_property_t *prop)
+{
+ clear_property(prop);
+ desc->property_cnt--;
+ memmove(desc->properties, SPA_PTROFF(prop, sizeof(*prop), void),
+ SPA_PTRDIFF(SPA_PTROFF(desc->properties, sizeof(*prop) * desc->property_cnt, void),
+ prop));
+
+ if (desc->property_cnt == 0)
+ remove_description(desc);
+}
+
+static int change_property(jack_property_t *prop, const char *value, const char *type)
+{
+ int changed = 0;
+ if (!spa_streq(prop->data, value)) {
+ free((char*)prop->data);
+ prop->data = strdup(value);
+ changed++;
+ }
+ if (!spa_streq(prop->type, type)) {
+ free((char*)prop->type);
+ prop->type = strdup(type);
+ changed++;
+ }
+ return changed;
+}
+
+static int update_property(struct client *c,
+ jack_uuid_t subject,
+ const char* key,
+ const char* type,
+ const char* value)
+{
+ jack_property_change_t change;
+ jack_description_t *desc;
+ int changed = 0;
+
+ pthread_mutex_lock(&globals.lock);
+ desc = find_description(subject);
+
+ if (key == NULL) {
+ if (desc != NULL) {
+ remove_description(desc);
+ change = PropertyDeleted;
+ changed++;
+ }
+ } else {
+ jack_property_t *prop;
+
+ prop = desc ? find_property(desc, key) : NULL;
+
+ if (value == NULL || type == NULL) {
+ if (prop != NULL) {
+ remove_property(desc, prop);
+ change = PropertyDeleted;
+ changed++;
+ }
+ } else if (prop == NULL) {
+ if (desc == NULL)
+ desc = add_description(subject);
+ if (desc == NULL) {
+ changed = -errno;
+ pw_log_warn("add_description failed: %m");
+ } else if (add_property(desc, key, value, type) == NULL) {
+ changed = -errno;
+ pw_log_warn("add_property failed: %m");
+ } else {
+ change = PropertyCreated;
+ changed++;
+ }
+ } else {
+ changed = change_property(prop, value, type);
+ change = PropertyChanged;
+ }
+ }
+ pthread_mutex_unlock(&globals.lock);
+
+ if (c->property_callback && changed > 0) {
+ pw_log_info("emit %"PRIu64" %s", (uint64_t)subject, key);
+ c->property_callback(subject, key, change, c->property_arg);
+ }
+ return changed;
+}
+
+
+SPA_EXPORT
+int jack_set_property(jack_client_t*client,
+ jack_uuid_t subject,
+ const char* key,
+ const char* value,
+ const char* type)
+{
+ struct client *c = (struct client *) client;
+ struct object *o;
+ uint32_t serial;
+ int res = -1;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+ spa_return_val_if_fail(key != NULL, -EINVAL);
+ spa_return_val_if_fail(value != NULL, -EINVAL);
+
+ pw_thread_loop_lock(c->context.loop);
+ if (c->metadata == NULL)
+ goto done;
+
+ if (subject & (1<<30))
+ goto done;
+
+ serial = jack_uuid_to_index(subject);
+ if ((o = find_by_serial(c, serial)) == NULL)
+ goto done;
+
+ if (type == NULL)
+ type = "";
+
+ pw_log_info("set id:%u (%"PRIu64") '%s' to '%s@%s'", o->id, subject, key, value, type);
+ if (update_property(c, subject, key, type, value))
+ pw_metadata_set_property(c->metadata->proxy, o->id, key, type, value);
+ res = 0;
+done:
+ pw_thread_loop_unlock(c->context.loop);
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_get_property(jack_uuid_t subject,
+ const char* key,
+ char** value,
+ char** type)
+{
+ jack_description_t *desc;
+ jack_property_t *prop;
+ int res = -1;
+
+ pthread_mutex_lock(&globals.lock);
+ desc = find_description(subject);
+ if (desc == NULL)
+ goto done;
+
+ prop = find_property(desc, key);
+ if (prop == NULL)
+ goto done;
+
+ *value = strdup(prop->data);
+ *type = strdup(prop->type);
+ res = 0;
+
+ pw_log_debug("subject:%"PRIu64" key:'%s' value:'%s' type:'%s'",
+ subject, key, *value, *type);
+done:
+ pthread_mutex_unlock(&globals.lock);
+ return res;
+}
+
+SPA_EXPORT
+void jack_free_description (jack_description_t* desc, int free_description_itself)
+{
+ uint32_t n;
+
+ for (n = 0; n < desc->property_cnt; ++n)
+ clear_property(&desc->properties[n]);
+ free(desc->properties);
+ if (free_description_itself)
+ free(desc);
+}
+
+SPA_EXPORT
+int jack_get_properties (jack_uuid_t subject,
+ jack_description_t* desc)
+{
+ jack_description_t *d;
+ int res = -1;
+
+ spa_return_val_if_fail(desc != NULL, -EINVAL);
+
+ pthread_mutex_lock(&globals.lock);
+ d = find_description(subject);
+ if (d == NULL)
+ goto done;
+
+ res = copy_description(desc, d);
+done:
+ pthread_mutex_unlock(&globals.lock);
+ return res;
+}
+
+SPA_EXPORT
+int jack_get_all_properties (jack_description_t** result)
+{
+ uint32_t i;
+ jack_description_t *dst, *src;
+ struct pw_array *descriptions;
+ uint32_t len;
+
+ pthread_mutex_lock(&globals.lock);
+ descriptions = &globals.descriptions;
+ len = pw_array_get_len(descriptions, jack_description_t);
+ src = descriptions->data;
+ dst = malloc(descriptions->size);
+ for (i = 0; i < len; i++)
+ copy_description(&dst[i], &src[i]);
+ *result = dst;
+ pthread_mutex_unlock(&globals.lock);
+
+ return len;
+}
+
+SPA_EXPORT
+int jack_remove_property (jack_client_t* client, jack_uuid_t subject, const char* key)
+{
+ struct client *c = (struct client *) client;
+ uint32_t id;
+ int res = -1;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+ spa_return_val_if_fail(key != NULL, -EINVAL);
+
+ pw_thread_loop_lock(c->context.loop);
+
+ if (c->metadata == NULL)
+ goto done;
+
+ id = jack_uuid_to_index(subject);
+
+ pw_log_info("remove id:%u (%"PRIu64") '%s'", id, subject, key);
+ pw_metadata_set_property(c->metadata->proxy,
+ id, key, NULL, NULL);
+ res = 0;
+done:
+ pw_thread_loop_unlock(c->context.loop);
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_remove_properties (jack_client_t* client, jack_uuid_t subject)
+{
+ struct client *c = (struct client *) client;
+ uint32_t id;
+ int res = -1;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ pw_thread_loop_lock(c->context.loop);
+ if (c->metadata == NULL)
+ goto done;
+
+ id = jack_uuid_to_index(subject);
+
+ pw_log_info("remove id:%u (%"PRIu64")", id, subject);
+ pw_metadata_set_property(c->metadata->proxy,
+ id, NULL, NULL, NULL);
+ res = 0;
+done:
+ pw_thread_loop_unlock(c->context.loop);
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_remove_all_properties (jack_client_t* client)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ pw_thread_loop_lock(c->context.loop);
+ pw_metadata_clear(c->metadata->proxy);
+ pw_thread_loop_unlock(c->context.loop);
+
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_property_change_callback (jack_client_t* client,
+ JackPropertyChangeCallback callback,
+ void* arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ c->property_callback = callback;
+ c->property_arg = arg;
+ return 0;
+}
diff --git a/pipewire-jack/src/net.c b/pipewire-jack/src/net.c
new file mode 100644
index 0000000..e48b76b
--- /dev/null
+++ b/pipewire-jack/src/net.c
@@ -0,0 +1,169 @@
+/* PipeWire
+ *
+ * Copyright © 2022 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <jack/net.h>
+
+#include <pipewire/pipewire.h>
+
+SPA_EXPORT
+jack_net_slave_t* jack_net_slave_open(const char* ip, int port, const char* name,
+ jack_slave_t* request, jack_master_t* result)
+{
+ return NULL;
+}
+
+SPA_EXPORT
+int jack_net_slave_close(jack_net_slave_t* net)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_set_net_slave_process_callback(jack_net_slave_t * net, JackNetSlaveProcessCallback net_callback, void *arg)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_net_slave_activate(jack_net_slave_t* net)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_net_slave_deactivate(jack_net_slave_t* net)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_net_slave_is_active(jack_net_slave_t* net)
+{
+ return false;
+}
+
+SPA_EXPORT
+int jack_set_net_slave_buffer_size_callback(jack_net_slave_t *net, JackNetSlaveBufferSizeCallback bufsize_callback, void *arg)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_set_net_slave_sample_rate_callback(jack_net_slave_t *net, JackNetSlaveSampleRateCallback samplerate_callback, void *arg)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_set_net_slave_shutdown_callback(jack_net_slave_t *net, JackNetSlaveShutdownCallback shutdown_callback, void *arg)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_set_net_slave_restart_callback(jack_net_slave_t *net, JackNetSlaveRestartCallback restart_callback, void *arg)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_set_net_slave_error_callback(jack_net_slave_t *net, JackNetSlaveErrorCallback error_callback, void *arg)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+jack_net_master_t* jack_net_master_open(const char* ip, int port, jack_master_t* request, jack_slave_t* result)
+{
+ return NULL;
+}
+
+SPA_EXPORT
+int jack_net_master_close(jack_net_master_t* net)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_net_master_recv(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_net_master_recv_slice(jack_net_master_t* net, int audio_input, float** audio_input_buffer, int midi_input, void** midi_input_buffer, int frames)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_net_master_send(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_net_master_send_slice(jack_net_master_t* net, int audio_output, float** audio_output_buffer, int midi_output, void** midi_output_buffer, int frames)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+jack_adapter_t* jack_create_adapter(int input, int output,
+ jack_nframes_t host_buffer_size,
+ jack_nframes_t host_sample_rate,
+ jack_nframes_t adapted_buffer_size,
+ jack_nframes_t adapted_sample_rate)
+{
+ return NULL;
+}
+
+SPA_EXPORT
+int jack_destroy_adapter(jack_adapter_t* adapter)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+void jack_flush_adapter(jack_adapter_t* adapter)
+{
+}
+
+SPA_EXPORT
+int jack_adapter_push_and_pull(jack_adapter_t* adapter, float** input, float** output, unsigned int frames)
+{
+ return ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_adapter_pull_and_push(jack_adapter_t* adapter, float** input, float** output, unsigned int frames)
+{
+ return ENOTSUP;
+}
diff --git a/pipewire-jack/src/pipewire-jack-extensions.h b/pipewire-jack/src/pipewire-jack-extensions.h
new file mode 100644
index 0000000..8e38e9e
--- /dev/null
+++ b/pipewire-jack/src/pipewire-jack-extensions.h
@@ -0,0 +1,50 @@
+/* PipeWire JACK extensions
+ *
+ * Copyright © 2020 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef PIPEWIRE_JACK_EXTENSIONS_H
+#define PIPEWIRE_JACK_EXTENSIONS_H
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** 1.0 gamma, full range HDR 0.0 -> 1.0, pre-multiplied
+ * alpha, BT.2020 primaries, progressive */
+#define JACK_DEFAULT_VIDEO_TYPE "32 bit float RGBA video"
+
+typedef struct jack_image_size {
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uint32_t flags;
+} jack_image_size_t;
+
+int jack_get_video_image_size(jack_client_t *client, jack_image_size_t *size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PIPEWIRE_JACK_EXTENSIONS_H */
diff --git a/pipewire-jack/src/pipewire-jack.c b/pipewire-jack/src/pipewire-jack.c
new file mode 100644
index 0000000..2e82677
--- /dev/null
+++ b/pipewire-jack/src/pipewire-jack.c
@@ -0,0 +1,6509 @@
+/* PipeWire
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <regex.h>
+#include <math.h>
+
+#include <jack/jack.h>
+#include <jack/session.h>
+#include <jack/thread.h>
+#include <jack/midiport.h>
+#include <jack/uuid.h>
+#include <jack/metadata.h>
+
+#include <spa/support/cpu.h>
+#include <spa/param/audio/format-utils.h>
+#include <spa/param/video/format-utils.h>
+#include <spa/debug/types.h>
+#include <spa/debug/pod.h>
+#include <spa/utils/json.h>
+#include <spa/utils/string.h>
+
+#include <pipewire/pipewire.h>
+#include <pipewire/private.h>
+#include <pipewire/thread.h>
+#include <pipewire/data-loop.h>
+
+#include "pipewire/extensions/client-node.h"
+#include "pipewire/extensions/metadata.h"
+#include "pipewire-jack-extensions.h"
+
+#define JACK_DEFAULT_VIDEO_TYPE "32 bit float RGBA video"
+
+/* use 512KB stack per thread - the default is way too high to be feasible
+ * with mlockall() on many systems */
+#define THREAD_STACK 524288
+
+#define DEFAULT_RT_MAX 88
+
+#define JACK_CLIENT_NAME_SIZE 256
+#define JACK_PORT_NAME_SIZE 256
+#define JACK_PORT_TYPE_SIZE 32
+#define MONITOR_EXT " Monitor"
+
+#define MAX_MIX 1024
+#define MAX_BUFFER_FRAMES 8192
+
+#define MAX_ALIGN 16
+#define MAX_BUFFERS 2
+#define MAX_BUFFER_DATAS 1u
+
+#define REAL_JACK_PORT_NAME_SIZE (JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE)
+
+PW_LOG_TOPIC_STATIC(jack_log_topic, "jack");
+#define PW_LOG_TOPIC_DEFAULT jack_log_topic
+
+#define TYPE_ID_AUDIO 0
+#define TYPE_ID_MIDI 1
+#define TYPE_ID_VIDEO 2
+#define TYPE_ID_OTHER 3
+
+#define SELF_CONNECT_ALLOW 0
+#define SELF_CONNECT_FAIL_EXT -1
+#define SELF_CONNECT_IGNORE_EXT 1
+#define SELF_CONNECT_FAIL_ALL -2
+#define SELF_CONNECT_IGNORE_ALL 2
+
+struct client;
+struct port;
+
+struct globals {
+ jack_thread_creator_t creator;
+ pthread_mutex_t lock;
+ struct pw_array descriptions;
+ struct spa_list free_objects;
+ struct spa_thread_utils *thread_utils;
+};
+
+static struct globals globals;
+static bool mlock_warned = false;
+
+#define OBJECT_CHUNK 8
+#define RECYCLE_THRESHOLD 128
+
+typedef void (*mix_func) (float *dst, float *src[], uint32_t n_src, bool aligned, uint32_t n_samples);
+
+static mix_func mix_function;
+
+struct object {
+ struct spa_list link;
+
+ struct client *client;
+
+#define INTERFACE_Port 0
+#define INTERFACE_Node 1
+#define INTERFACE_Link 2
+ uint32_t type;
+ uint32_t id;
+ uint32_t serial;
+
+ union {
+ struct {
+ char name[JACK_CLIENT_NAME_SIZE+1];
+ char node_name[512];
+ int32_t priority;
+ uint32_t client_id;
+ } node;
+ struct {
+ uint32_t src;
+ uint32_t dst;
+ uint32_t src_serial;
+ uint32_t dst_serial;
+ bool src_ours;
+ bool dst_ours;
+ bool is_complete;
+ struct port *our_input;
+ struct port *our_output;
+ } port_link;
+ struct {
+ unsigned long flags;
+ char name[REAL_JACK_PORT_NAME_SIZE+1];
+ char alias1[REAL_JACK_PORT_NAME_SIZE+1];
+ char alias2[REAL_JACK_PORT_NAME_SIZE+1];
+ char system[REAL_JACK_PORT_NAME_SIZE+1];
+ uint32_t system_id;
+ uint32_t type_id;
+ uint32_t node_id;
+ uint32_t monitor_requests;
+ int32_t priority;
+ struct port *port;
+ bool is_monitor;
+ struct object *node;
+ struct spa_latency_info latency[2];
+ } port;
+ };
+ struct pw_proxy *proxy;
+ struct spa_hook proxy_listener;
+ struct spa_hook object_listener;
+ unsigned int removing:1;
+ unsigned int removed:1;
+};
+
+struct midi_buffer {
+#define MIDI_BUFFER_MAGIC 0x900df00d
+ uint32_t magic;
+ int32_t buffer_size;
+ uint32_t nframes;
+ int32_t write_pos;
+ uint32_t event_count;
+ uint32_t lost_events;
+};
+
+#define MIDI_INLINE_MAX 4
+
+struct midi_event {
+ uint16_t time;
+ uint16_t size;
+ union {
+ uint32_t byte_offset;
+ uint8_t inline_data[MIDI_INLINE_MAX];
+ };
+};
+
+struct buffer {
+ struct spa_list link;
+#define BUFFER_FLAG_OUT (1<<0)
+#define BUFFER_FLAG_MAPPED (1<<1)
+ uint32_t flags;
+ uint32_t id;
+
+ struct spa_data datas[MAX_BUFFER_DATAS];
+ uint32_t n_datas;
+
+ struct pw_memmap *mem[MAX_BUFFER_DATAS+1];
+ uint32_t n_mem;
+};
+
+struct mix {
+ struct spa_list link;
+ struct spa_list port_link;
+ uint32_t id;
+ uint32_t peer_id;
+ struct port *port;
+ struct port *peer_port;
+
+ struct spa_io_buffers *io;
+
+ struct buffer buffers[MAX_BUFFERS];
+ uint32_t n_buffers;
+ struct spa_list queue;
+};
+
+struct port {
+ bool valid;
+ struct spa_list link;
+
+ struct client *client;
+
+ enum spa_direction direction;
+ uint32_t port_id;
+ struct object *object;
+ struct pw_properties *props;
+ struct spa_port_info info;
+#define IDX_EnumFormat 0
+#define IDX_Buffers 1
+#define IDX_IO 2
+#define IDX_Format 3
+#define IDX_Latency 4
+#define N_PORT_PARAMS 5
+ struct spa_param_info params[N_PORT_PARAMS];
+
+ struct spa_io_buffers io;
+ struct spa_list mix;
+ struct mix *global_mix;
+
+ unsigned int empty_out:1;
+ unsigned int zeroed:1;
+
+ float *emptyptr;
+ float empty[MAX_BUFFER_FRAMES + MAX_ALIGN];
+
+ void *(*get_buffer) (struct port *p, jack_nframes_t frames);
+};
+
+struct link {
+ struct spa_list link;
+ struct spa_list target_link;
+ struct client *client;
+ uint32_t node_id;
+ struct pw_memmap *mem;
+ struct pw_node_activation *activation;
+ int signalfd;
+};
+
+struct context {
+ struct pw_loop *l;
+ struct pw_thread_loop *loop; /* thread_lock protects all below */
+ struct pw_context *context;
+
+ struct spa_thread_utils *old_thread_utils;
+ struct spa_thread_utils thread_utils;
+ pthread_mutex_t lock; /* protects map and lists below, in addition to thread_lock */
+ struct spa_list objects;
+ uint32_t free_count;
+};
+
+#define GET_DIRECTION(f) ((f) & JackPortIsInput ? SPA_DIRECTION_INPUT : SPA_DIRECTION_OUTPUT)
+
+#define GET_PORT(c,d,p) (pw_map_lookup(&c->ports[d], p))
+
+struct metadata {
+ struct pw_metadata *proxy;
+ struct spa_hook proxy_listener;
+ struct spa_hook listener;
+
+ char default_audio_sink[1024];
+ char default_audio_source[1024];
+};
+
+struct client {
+ char name[JACK_CLIENT_NAME_SIZE+1];
+
+ struct context context;
+
+ char *server_name;
+ char *load_name; /* load module name */
+ char *load_init; /* initialization string */
+ jack_uuid_t session_id; /* requested session_id */
+
+ struct pw_data_loop *loop;
+ struct pw_properties *props;
+
+ struct pw_core *core;
+ struct spa_hook core_listener;
+ struct pw_mempool *pool;
+ int pending_sync;
+ int last_sync;
+ int last_res;
+
+ struct spa_node_info info;
+
+ struct pw_registry *registry;
+ struct spa_hook registry_listener;
+
+ struct pw_client_node *node;
+ struct spa_hook node_listener;
+ struct spa_hook proxy_listener;
+
+ struct metadata *metadata;
+ struct metadata *settings;
+
+ uint32_t node_id;
+ uint32_t serial;
+ struct spa_source *socket_source;
+
+ JackThreadCallback thread_callback;
+ void *thread_arg;
+ JackThreadInitCallback thread_init_callback;
+ void *thread_init_arg;
+ JackShutdownCallback shutdown_callback;
+ void *shutdown_arg;
+ JackInfoShutdownCallback info_shutdown_callback;
+ void *info_shutdown_arg;
+ JackProcessCallback process_callback;
+ void *process_arg;
+ JackFreewheelCallback freewheel_callback;
+ void *freewheel_arg;
+ JackBufferSizeCallback bufsize_callback;
+ void *bufsize_arg;
+ JackSampleRateCallback srate_callback;
+ void *srate_arg;
+ JackClientRegistrationCallback registration_callback;
+ void *registration_arg;
+ JackPortRegistrationCallback portregistration_callback;
+ void *portregistration_arg;
+ JackPortConnectCallback connect_callback;
+ void *connect_arg;
+ JackPortRenameCallback rename_callback;
+ void *rename_arg;
+ JackGraphOrderCallback graph_callback;
+ void *graph_arg;
+ JackXRunCallback xrun_callback;
+ void *xrun_arg;
+ JackLatencyCallback latency_callback;
+ void *latency_arg;
+ JackSyncCallback sync_callback;
+ void *sync_arg;
+ JackTimebaseCallback timebase_callback;
+ void *timebase_arg;
+ JackPropertyChangeCallback property_callback;
+ void *property_arg;
+
+ struct spa_io_position *position;
+ uint32_t sample_rate;
+ uint32_t buffer_frames;
+ struct spa_fraction latency;
+
+ struct spa_list mix;
+ struct spa_list free_mix;
+
+ struct spa_list free_ports;
+ struct pw_map ports[2];
+
+ struct spa_list links;
+ uint32_t driver_id;
+ struct pw_node_activation *driver_activation;
+
+ struct pw_memmap *mem;
+ struct pw_node_activation *activation;
+ uint32_t xrun_count;
+
+ struct {
+ struct spa_io_position *position;
+ struct pw_node_activation *driver_activation;
+ struct spa_list target_links;
+ } rt;
+
+ pthread_mutex_t rt_lock;
+ unsigned int rt_locked:1;
+ unsigned int data_locked:1;
+
+ unsigned int started:1;
+ unsigned int active:1;
+ unsigned int destroyed:1;
+ unsigned int first:1;
+ unsigned int thread_entered:1;
+ unsigned int has_transport:1;
+ unsigned int allow_mlock:1;
+ unsigned int warn_mlock:1;
+ unsigned int timeowner_conditional:1;
+ unsigned int show_monitor:1;
+ unsigned int merge_monitor:1;
+ unsigned int short_name:1;
+ unsigned int filter_name:1;
+ unsigned int freewheeling:1;
+ unsigned int locked_process:1;
+ unsigned int default_as_system:1;
+ int self_connect_mode;
+ int rt_max;
+ unsigned int fix_midi_events:1;
+ unsigned int global_buffer_size:1;
+ char filter_char;
+
+ jack_position_t jack_position;
+ jack_transport_state_t jack_state;
+};
+
+static int do_sync(struct client *client);
+static struct object *find_by_serial(struct client *c, uint32_t serial);
+
+#include "metadata.c"
+
+int pw_jack_match_rules(const char *rules, size_t size, const struct spa_dict *props,
+ int (*matched) (void *data, const char *action, const char *val, int len),
+ void *data);
+
+static struct object * alloc_object(struct client *c, int type)
+{
+ struct object *o;
+ int i;
+
+ pthread_mutex_lock(&globals.lock);
+ if (spa_list_is_empty(&globals.free_objects)) {
+ o = calloc(OBJECT_CHUNK, sizeof(struct object));
+ if (o == NULL) {
+ pthread_mutex_unlock(&globals.lock);
+ return NULL;
+ }
+ for (i = 0; i < OBJECT_CHUNK; i++)
+ spa_list_append(&globals.free_objects, &o[i].link);
+ }
+ o = spa_list_first(&globals.free_objects, struct object, link);
+ spa_list_remove(&o->link);
+ pthread_mutex_unlock(&globals.lock);
+
+ o->client = c;
+ o->removed = false;
+ o->type = type;
+ pw_log_debug("%p: object:%p type:%d", c, o, type);
+
+ return o;
+}
+
+static void recycle_objects(struct client *c, uint32_t remain)
+{
+ struct object *o, *t;
+ pthread_mutex_lock(&globals.lock);
+ spa_list_for_each_safe(o, t, &c->context.objects, link) {
+ if (o->removed) {
+ pw_log_info("%p: recycle object:%p type:%d id:%u/%u",
+ c, o, o->type, o->id, o->serial);
+ spa_list_remove(&o->link);
+ memset(o, 0, sizeof(struct object));
+ spa_list_append(&globals.free_objects, &o->link);
+ if (--c->context.free_count == remain)
+ break;
+ }
+ }
+ pthread_mutex_unlock(&globals.lock);
+}
+
+/* JACK clients expect the objects to hang around after
+ * they are unregistered and freed. We mark the object removed and
+ * move it to the end of the queue. */
+static void free_object(struct client *c, struct object *o)
+{
+ pw_log_debug("%p: object:%p type:%d", c, o, o->type);
+ pthread_mutex_lock(&c->context.lock);
+ spa_list_remove(&o->link);
+ o->removed = true;
+ o->id = SPA_ID_INVALID;
+ spa_list_append(&c->context.objects, &o->link);
+ if (++c->context.free_count > RECYCLE_THRESHOLD)
+ recycle_objects(c, RECYCLE_THRESHOLD / 2);
+ pthread_mutex_unlock(&c->context.lock);
+
+}
+
+static void init_mix(struct mix *mix, uint32_t mix_id, struct port *port)
+{
+ mix->id = mix_id;
+ mix->port = port;
+ mix->io = NULL;
+ mix->n_buffers = 0;
+ spa_list_init(&mix->queue);
+ if (mix_id == SPA_ID_INVALID)
+ port->global_mix = mix;
+}
+static struct mix *find_mix_peer(struct client *c, uint32_t peer_id)
+{
+ struct mix *mix;
+ spa_list_for_each(mix, &c->mix, link) {
+ if (mix->peer_id == peer_id)
+ return mix;
+ }
+ return NULL;
+}
+
+static struct mix *find_mix(struct client *c, struct port *port, uint32_t mix_id)
+{
+ struct mix *mix;
+
+ spa_list_for_each(mix, &port->mix, port_link) {
+ if (mix->id == mix_id)
+ return mix;
+ }
+ return NULL;
+}
+
+static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_id)
+{
+ struct mix *mix;
+ uint32_t i;
+
+ if ((mix = find_mix(c, port, mix_id)) != NULL)
+ return mix;
+
+ if (spa_list_is_empty(&c->free_mix)) {
+ mix = calloc(OBJECT_CHUNK, sizeof(struct mix));
+ if (mix == NULL)
+ return NULL;
+ for (i = 0; i < OBJECT_CHUNK; i++)
+ spa_list_append(&c->free_mix, &mix[i].link);
+ }
+ mix = spa_list_first(&c->free_mix, struct mix, link);
+ spa_list_remove(&mix->link);
+ spa_list_append(&c->mix, &mix->link);
+
+ spa_list_append(&port->mix, &mix->port_link);
+
+ init_mix(mix, mix_id, port);
+
+ return mix;
+}
+
+static int clear_buffers(struct client *c, struct mix *mix)
+{
+ struct port *port = mix->port;
+ struct buffer *b;
+ uint32_t i, j;
+
+ pw_log_debug("%p: port %p clear buffers", c, port);
+
+ for (i = 0; i < mix->n_buffers; i++) {
+ b = &mix->buffers[i];
+
+ for (j = 0; j < b->n_mem; j++)
+ pw_memmap_free(b->mem[j]);
+
+ b->n_mem = 0;
+ }
+ mix->n_buffers = 0;
+ spa_list_init(&mix->queue);
+ return 0;
+}
+
+static void free_mix(struct client *c, struct mix *mix)
+{
+ clear_buffers(c, mix);
+ spa_list_remove(&mix->port_link);
+ if (mix->id == SPA_ID_INVALID)
+ mix->port->global_mix = NULL;
+ spa_list_remove(&mix->link);
+ spa_list_append(&c->free_mix, &mix->link);
+}
+
+static struct port * alloc_port(struct client *c, enum spa_direction direction)
+{
+ struct port *p;
+ struct object *o;
+ uint32_t i;
+
+ if (spa_list_is_empty(&c->free_ports)) {
+ p = calloc(OBJECT_CHUNK, sizeof(struct port));
+ if (p == NULL)
+ return NULL;
+ for (i = 0; i < OBJECT_CHUNK; i++)
+ spa_list_append(&c->free_ports, &p[i].link);
+ }
+ p = spa_list_first(&c->free_ports, struct port, link);
+ spa_list_remove(&p->link);
+
+ o = alloc_object(c, INTERFACE_Port);
+ o->id = SPA_ID_INVALID;
+ o->port.node_id = c->node_id;
+ o->port.port = p;
+ o->port.latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
+ o->port.latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT);
+
+ p->valid = true;
+ p->zeroed = false;
+ p->client = c;
+ p->object = o;
+ spa_list_init(&p->mix);
+ p->props = pw_properties_new(NULL, NULL);
+
+ p->direction = direction;
+ p->emptyptr = SPA_PTR_ALIGN(p->empty, MAX_ALIGN, float);
+ p->port_id = pw_map_insert_new(&c->ports[direction], p);
+
+ pthread_mutex_lock(&c->context.lock);
+ spa_list_append(&c->context.objects, &o->link);
+ pthread_mutex_unlock(&c->context.lock);
+
+ return p;
+}
+
+static void free_port(struct client *c, struct port *p)
+{
+ struct mix *m;
+
+ spa_list_consume(m, &p->mix, port_link)
+ free_mix(c, m);
+
+ pw_map_remove(&c->ports[p->direction], p->port_id);
+ free_object(c, p->object);
+ pw_properties_free(p->props);
+ spa_list_append(&c->free_ports, &p->link);
+}
+
+static struct object *find_node(struct client *c, const char *name)
+{
+ struct object *o;
+
+ spa_list_for_each(o, &c->context.objects, link) {
+ if (o->removing || o->removed || o->type != INTERFACE_Node)
+ continue;
+ if (spa_streq(o->node.name, name))
+ return o;
+ }
+ return NULL;
+}
+
+static bool is_port_default(struct client *c, struct object *o)
+{
+ struct object *ot;
+
+ if (c->metadata == NULL)
+ return false;
+
+ if ((ot = o->port.node) != NULL &&
+ (spa_streq(ot->node.node_name, c->metadata->default_audio_source) ||
+ spa_streq(ot->node.node_name, c->metadata->default_audio_sink)))
+ return true;
+
+ return false;
+}
+
+static struct object *find_port_by_name(struct client *c, const char *name)
+{
+ struct object *o;
+
+ spa_list_for_each(o, &c->context.objects, link) {
+ if (o->type != INTERFACE_Port || o->removed)
+ continue;
+ if (spa_streq(o->port.name, name) ||
+ spa_streq(o->port.alias1, name) ||
+ spa_streq(o->port.alias2, name))
+ return o;
+ if (is_port_default(c, o) && spa_streq(o->port.system, name))
+ return o;
+ }
+ return NULL;
+}
+
+static struct object *find_by_id(struct client *c, uint32_t id)
+{
+ struct object *o;
+ spa_list_for_each(o, &c->context.objects, link) {
+ if (o->id == id)
+ return o;
+ }
+ return NULL;
+}
+
+static struct object *find_by_serial(struct client *c, uint32_t serial)
+{
+ struct object *o;
+ spa_list_for_each(o, &c->context.objects, link) {
+ if (o->serial == serial)
+ return o;
+ }
+ return NULL;
+}
+
+static struct object *find_id(struct client *c, uint32_t id, bool valid)
+{
+ struct object *o = find_by_id(c, id);
+ if (o != NULL && (!valid || o->client == c))
+ return o;
+ return NULL;
+}
+
+static struct object *find_type(struct client *c, uint32_t id, uint32_t type, bool valid)
+{
+ struct object *o = find_id(c, id, valid);
+ if (o != NULL && o->type == type)
+ return o;
+ return NULL;
+}
+
+static struct object *find_link(struct client *c, uint32_t src, uint32_t dst)
+{
+ struct object *l;
+
+ spa_list_for_each(l, &c->context.objects, link) {
+ if (l->type != INTERFACE_Link || l->removed)
+ continue;
+ if (l->port_link.src == src &&
+ l->port_link.dst == dst) {
+ return l;
+ }
+ }
+ return NULL;
+}
+
+static struct buffer *dequeue_buffer(struct client *c, struct mix *mix)
+{
+ struct buffer *b;
+
+ if (SPA_UNLIKELY(spa_list_is_empty(&mix->queue)))
+ return NULL;
+
+ b = spa_list_first(&mix->queue, struct buffer, link);
+ spa_list_remove(&b->link);
+ SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
+ pw_log_trace_fp("%p: port %p: dequeue buffer %d", c, mix->port, b->id);
+
+ return b;
+}
+
+#if defined (__SSE__)
+#include <xmmintrin.h>
+static void mix_sse(float *dst, float *src[], uint32_t n_src, bool aligned, uint32_t n_samples)
+{
+ uint32_t i, n, unrolled;
+ __m128 in[1];
+
+ if (SPA_IS_ALIGNED(dst, 16) && aligned)
+ unrolled = n_samples & ~3;
+ else
+ unrolled = 0;
+
+ for (n = 0; n < unrolled; n += 4) {
+ in[0] = _mm_load_ps(&src[0][n]);
+ for (i = 1; i < n_src; i++)
+ in[0] = _mm_add_ps(in[0], _mm_load_ps(&src[i][n]));
+ _mm_store_ps(&dst[n], in[0]);
+ }
+ for (; n < n_samples; n++) {
+ in[0] = _mm_load_ss(&src[0][n]);
+ for (i = 1; i < n_src; i++)
+ in[0] = _mm_add_ss(in[0], _mm_load_ss(&src[i][n]));
+ _mm_store_ss(&dst[n], in[0]);
+ }
+}
+#endif
+
+static void mix_c(float *dst, float *src[], uint32_t n_src, bool aligned, uint32_t n_samples)
+{
+ uint32_t n, i;
+ for (n = 0; n < n_samples; n++) {
+ float t = src[0][n];
+ for (i = 1; i < n_src; i++)
+ t += src[i][n];
+ dst[n] = t;
+ }
+}
+
+SPA_EXPORT
+void jack_get_version(int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto_ptr)
+{
+ if (major_ptr)
+ *major_ptr = 3;
+ if (minor_ptr)
+ *minor_ptr = 0;
+ if (micro_ptr)
+ *micro_ptr = 0;
+ if (proto_ptr)
+ *proto_ptr = 0;
+}
+
+#define do_callback_expr(c,expr,callback,...) \
+({ \
+ if (c->callback && c->active) { \
+ pw_thread_loop_unlock(c->context.loop); \
+ if (c->locked_process) \
+ pthread_mutex_lock(&c->rt_lock); \
+ (expr); \
+ pw_log_debug("emit " #callback); \
+ c->callback(__VA_ARGS__); \
+ if (c->locked_process) \
+ pthread_mutex_unlock(&c->rt_lock); \
+ pw_thread_loop_lock(c->context.loop); \
+ } else { \
+ if (c->active) \
+ (expr); \
+ pw_log_debug("skip " #callback \
+ " cb:%p active:%d", c->callback, \
+ c->active); \
+ } \
+})
+
+#define do_callback(c,callback,...) do_callback_expr(c,(void)0,callback,__VA_ARGS__)
+
+#define do_rt_callback_res(c,callback,...) \
+({ \
+ int res = 0; \
+ if (c->callback) { \
+ if (pthread_mutex_trylock(&c->rt_lock) == 0) { \
+ c->rt_locked = true; \
+ res = c->callback(__VA_ARGS__); \
+ c->rt_locked = false; \
+ pthread_mutex_unlock(&c->rt_lock); \
+ } else { \
+ pw_log_debug("skip " #callback \
+ " cb:%p", c->callback); \
+ } \
+ } \
+ res; \
+})
+
+SPA_EXPORT
+const char *
+jack_get_version_string(void)
+{
+ static char name[1024];
+ snprintf(name, sizeof(name), "3.0.0.0 (using PipeWire %s)", pw_get_library_version());
+ return name;
+}
+
+static void on_sync_reply(void *data, uint32_t id, int seq)
+{
+ struct client *client = data;
+ if (id != PW_ID_CORE)
+ return;
+ client->last_sync = seq;
+ if (client->pending_sync == seq)
+ pw_thread_loop_signal(client->context.loop, false);
+}
+
+
+static void on_error(void *data, uint32_t id, int seq, int res, const char *message)
+{
+ struct client *client = data;
+
+ pw_log_warn("%p: error id:%u seq:%d res:%d (%s): %s", client,
+ id, seq, res, spa_strerror(res), message);
+
+ if (id == PW_ID_CORE) {
+ client->last_res = res;
+ if (!client->destroyed)
+ do_callback(client, shutdown_callback, client->shutdown_arg);
+ }
+ pw_thread_loop_signal(client->context.loop, false);
+}
+
+static const struct pw_core_events core_events = {
+ PW_VERSION_CORE_EVENTS,
+ .done = on_sync_reply,
+ .error = on_error,
+};
+
+static int do_sync(struct client *client)
+{
+ bool in_data_thread = pw_data_loop_in_thread(client->loop);
+
+ if (pw_thread_loop_in_thread(client->context.loop)) {
+ pw_log_warn("sync requested from callback");
+ return 0;
+ }
+ if (client->last_res == -EPIPE)
+ return -EPIPE;
+
+ client->last_res = 0;
+ client->pending_sync = pw_proxy_sync((struct pw_proxy*)client->core, client->pending_sync);
+
+ while (true) {
+ if (in_data_thread) {
+ if (client->rt_locked)
+ pthread_mutex_unlock(&client->rt_lock);
+ client->data_locked = true;
+ }
+ pw_thread_loop_wait(client->context.loop);
+
+ if (in_data_thread) {
+ client->data_locked = false;
+ if (client->rt_locked)
+ pthread_mutex_lock(&client->rt_lock);
+ }
+
+ if (client->last_res < 0)
+ return client->last_res;
+
+ if (client->pending_sync == client->last_sync)
+ break;
+ }
+ return 0;
+}
+
+static void on_node_removed(void *data)
+{
+ struct client *client = data;
+ pw_proxy_destroy((struct pw_proxy*)client->node);
+}
+
+static void on_node_destroy(void *data)
+{
+ struct client *client = data;
+ client->node = NULL;
+ spa_hook_remove(&client->proxy_listener);
+ spa_hook_remove(&client->node_listener);
+}
+
+static void on_node_bound(void *data, uint32_t global_id)
+{
+ struct client *client = data;
+ client->node_id = global_id;
+}
+
+static const struct pw_proxy_events node_proxy_events = {
+ PW_VERSION_PROXY_EVENTS,
+ .removed = on_node_removed,
+ .destroy = on_node_destroy,
+ .bound = on_node_bound,
+};
+
+static struct link *find_activation(struct spa_list *links, uint32_t node_id)
+{
+ struct link *l;
+
+ spa_list_for_each(l, links, link) {
+ if (l->node_id == node_id)
+ return l;
+ }
+ return NULL;
+}
+
+static void client_remove_source(struct client *c)
+{
+ if (c->socket_source) {
+ pw_loop_destroy_source(c->loop->loop, c->socket_source);
+ c->socket_source = NULL;
+ }
+}
+
+static int
+do_remove_sources(struct spa_loop *loop,
+ bool async, uint32_t seq, const void *data, size_t size, void *user_data)
+{
+ struct client *c = user_data;
+ client_remove_source(c);
+ return 0;
+}
+
+static inline void reuse_buffer(struct client *c, struct mix *mix, uint32_t id)
+{
+ struct buffer *b;
+
+ b = &mix->buffers[id];
+
+ if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
+ pw_log_trace_fp("%p: port %p: recycle buffer %d", c, mix->port, id);
+ spa_list_append(&mix->queue, &b->link);
+ SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
+ }
+}
+
+
+static size_t convert_from_midi(void *midi, void *buffer, size_t size)
+{
+ struct spa_pod_builder b = { 0, };
+ uint32_t i, count;
+ struct spa_pod_frame f;
+
+ count = jack_midi_get_event_count(midi);
+
+ spa_pod_builder_init(&b, buffer, size);
+ spa_pod_builder_push_sequence(&b, &f, 0);
+
+ for (i = 0; i < count; i++) {
+ jack_midi_event_t ev;
+ jack_midi_event_get(&ev, midi, i);
+ spa_pod_builder_control(&b, ev.time, SPA_CONTROL_Midi);
+ spa_pod_builder_bytes(&b, ev.buffer, ev.size);
+ }
+ spa_pod_builder_pop(&b, &f);
+ return b.state.offset;
+}
+
+static inline void fix_midi_event(uint8_t *data, size_t size)
+{
+ /* fixup NoteOn with vel 0 */
+ if (size > 2 && (data[0] & 0xF0) == 0x90 && data[2] == 0x00) {
+ data[0] = 0x80 + (data[0] & 0x0F);
+ data[2] = 0x40;
+ }
+}
+
+static inline int event_sort(struct spa_pod_control *a, struct spa_pod_control *b)
+{
+ if (a->offset < b->offset)
+ return -1;
+ if (a->offset > b->offset)
+ return 1;
+ if (a->type != b->type)
+ return 0;
+ switch(a->type) {
+ case SPA_CONTROL_Midi:
+ {
+ /* 11 (controller) > 12 (program change) >
+ * 8 (note off) > 9 (note on) > 10 (aftertouch) >
+ * 13 (channel pressure) > 14 (pitch bend) */
+ static int priotab[] = { 5,4,3,7,6,2,1,0 };
+ uint8_t *da, *db;
+
+ if (SPA_POD_BODY_SIZE(&a->value) < 1 ||
+ SPA_POD_BODY_SIZE(&b->value) < 1)
+ return 0;
+
+ da = SPA_POD_BODY(&a->value);
+ db = SPA_POD_BODY(&b->value);
+ if ((da[0] & 0xf) != (db[0] & 0xf))
+ return 0;
+ return priotab[(db[0]>>4) & 7] - priotab[(da[0]>>4) & 7];
+ }
+ default:
+ return 0;
+ }
+}
+
+static void convert_to_midi(struct spa_pod_sequence **seq, uint32_t n_seq, void *midi, bool fix)
+{
+ struct spa_pod_control *c[n_seq];
+ uint32_t i;
+ int res;
+
+ for (i = 0; i < n_seq; i++)
+ c[i] = spa_pod_control_first(&seq[i]->body);
+
+ while (true) {
+ struct spa_pod_control *next = NULL;
+ uint32_t next_index = 0;
+
+ for (i = 0; i < n_seq; i++) {
+ if (!spa_pod_control_is_inside(&seq[i]->body,
+ SPA_POD_BODY_SIZE(seq[i]), c[i]))
+ continue;
+
+ if (next == NULL || event_sort(c[i], next) <= 0) {
+ next = c[i];
+ next_index = i;
+ }
+ }
+ if (SPA_UNLIKELY(next == NULL))
+ break;
+
+ switch(next->type) {
+ case SPA_CONTROL_Midi:
+ {
+ uint8_t *data = SPA_POD_BODY(&next->value);
+ size_t size = SPA_POD_BODY_SIZE(&next->value);
+
+ if (fix)
+ fix_midi_event(data, size);
+
+ if ((res = jack_midi_event_write(midi, next->offset, data, size)) < 0)
+ pw_log_warn("midi %p: can't write event: %s", midi,
+ spa_strerror(res));
+ break;
+ }
+ }
+ c[next_index] = spa_pod_control_next(c[next_index]);
+ }
+}
+
+
+static inline void *get_buffer_output(struct port *p, uint32_t frames, uint32_t stride, struct buffer **buf)
+{
+ struct mix *mix;
+ struct client *c = p->client;
+ void *ptr = NULL;
+ struct buffer *b;
+ struct spa_data *d;
+
+ if (frames == 0 || !p->valid)
+ return NULL;
+
+ if (SPA_UNLIKELY((mix = p->global_mix) == NULL))
+ return NULL;
+
+ pw_log_trace_fp("%p: port %s %d get buffer %d n_buffers:%d",
+ c, p->object->port.name, p->port_id, frames, mix->n_buffers);
+
+ if (SPA_UNLIKELY(mix->n_buffers == 0))
+ return NULL;
+
+ if (p->io.status == SPA_STATUS_HAVE_DATA &&
+ p->io.buffer_id < mix->n_buffers) {
+ b = &mix->buffers[p->io.buffer_id];
+ d = &b->datas[0];
+ } else {
+ if (p->io.buffer_id < mix->n_buffers) {
+ reuse_buffer(c, mix, p->io.buffer_id);
+ p->io.buffer_id = SPA_ID_INVALID;
+ }
+ if (SPA_UNLIKELY((b = dequeue_buffer(c, mix)) == NULL)) {
+ pw_log_warn("port %p: out of buffers", p);
+ return NULL;
+ }
+ d = &b->datas[0];
+ d->chunk->offset = 0;
+ d->chunk->size = frames * sizeof(float);
+ d->chunk->stride = stride;
+
+ p->io.status = SPA_STATUS_HAVE_DATA;
+ p->io.buffer_id = b->id;
+ }
+ ptr = d->data;
+ if (buf)
+ *buf = b;
+ return ptr;
+}
+
+static inline void process_empty(struct port *p, uint32_t frames)
+{
+ void *ptr;
+
+ switch (p->object->port.type_id) {
+ case TYPE_ID_AUDIO:
+ ptr = get_buffer_output(p, frames, sizeof(float), NULL);
+ if (SPA_LIKELY(ptr != NULL))
+ memcpy(ptr, p->emptyptr, frames * sizeof(float));
+ break;
+ case TYPE_ID_MIDI:
+ {
+ struct buffer *b;
+ ptr = get_buffer_output(p, MAX_BUFFER_FRAMES, 1, &b);
+ if (SPA_LIKELY(ptr != NULL)) {
+ b->datas[0].chunk->size = convert_from_midi(p->emptyptr,
+ ptr, MAX_BUFFER_FRAMES * sizeof(float));
+ }
+ break;
+ }
+ default:
+ pw_log_warn("port %p: unhandled format %d", p, p->object->port.type_id);
+ break;
+ }
+}
+
+static void prepare_output(struct port *p, uint32_t frames)
+{
+ struct mix *mix;
+
+ if (SPA_UNLIKELY(p->empty_out))
+ process_empty(p, frames);
+
+ spa_list_for_each(mix, &p->mix, port_link) {
+ if (SPA_LIKELY(mix->io != NULL))
+ *mix->io = p->io;
+ }
+}
+
+static void complete_process(struct client *c, uint32_t frames)
+{
+ struct port *p;
+ struct mix *mix;
+ union pw_map_item *item;
+
+ pw_array_for_each(item, &c->ports[SPA_DIRECTION_INPUT].items) {
+ if (pw_map_item_is_free(item))
+ continue;
+ p = item->data;
+ if (!p->valid)
+ continue;
+ spa_list_for_each(mix, &p->mix, port_link) {
+ if (SPA_LIKELY(mix->io != NULL))
+ mix->io->status = SPA_STATUS_NEED_DATA;
+ }
+ }
+ pw_array_for_each(item, &c->ports[SPA_DIRECTION_OUTPUT].items) {
+ if (pw_map_item_is_free(item))
+ continue;
+ p = item->data;
+ if (!p->valid)
+ continue;
+ prepare_output(p, frames);
+ p->io.status = SPA_STATUS_NEED_DATA;
+ }
+}
+
+static inline void debug_position(struct client *c, jack_position_t *p)
+{
+ pw_log_trace("usecs: %"PRIu64, p->usecs);
+ pw_log_trace("frame_rate: %u", p->frame_rate);
+ pw_log_trace("frame: %u", p->frame);
+ pw_log_trace("valid: %08x", p->valid);
+
+ if (p->valid & JackPositionBBT) {
+ pw_log_trace("BBT");
+ pw_log_trace(" bar: %u", p->bar);
+ pw_log_trace(" beat: %u", p->beat);
+ pw_log_trace(" tick: %u", p->tick);
+ pw_log_trace(" bar_start_tick: %f", p->bar_start_tick);
+ pw_log_trace(" beats_per_bar: %f", p->beats_per_bar);
+ pw_log_trace(" beat_type: %f", p->beat_type);
+ pw_log_trace(" ticks_per_beat: %f", p->ticks_per_beat);
+ pw_log_trace(" beats_per_minute: %f", p->beats_per_minute);
+ }
+ if (p->valid & JackPositionTimecode) {
+ pw_log_trace("Timecode:");
+ pw_log_trace(" frame_time: %f", p->frame_time);
+ pw_log_trace(" next_time: %f", p->next_time);
+ }
+ if (p->valid & JackBBTFrameOffset) {
+ pw_log_trace("BBTFrameOffset:");
+ pw_log_trace(" bbt_offset: %u", p->bbt_offset);
+ }
+ if (p->valid & JackAudioVideoRatio) {
+ pw_log_trace("AudioVideoRatio:");
+ pw_log_trace(" audio_frames_per_video_frame: %f", p->audio_frames_per_video_frame);
+ }
+ if (p->valid & JackVideoFrameOffset) {
+ pw_log_trace("JackVideoFrameOffset:");
+ pw_log_trace(" video_offset: %u", p->video_offset);
+ }
+}
+
+static inline void jack_to_position(jack_position_t *s, struct pw_node_activation *a)
+{
+ struct spa_io_segment *d = &a->segment;
+
+ if (s->valid & JackPositionBBT) {
+ d->bar.flags = SPA_IO_SEGMENT_BAR_FLAG_VALID;
+ if (s->valid & JackBBTFrameOffset)
+ d->bar.offset = s->bbt_offset;
+ else
+ d->bar.offset = 0;
+ d->bar.signature_num = s->beats_per_bar;
+ d->bar.signature_denom = s->beat_type;
+ d->bar.bpm = s->beats_per_minute;
+ d->bar.beat = (s->bar - 1) * s->beats_per_bar + (s->beat - 1) +
+ (s->tick / s->ticks_per_beat);
+ }
+}
+
+static inline jack_transport_state_t position_to_jack(struct pw_node_activation *a, jack_position_t *d)
+{
+ struct spa_io_position *s = &a->position;
+ jack_transport_state_t state;
+ struct spa_io_segment *seg = &s->segments[0];
+ uint64_t running;
+
+ switch (s->state) {
+ default:
+ case SPA_IO_POSITION_STATE_STOPPED:
+ state = JackTransportStopped;
+ break;
+ case SPA_IO_POSITION_STATE_STARTING:
+ state = JackTransportStarting;
+ break;
+ case SPA_IO_POSITION_STATE_RUNNING:
+ if (seg->flags & SPA_IO_SEGMENT_FLAG_LOOPING)
+ state = JackTransportLooping;
+ else
+ state = JackTransportRolling;
+ break;
+ }
+ if (SPA_UNLIKELY(d == NULL))
+ return state;
+
+ d->unique_1++;
+ d->usecs = s->clock.nsec / SPA_NSEC_PER_USEC;
+ d->frame_rate = s->clock.rate.denom;
+
+ if ((int64_t)s->clock.position < s->offset) {
+ d->frame = seg->position;
+ } else {
+ running = s->clock.position - s->offset;
+ if (running >= seg->start &&
+ (seg->duration == 0 || running < seg->start + seg->duration))
+ d->frame = (running - seg->start) * seg->rate + seg->position;
+ else
+ d->frame = seg->position;
+ }
+ d->valid = 0;
+ if (a->segment_owner[0] && SPA_FLAG_IS_SET(seg->bar.flags, SPA_IO_SEGMENT_BAR_FLAG_VALID)) {
+ double abs_beat;
+ long beats;
+
+ d->valid |= JackPositionBBT;
+
+ d->bbt_offset = seg->bar.offset;
+ if (seg->bar.offset)
+ d->valid |= JackBBTFrameOffset;
+
+ d->beats_per_bar = seg->bar.signature_num;
+ d->beat_type = seg->bar.signature_denom;
+ d->ticks_per_beat = 1920.0f;
+ d->beats_per_minute = seg->bar.bpm;
+
+ abs_beat = seg->bar.beat;
+
+ d->bar = abs_beat / d->beats_per_bar;
+ beats = d->bar * d->beats_per_bar;
+ d->bar_start_tick = beats * d->ticks_per_beat;
+ d->beat = abs_beat - beats;
+ beats += d->beat;
+ d->tick = (abs_beat - beats) * d->ticks_per_beat;
+ d->bar++;
+ d->beat++;
+ }
+ d->unique_2 = d->unique_1;
+ return state;
+}
+
+static void recompute_latencies(struct client *c)
+{
+ do_callback(c, latency_callback, JackCaptureLatency, c->latency_arg);
+ do_callback(c, latency_callback, JackPlaybackLatency, c->latency_arg);
+}
+
+static int
+do_buffer_frames(struct spa_loop *loop,
+ bool async, uint32_t seq, const void *data, size_t size, void *user_data)
+{
+ uint32_t buffer_frames = *((uint32_t*)data);
+ struct client *c = user_data;
+ if (c->buffer_frames != buffer_frames)
+ do_callback_expr(c, c->buffer_frames = buffer_frames, bufsize_callback, buffer_frames, c->bufsize_arg);
+ recompute_latencies(c);
+ return 0;
+}
+
+static inline int check_buffer_frames(struct client *c, struct spa_io_position *pos)
+{
+ uint32_t buffer_frames = pos->clock.duration;
+ if (SPA_UNLIKELY(buffer_frames != c->buffer_frames)) {
+ pw_log_info("%p: bufferframes old:%d new:%d cb:%p", c,
+ c->buffer_frames, buffer_frames, c->bufsize_callback);
+ if (c->buffer_frames != (uint32_t)-1)
+ pw_loop_invoke(c->context.l, do_buffer_frames, 0,
+ &buffer_frames, sizeof(buffer_frames), false, c);
+ else
+ c->buffer_frames = buffer_frames;
+ }
+ return c->buffer_frames == buffer_frames;
+}
+
+static int
+do_sample_rate(struct spa_loop *loop,
+ bool async, uint32_t seq, const void *data, size_t size, void *user_data)
+{
+ struct client *c = user_data;
+ uint32_t sample_rate = *((uint32_t*)data);
+ do_callback_expr(c, c->sample_rate = sample_rate, srate_callback, sample_rate, c->srate_arg);
+ return 0;
+}
+
+static inline int check_sample_rate(struct client *c, struct spa_io_position *pos)
+{
+ uint32_t sample_rate = pos->clock.rate.denom;
+ if (SPA_UNLIKELY(sample_rate != c->sample_rate)) {
+ pw_log_info("%p: sample_rate old:%d new:%d cb:%p", c,
+ c->sample_rate, sample_rate, c->srate_callback);
+ if (c->srate_callback != NULL) {
+ pw_loop_invoke(c->context.l, do_sample_rate, 0,
+ &sample_rate, sizeof(sample_rate), false, c);
+ } else {
+ c->sample_rate = sample_rate;
+ }
+ }
+ return c->sample_rate == sample_rate;
+}
+
+static inline uint32_t cycle_run(struct client *c)
+{
+ uint64_t cmd;
+ struct timespec ts;
+ int fd = c->socket_source->fd;
+ struct spa_io_position *pos = c->rt.position;
+ struct pw_node_activation *activation = c->activation;
+ struct pw_node_activation *driver = c->rt.driver_activation;
+
+ while (true) {
+ if (SPA_UNLIKELY(read(fd, &cmd, sizeof(cmd)) != sizeof(cmd))) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return 0;
+ pw_log_warn("%p: read failed %m", c);
+ }
+ break;
+ }
+ if (SPA_UNLIKELY(cmd > 1))
+ pw_log_info("%p: missed %"PRIu64" wakeups", c, cmd - 1);
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ activation->status = PW_NODE_ACTIVATION_AWAKE;
+ activation->awake_time = SPA_TIMESPEC_TO_NSEC(&ts);
+
+ if (SPA_UNLIKELY(c->first)) {
+ if (c->thread_init_callback)
+ c->thread_init_callback(c->thread_init_arg);
+ c->first = false;
+ }
+
+ if (SPA_UNLIKELY(pos == NULL)) {
+ pw_log_error("%p: missing position", c);
+ return 0;
+ }
+
+ if (check_buffer_frames(c, pos) == 0)
+ return 0;
+ if (check_sample_rate(c, pos) == 0)
+ return 0;
+
+ if (SPA_LIKELY(driver)) {
+ c->jack_state = position_to_jack(driver, &c->jack_position);
+
+ if (SPA_UNLIKELY(activation->pending_sync)) {
+ if (c->sync_callback == NULL ||
+ c->sync_callback(c->jack_state, &c->jack_position, c->sync_arg))
+ activation->pending_sync = false;
+ }
+ if (SPA_UNLIKELY(c->xrun_count != driver->xrun_count &&
+ c->xrun_count != 0 && c->xrun_callback))
+ c->xrun_callback(c->xrun_arg);
+ c->xrun_count = driver->xrun_count;
+ }
+ pw_log_trace_fp("%p: wait %"PRIu64" frames:%d rate:%d pos:%d delay:%"PRIi64" corr:%f", c,
+ activation->awake_time, c->buffer_frames, c->sample_rate,
+ c->jack_position.frame, pos->clock.delay, pos->clock.rate_diff);
+
+ return c->buffer_frames;
+}
+
+static inline uint32_t cycle_wait(struct client *c)
+{
+ int res;
+ uint32_t nframes;
+
+ do {
+ res = pw_data_loop_wait(c->loop, -1);
+ if (SPA_UNLIKELY(res <= 0)) {
+ pw_log_warn("%p: wait error %m", c);
+ return 0;
+ }
+ nframes = cycle_run(c);
+ } while (!nframes);
+
+ return nframes;
+}
+
+static inline void signal_sync(struct client *c)
+{
+ struct timespec ts;
+ uint64_t cmd, nsec;
+ struct link *l;
+ struct pw_node_activation *activation = c->activation;
+
+ complete_process(c, c->buffer_frames);
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ nsec = SPA_TIMESPEC_TO_NSEC(&ts);
+ activation->status = PW_NODE_ACTIVATION_FINISHED;
+ activation->finish_time = nsec;
+
+ cmd = 1;
+ spa_list_for_each(l, &c->rt.target_links, target_link) {
+ struct pw_node_activation_state *state;
+
+ if (SPA_UNLIKELY(l->activation == NULL))
+ continue;
+
+ state = &l->activation->state[0];
+
+ pw_log_trace_fp("%p: link %p %p %d/%d", c, l, state,
+ state->pending, state->required);
+
+ if (pw_node_activation_state_dec(state, 1)) {
+ l->activation->status = PW_NODE_ACTIVATION_TRIGGERED;
+ l->activation->signal_time = nsec;
+
+ pw_log_trace_fp("%p: signal %p %p", c, l, state);
+
+ if (SPA_UNLIKELY(write(l->signalfd, &cmd, sizeof(cmd)) != sizeof(cmd)))
+ pw_log_warn("%p: write failed %m", c);
+ }
+ }
+}
+
+static inline void cycle_signal(struct client *c, int status)
+{
+ struct pw_node_activation *driver = c->rt.driver_activation;
+ struct pw_node_activation *activation = c->activation;
+
+ if (SPA_LIKELY(status == 0)) {
+ if (c->timebase_callback && driver && driver->segment_owner[0] == c->node_id) {
+ if (activation->pending_new_pos ||
+ c->jack_state == JackTransportRolling ||
+ c->jack_state == JackTransportLooping) {
+ c->timebase_callback(c->jack_state,
+ c->buffer_frames,
+ &c->jack_position,
+ activation->pending_new_pos,
+ c->timebase_arg);
+
+ activation->pending_new_pos = false;
+
+ debug_position(c, &c->jack_position);
+ jack_to_position(&c->jack_position, activation);
+ }
+ }
+ }
+ signal_sync(c);
+}
+
+static void
+on_rtsocket_condition(void *data, int fd, uint32_t mask)
+{
+ struct client *c = data;
+
+ if (SPA_UNLIKELY(mask & (SPA_IO_ERR | SPA_IO_HUP))) {
+ pw_log_warn("%p: got error", c);
+ client_remove_source(c);
+ return;
+ }
+ if (SPA_UNLIKELY(c->thread_callback)) {
+ if (!c->thread_entered) {
+ c->thread_entered = true;
+ c->thread_callback(c->thread_arg);
+ }
+ } else if (SPA_LIKELY(mask & SPA_IO_IN)) {
+ uint32_t buffer_frames;
+ int status = 0;
+
+ buffer_frames = cycle_run(c);
+
+ if (buffer_frames > 0)
+ status = do_rt_callback_res(c, process_callback, buffer_frames, c->process_arg);
+
+ cycle_signal(c, status);
+ }
+}
+
+static int
+do_clear_link(struct spa_loop *loop,
+ bool async, uint32_t seq, const void *data, size_t size, void *user_data)
+{
+ struct link *link = user_data;
+ spa_list_remove(&link->target_link);
+ return 0;
+}
+
+static void clear_link(struct client *c, struct link *link)
+{
+ pw_data_loop_invoke(c->loop,
+ do_clear_link, 1, NULL, 0, !c->data_locked, link);
+ pw_memmap_free(link->mem);
+ close(link->signalfd);
+ spa_list_remove(&link->link);
+ free(link);
+}
+
+static void clean_transport(struct client *c)
+{
+ struct link *l;
+
+ if (!c->has_transport)
+ return;
+
+ pw_data_loop_invoke(c->loop,
+ do_remove_sources, 1, NULL, 0, !c->data_locked, c);
+
+ spa_list_consume(l, &c->links, link)
+ clear_link(c, l);
+
+ c->has_transport = false;
+}
+
+static int client_node_transport(void *data,
+ int readfd, int writefd,
+ uint32_t mem_id, uint32_t offset, uint32_t size)
+{
+ struct client *c = (struct client *) data;
+
+ clean_transport(c);
+
+ c->mem = pw_mempool_map_id(c->pool, mem_id,
+ PW_MEMMAP_FLAG_READWRITE, offset, size, NULL);
+ if (c->mem == NULL) {
+ pw_log_debug("%p: can't map activation: %m", c);
+ return -errno;
+ }
+ c->activation = c->mem->ptr;
+
+ pw_log_debug("%p: create client transport with fds %d %d for node %u",
+ c, readfd, writefd, c->node_id);
+
+ close(writefd);
+ c->socket_source = pw_loop_add_io(c->loop->loop,
+ readfd,
+ SPA_IO_ERR | SPA_IO_HUP,
+ true, on_rtsocket_condition, c);
+
+ c->has_transport = true;
+ c->position = &c->activation->position;
+ pw_thread_loop_signal(c->context.loop, false);
+
+ return 0;
+}
+
+static int client_node_set_param(void *data,
+ uint32_t id, uint32_t flags,
+ const struct spa_pod *param)
+{
+ struct client *c = (struct client *) data;
+ pw_proxy_error((struct pw_proxy*)c->node, -ENOTSUP, "not supported");
+ return -ENOTSUP;
+}
+
+static int install_timeowner(struct client *c)
+{
+ struct pw_node_activation *a;
+ uint32_t owner;
+
+ if (!c->timebase_callback)
+ return 0;
+
+ if ((a = c->driver_activation) == NULL)
+ return -EIO;
+
+ pw_log_debug("%p: activation %p", c, a);
+
+ /* was ok */
+ owner = ATOMIC_LOAD(a->segment_owner[0]);
+ if (owner == c->node_id)
+ return 0;
+
+ /* try to become owner */
+ if (c->timeowner_conditional) {
+ if (!ATOMIC_CAS(a->segment_owner[0], 0, c->node_id)) {
+ pw_log_debug("%p: owner:%u id:%u", c, owner, c->node_id);
+ return -EBUSY;
+ }
+ } else {
+ ATOMIC_STORE(a->segment_owner[0], c->node_id);
+ }
+
+ pw_log_debug("%p: timebase installed for id:%u", c, c->node_id);
+
+ return 0;
+}
+
+static int
+do_update_driver_activation(struct spa_loop *loop,
+ bool async, uint32_t seq, const void *data, size_t size, void *user_data)
+{
+ struct client *c = user_data;
+ c->rt.position = c->position;
+ c->rt.driver_activation = c->driver_activation;
+ if (c->position) {
+ pw_log_info("%p: driver:%d clock:%s", c,
+ c->driver_id, c->position->clock.name);
+ check_sample_rate(c, c->position);
+ check_buffer_frames(c, c->position);
+ }
+ return 0;
+}
+
+static int update_driver_activation(struct client *c)
+{
+ jack_client_t *client = (jack_client_t*)c;
+ struct link *link;
+ bool freewheeling;
+
+ pw_log_debug("%p: driver %d", c, c->driver_id);
+
+ freewheeling = SPA_FLAG_IS_SET(c->position->clock.flags, SPA_IO_CLOCK_FLAG_FREEWHEEL);
+ if (c->freewheeling != freewheeling) {
+ jack_native_thread_t thr = jack_client_thread_id(client);
+
+ c->freewheeling = freewheeling;
+ if (freewheeling && thr) {
+ jack_drop_real_time_scheduling(thr);
+ }
+
+ do_callback(c, freewheel_callback, freewheeling, c->freewheel_arg);
+
+ if (!freewheeling && thr) {
+ jack_acquire_real_time_scheduling(thr,
+ jack_client_real_time_priority(client));
+ }
+ }
+
+ link = find_activation(&c->links, c->driver_id);
+ c->driver_activation = link ? link->activation : NULL;
+ pw_data_loop_invoke(c->loop,
+ do_update_driver_activation, SPA_ID_INVALID, NULL, 0, false, c);
+ install_timeowner(c);
+
+ return 0;
+}
+
+static int client_node_set_io(void *data,
+ uint32_t id,
+ uint32_t mem_id,
+ uint32_t offset,
+ uint32_t size)
+{
+ struct client *c = (struct client *) data;
+ struct pw_memmap *old, *mm;
+ void *ptr;
+ uint32_t tag[5] = { c->node_id, id, };
+
+ old = pw_mempool_find_tag(c->pool, tag, sizeof(tag));
+
+ if (mem_id == SPA_ID_INVALID) {
+ mm = ptr = NULL;
+ } else {
+ mm = pw_mempool_map_id(c->pool, mem_id,
+ PW_MEMMAP_FLAG_READWRITE, offset, size, tag);
+ if (mm == NULL) {
+ pw_log_warn("%p: can't map memory id %u", c, mem_id);
+ return -errno;
+ }
+ ptr = mm->ptr;
+ }
+ pw_log_debug("%p: set io %s %p", c,
+ spa_debug_type_find_name(spa_type_io, id), ptr);
+
+ switch (id) {
+ case SPA_IO_Position:
+ c->position = ptr;
+ c->driver_id = ptr ? c->position->clock.id : SPA_ID_INVALID;
+ update_driver_activation(c);
+ break;
+ default:
+ break;
+ }
+ pw_memmap_free(old);
+
+ return 0;
+}
+
+static int client_node_event(void *data, const struct spa_event *event)
+{
+ return -ENOTSUP;
+}
+
+static int client_node_command(void *data, const struct spa_command *command)
+{
+ struct client *c = (struct client *) data;
+
+ pw_log_debug("%p: got command %d", c, SPA_COMMAND_TYPE(command));
+
+ switch (SPA_NODE_COMMAND_ID(command)) {
+ case SPA_NODE_COMMAND_Suspend:
+ case SPA_NODE_COMMAND_Pause:
+ if (c->started) {
+ pw_loop_update_io(c->loop->loop,
+ c->socket_source, SPA_IO_ERR | SPA_IO_HUP);
+
+ c->started = false;
+ }
+ break;
+
+ case SPA_NODE_COMMAND_Start:
+ if (!c->started) {
+ pw_loop_update_io(c->loop->loop,
+ c->socket_source,
+ SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP);
+ c->started = true;
+ c->first = true;
+ c->thread_entered = false;
+ }
+ break;
+ default:
+ pw_log_warn("%p: unhandled node command %d", c, SPA_COMMAND_TYPE(command));
+ pw_proxy_errorf((struct pw_proxy*)c->node, -ENOTSUP,
+ "unhandled command %d", SPA_COMMAND_TYPE(command));
+ }
+ return 0;
+}
+
+static int client_node_add_port(void *data,
+ enum spa_direction direction,
+ uint32_t port_id, const struct spa_dict *props)
+{
+ struct client *c = (struct client *) data;
+ pw_proxy_error((struct pw_proxy*)c->node, -ENOTSUP, "add port not supported");
+ return -ENOTSUP;
+}
+
+static int client_node_remove_port(void *data,
+ enum spa_direction direction,
+ uint32_t port_id)
+{
+ struct client *c = (struct client *) data;
+ pw_proxy_error((struct pw_proxy*)c->node, -ENOTSUP, "remove port not supported");
+ return -ENOTSUP;
+}
+
+static int param_enum_format(struct client *c, struct port *p,
+ struct spa_pod **param, struct spa_pod_builder *b)
+{
+ switch (p->object->port.type_id) {
+ case TYPE_ID_AUDIO:
+ *param = spa_pod_builder_add_object(b,
+ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp),
+ SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_DSP_F32));
+ break;
+ case TYPE_ID_MIDI:
+ *param = spa_pod_builder_add_object(b,
+ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
+ break;
+ case TYPE_ID_VIDEO:
+ *param = spa_pod_builder_add_object(b,
+ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp),
+ SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_DSP_F32));
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 1;
+}
+
+static int param_format(struct client *c, struct port *p,
+ struct spa_pod **param, struct spa_pod_builder *b)
+{
+ switch (p->object->port.type_id) {
+ case TYPE_ID_AUDIO:
+ *param = spa_pod_builder_add_object(b,
+ SPA_TYPE_OBJECT_Format, SPA_PARAM_Format,
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp),
+ SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_DSP_F32));
+ break;
+ case TYPE_ID_MIDI:
+ *param = spa_pod_builder_add_object(b,
+ SPA_TYPE_OBJECT_Format, SPA_PARAM_Format,
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
+ break;
+ case TYPE_ID_VIDEO:
+ *param = spa_pod_builder_add_object(b,
+ SPA_TYPE_OBJECT_Format, SPA_PARAM_Format,
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp),
+ SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_DSP_F32));
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 1;
+}
+
+static int param_buffers(struct client *c, struct port *p,
+ struct spa_pod **param, struct spa_pod_builder *b)
+{
+ switch (p->object->port.type_id) {
+ case TYPE_ID_AUDIO:
+ case TYPE_ID_MIDI:
+ *param = spa_pod_builder_add_object(b,
+ SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
+ SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
+ SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
+ SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_STEP_Int(
+ MAX_BUFFER_FRAMES * sizeof(float),
+ sizeof(float),
+ INT32_MAX,
+ sizeof(float)),
+ SPA_PARAM_BUFFERS_stride, SPA_POD_Int(p->object->port.type_id == TYPE_ID_AUDIO ?
+ sizeof(float) : 1));
+ break;
+ case TYPE_ID_VIDEO:
+ *param = spa_pod_builder_add_object(b,
+ SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
+ SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
+ SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
+ SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(
+ 320 * 240 * 4 * 4,
+ 0,
+ INT32_MAX),
+ SPA_PARAM_BUFFERS_stride, SPA_POD_CHOICE_RANGE_Int(4, 4, INT32_MAX));
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 1;
+}
+
+static int param_io(struct client *c, struct port *p,
+ struct spa_pod **param, struct spa_pod_builder *b)
+{
+ *param = spa_pod_builder_add_object(b,
+ SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO,
+ SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers),
+ SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers)));
+ return 1;
+}
+
+static int param_latency(struct client *c, struct port *p,
+ struct spa_pod **param, struct spa_pod_builder *b)
+{
+ *param = spa_latency_build(b, SPA_PARAM_Latency,
+ &p->object->port.latency[p->direction]);
+ return 1;
+}
+
+static int param_latency_other(struct client *c, struct port *p,
+ struct spa_pod **param, struct spa_pod_builder *b)
+{
+ *param = spa_latency_build(b, SPA_PARAM_Latency,
+ &p->object->port.latency[SPA_DIRECTION_REVERSE(p->direction)]);
+ return 1;
+}
+
+/* called from thread-loop */
+static int port_set_format(struct client *c, struct port *p,
+ uint32_t flags, const struct spa_pod *param)
+{
+ struct spa_pod *params[6];
+ uint8_t buffer[4096];
+ struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
+
+ if (param == NULL) {
+ struct mix *mix;
+
+ pw_log_debug("%p: port %p clear format", c, p);
+
+ spa_list_for_each(mix, &p->mix, port_link)
+ clear_buffers(c, mix);
+
+ p->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
+ }
+ else {
+ struct spa_audio_info info = { 0 };
+ if (spa_format_parse(param, &info.media_type, &info.media_subtype) < 0)
+ return -EINVAL;
+
+ switch (info.media_type) {
+ case SPA_MEDIA_TYPE_audio:
+ {
+ if (info.media_subtype != SPA_MEDIA_SUBTYPE_dsp)
+ return -EINVAL;
+
+ if (spa_format_audio_dsp_parse(param, &info.info.dsp) < 0)
+ return -EINVAL;
+ if (info.info.dsp.format != SPA_AUDIO_FORMAT_DSP_F32)
+ return -EINVAL;
+ break;
+ }
+ case SPA_MEDIA_TYPE_application:
+ if (info.media_subtype != SPA_MEDIA_SUBTYPE_control)
+ return -EINVAL;
+ break;
+ case SPA_MEDIA_TYPE_video:
+ {
+ struct spa_video_info vinfo = { 0 };
+
+ if (info.media_subtype != SPA_MEDIA_SUBTYPE_dsp)
+ return -EINVAL;
+ if (spa_format_video_dsp_parse(param, &vinfo.info.dsp) < 0)
+ return -EINVAL;
+ if (vinfo.info.dsp.format != SPA_VIDEO_FORMAT_DSP_F32)
+ return -EINVAL;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ p->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
+ }
+
+ pw_log_info("port %s: update", p->object->port.name);
+
+ p->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
+
+ param_enum_format(c, p, &params[0], &b);
+ param_format(c, p, &params[1], &b);
+ param_buffers(c, p, &params[2], &b);
+ param_io(c, p, &params[3], &b);
+ param_latency(c, p, &params[4], &b);
+ param_latency_other(c, p, &params[5], &b);
+
+ pw_client_node_port_update(c->node,
+ p->direction,
+ p->port_id,
+ PW_CLIENT_NODE_PORT_UPDATE_PARAMS |
+ PW_CLIENT_NODE_PORT_UPDATE_INFO,
+ SPA_N_ELEMENTS(params),
+ (const struct spa_pod **) params,
+ &p->info);
+ p->info.change_mask = 0;
+ return 0;
+}
+
+/* called from thread-loop */
+static void port_update_latency(struct port *p)
+{
+ struct client *c = p->client;
+ struct spa_pod *params[6];
+ uint8_t buffer[4096];
+ struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
+
+ param_enum_format(c, p, &params[0], &b);
+ param_format(c, p, &params[1], &b);
+ param_buffers(c, p, &params[2], &b);
+ param_io(c, p, &params[3], &b);
+ param_latency(c, p, &params[4], &b);
+ param_latency_other(c, p, &params[5], &b);
+
+ pw_log_info("port %s: update", p->object->port.name);
+
+ p->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
+ p->params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL;
+
+ pw_client_node_port_update(c->node,
+ p->direction,
+ p->port_id,
+ PW_CLIENT_NODE_PORT_UPDATE_PARAMS |
+ PW_CLIENT_NODE_PORT_UPDATE_INFO,
+ SPA_N_ELEMENTS(params),
+ (const struct spa_pod **) params,
+ &p->info);
+ p->info.change_mask = 0;
+}
+
+/* called from thread-loop */
+static void default_latency(struct client *c, enum spa_direction direction,
+ struct spa_latency_info *latency)
+{
+ enum spa_direction other;
+ union pw_map_item *item;
+ struct port *p;
+
+ other = SPA_DIRECTION_REVERSE(direction);
+
+ spa_latency_info_combine_start(latency, direction);
+
+ pw_array_for_each(item, &c->ports[other].items) {
+ if (pw_map_item_is_free(item))
+ continue;
+ p = item->data;
+ spa_latency_info_combine(latency, &p->object->port.latency[direction]);
+ }
+
+ spa_latency_info_combine_finish(latency);
+}
+
+/* called from thread-loop */
+static void default_latency_callback(jack_latency_callback_mode_t mode, struct client *c)
+{
+ struct spa_latency_info latency, *current;
+ enum spa_direction direction;
+ union pw_map_item *item;
+ struct port *p;
+
+ if (mode == JackPlaybackLatency)
+ direction = SPA_DIRECTION_INPUT;
+ else
+ direction = SPA_DIRECTION_OUTPUT;
+
+ default_latency(c, direction, &latency);
+
+ pw_log_info("client %p: update %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, c,
+ latency.direction == SPA_DIRECTION_INPUT ? "playback" : "capture",
+ latency.min_quantum, latency.max_quantum,
+ latency.min_rate, latency.max_rate,
+ latency.min_ns, latency.max_ns);
+
+ pw_array_for_each(item, &c->ports[direction].items) {
+ if (pw_map_item_is_free(item))
+ continue;
+ p = item->data;
+ current = &p->object->port.latency[direction];
+ if (spa_latency_info_compare(current, &latency) == 0)
+ continue;
+ *current = latency;
+ port_update_latency(p);
+ }
+}
+
+/* called from thread-loop */
+static int port_set_latency(struct client *c, struct port *p,
+ uint32_t flags, const struct spa_pod *param)
+{
+ struct spa_latency_info info;
+ jack_latency_callback_mode_t mode;
+ struct spa_latency_info *current;
+ int res;
+
+ if (param == NULL)
+ return 0;
+
+ if ((res = spa_latency_parse(param, &info)) < 0)
+ return res;
+
+ current = &p->object->port.latency[info.direction];
+ if (spa_latency_info_compare(current, &info) == 0)
+ return 0;
+
+ *current = info;
+
+ pw_log_info("port %s: set %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, p->object->port.name,
+ info.direction == SPA_DIRECTION_INPUT ? "playback" : "capture",
+ info.min_quantum, info.max_quantum,
+ info.min_rate, info.max_rate,
+ info.min_ns, info.max_ns);
+
+ if (info.direction == p->direction)
+ return 0;
+
+ if (info.direction == SPA_DIRECTION_INPUT)
+ mode = JackPlaybackLatency;
+ else
+ mode = JackCaptureLatency;
+
+ if (c->latency_callback)
+ do_callback(c, latency_callback, mode, c->latency_arg);
+ else
+ default_latency_callback(mode, c);
+
+ port_update_latency(p);
+
+ return 0;
+}
+
+/* called from thread-loop */
+static int client_node_port_set_param(void *data,
+ enum spa_direction direction,
+ uint32_t port_id,
+ uint32_t id, uint32_t flags,
+ const struct spa_pod *param)
+{
+ struct client *c = (struct client *) data;
+ struct port *p = GET_PORT(c, direction, port_id);
+
+ if (p == NULL || !p->valid)
+ return -EINVAL;
+
+ pw_log_info("client %p: port %s %d.%d id:%d (%s) %p", c, p->object->port.name,
+ direction, port_id, id,
+ spa_debug_type_find_name(spa_type_param, id), param);
+
+ switch (id) {
+ case SPA_PARAM_Format:
+ return port_set_format(c, p, flags, param);
+ break;
+ case SPA_PARAM_Latency:
+ return port_set_latency(c, p, flags, param);
+ default:
+ break;
+ }
+ return 0;
+}
+
+static inline void *init_buffer(struct port *p)
+{
+ void *data = p->emptyptr;
+ if (p->zeroed)
+ return data;
+
+ if (p->object->port.type_id == TYPE_ID_MIDI) {
+ struct midi_buffer *mb = data;
+ mb->magic = MIDI_BUFFER_MAGIC;
+ mb->buffer_size = MAX_BUFFER_FRAMES * sizeof(float);
+ mb->nframes = MAX_BUFFER_FRAMES;
+ mb->write_pos = 0;
+ mb->event_count = 0;
+ mb->lost_events = 0;
+ pw_log_debug("port %p: init midi buffer size:%d", p, mb->buffer_size);
+ } else
+ memset(data, 0, MAX_BUFFER_FRAMES * sizeof(float));
+
+ p->zeroed = true;
+ return data;
+}
+
+static int client_node_port_use_buffers(void *data,
+ enum spa_direction direction,
+ uint32_t port_id,
+ uint32_t mix_id,
+ uint32_t flags,
+ uint32_t n_buffers,
+ struct pw_client_node_buffer *buffers)
+{
+ struct client *c = (struct client *) data;
+ struct port *p = GET_PORT(c, direction, port_id);
+ struct buffer *b;
+ uint32_t i, j, fl;
+ int res;
+ struct mix *mix;
+
+ if (p == NULL || !p->valid) {
+ res = -EINVAL;
+ goto done;
+ }
+ if ((mix = ensure_mix(c, p, mix_id)) == NULL) {
+ res = -ENOMEM;
+ goto done;
+ }
+
+ pw_log_debug("%p: port %p %d %d.%d use_buffers %d", c, p, direction,
+ port_id, mix_id, n_buffers);
+
+ if (n_buffers > MAX_BUFFERS) {
+ pw_log_error("%p: too many buffers %u > %u", c, n_buffers, MAX_BUFFERS);
+ return -ENOSPC;
+ }
+
+ if (p->object->port.type_id == TYPE_ID_VIDEO && direction == SPA_DIRECTION_INPUT) {
+ fl = PW_MEMMAP_FLAG_READ;
+ } else {
+ /* some apps write to the input buffer so we want everything readwrite */
+ fl = PW_MEMMAP_FLAG_READWRITE;
+ }
+
+ /* clear previous buffers */
+ clear_buffers(c, mix);
+
+ for (i = 0; i < n_buffers; i++) {
+ off_t offset;
+ struct spa_buffer *buf;
+ struct pw_memmap *mm;
+
+ mm = pw_mempool_map_id(c->pool, buffers[i].mem_id,
+ fl, buffers[i].offset, buffers[i].size, NULL);
+ if (mm == NULL) {
+ pw_log_warn("%p: can't map memory id %u: %m", c, buffers[i].mem_id);
+ continue;
+ }
+
+ buf = buffers[i].buffer;
+
+ b = &mix->buffers[i];
+ b->id = i;
+ b->flags = 0;
+ b->n_mem = 0;
+ b->mem[b->n_mem++] = mm;
+
+ pw_log_debug("%p: add buffer id:%u offset:%u size:%u map:%p ptr:%p",
+ c, buffers[i].mem_id, buffers[i].offset,
+ buffers[i].size, mm, mm->ptr);
+
+ offset = 0;
+ for (j = 0; j < buf->n_metas; j++) {
+ struct spa_meta *m = &buf->metas[j];
+ offset += SPA_ROUND_UP_N(m->size, 8);
+ }
+
+ b->n_datas = SPA_MIN(buf->n_datas, MAX_BUFFER_DATAS);
+
+ for (j = 0; j < b->n_datas; j++) {
+ struct spa_data *d = &b->datas[j];
+
+ memcpy(d, &buf->datas[j], sizeof(struct spa_data));
+ d->chunk =
+ SPA_PTROFF(mm->ptr, offset + sizeof(struct spa_chunk) * j,
+ struct spa_chunk);
+
+ if (d->type == SPA_DATA_MemId) {
+ uint32_t mem_id = SPA_PTR_TO_UINT32(d->data);
+ struct pw_memblock *bm;
+ struct pw_memmap *bmm;
+
+ bm = pw_mempool_find_id(c->pool, mem_id);
+ if (bm == NULL) {
+ pw_log_error("%p: unknown buffer mem %u", c, mem_id);
+ res = -ENODEV;
+ goto done;
+
+ }
+
+ d->fd = bm->fd;
+ d->type = bm->type;
+ d->data = NULL;
+
+ bmm = pw_memblock_map(bm, fl, d->mapoffset, d->maxsize, NULL);
+ if (bmm == NULL) {
+ res = -errno;
+ pw_log_error("%p: failed to map buffer mem %m", c);
+ d->data = NULL;
+ goto done;
+ }
+ b->mem[b->n_mem++] = bmm;
+ d->data = bmm->ptr;
+
+ pw_log_debug("%p: data %d %u -> fd %d %d",
+ c, j, bm->id, bm->fd, d->maxsize);
+ } else if (d->type == SPA_DATA_MemPtr) {
+ int offs = SPA_PTR_TO_INT(d->data);
+ d->data = SPA_PTROFF(mm->ptr, offs, void);
+ d->fd = -1;
+ pw_log_debug("%p: data %d %u -> mem %p %d",
+ c, j, b->id, d->data, d->maxsize);
+ } else {
+ pw_log_warn("unknown buffer data type %d", d->type);
+ }
+ if (c->allow_mlock && mlock(d->data, d->maxsize) < 0) {
+ if (errno != ENOMEM || !mlock_warned) {
+ pw_log(c->warn_mlock ? SPA_LOG_LEVEL_WARN : SPA_LOG_LEVEL_DEBUG,
+ "%p: Failed to mlock memory %p %u: %s", c,
+ d->data, d->maxsize,
+ errno == ENOMEM ?
+ "This is not a problem but for best performance, "
+ "consider increasing RLIMIT_MEMLOCK" : strerror(errno));
+ mlock_warned |= errno == ENOMEM;
+ }
+ }
+ }
+ SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
+ if (direction == SPA_DIRECTION_OUTPUT)
+ reuse_buffer(c, mix, b->id);
+
+ }
+ pw_log_debug("%p: have %d buffers", c, n_buffers);
+ mix->n_buffers = n_buffers;
+ res = 0;
+
+ done:
+ if (res < 0)
+ pw_proxy_error((struct pw_proxy*)c->node, res, spa_strerror(res));
+ return res;
+}
+
+static int client_node_port_set_io(void *data,
+ enum spa_direction direction,
+ uint32_t port_id,
+ uint32_t mix_id,
+ uint32_t id,
+ uint32_t mem_id,
+ uint32_t offset,
+ uint32_t size)
+{
+ struct client *c = (struct client *) data;
+ struct port *p = GET_PORT(c, direction, port_id);
+ struct pw_memmap *mm, *old;
+ struct mix *mix;
+ uint32_t tag[5] = { c->node_id, direction, port_id, mix_id, id };
+ void *ptr;
+ int res = 0;
+
+ if (p == NULL || !p->valid) {
+ res = -EINVAL;
+ goto exit;
+ }
+
+ if ((mix = ensure_mix(c, p, mix_id)) == NULL) {
+ res = -ENOMEM;
+ goto exit;
+ }
+
+ old = pw_mempool_find_tag(c->pool, tag, sizeof(tag));
+
+ if (mem_id == SPA_ID_INVALID) {
+ mm = ptr = NULL;
+ }
+ else {
+ mm = pw_mempool_map_id(c->pool, mem_id,
+ PW_MEMMAP_FLAG_READWRITE, offset, size, tag);
+ if (mm == NULL) {
+ pw_log_warn("%p: can't map memory id %u", c, mem_id);
+ res = -EINVAL;
+ goto exit_free;
+ }
+ ptr = mm->ptr;
+ }
+
+ pw_log_debug("%p: port %p mix:%d set io:%s id:%u ptr:%p", c, p, mix_id,
+ spa_debug_type_find_name(spa_type_io, id), id, ptr);
+
+ switch (id) {
+ case SPA_IO_Buffers:
+ mix->io = ptr;
+ break;
+ default:
+ break;
+ }
+exit_free:
+ pw_memmap_free(old);
+exit:
+ if (res < 0)
+ pw_proxy_error((struct pw_proxy*)c->node, res, spa_strerror(res));
+ return res;
+}
+
+static int
+do_activate_link(struct spa_loop *loop,
+ bool async, uint32_t seq, const void *data, size_t size, void *user_data)
+{
+ struct link *link = user_data;
+ struct client *c = link->client;
+ pw_log_trace("link %p activate", link);
+ spa_list_append(&c->rt.target_links, &link->target_link);
+ return 0;
+}
+
+static int client_node_set_activation(void *data,
+ uint32_t node_id,
+ int signalfd,
+ uint32_t mem_id,
+ uint32_t offset,
+ uint32_t size)
+{
+ struct client *c = (struct client *) data;
+ struct pw_memmap *mm;
+ struct link *link;
+ void *ptr;
+ int res = 0;
+
+ if (c->node_id == node_id) {
+ pw_log_debug("%p: our activation %u: %u %u %u", c, node_id,
+ mem_id, offset, size);
+ close(signalfd);
+ return 0;
+ }
+
+ if (mem_id == SPA_ID_INVALID) {
+ mm = ptr = NULL;
+ size = 0;
+ }
+ else {
+ mm = pw_mempool_map_id(c->pool, mem_id,
+ PW_MEMMAP_FLAG_READWRITE, offset, size, NULL);
+ if (mm == NULL) {
+ pw_log_warn("%p: can't map memory id %u", c, mem_id);
+ res = -EINVAL;
+ goto exit;
+ }
+ ptr = mm->ptr;
+ }
+
+ pw_log_debug("%p: set activation %u: %u %u %u %p", c, node_id,
+ mem_id, offset, size, ptr);
+
+ if (ptr) {
+ link = calloc(1, sizeof(struct link));
+ if (link == NULL) {
+ res = -errno;
+ goto exit;
+ }
+ link->client = c;
+ link->node_id = node_id;
+ link->mem = mm;
+ link->activation = ptr;
+ link->signalfd = signalfd;
+ spa_list_append(&c->links, &link->link);
+
+ pw_data_loop_invoke(c->loop,
+ do_activate_link, SPA_ID_INVALID, NULL, 0, false, link);
+ }
+ else {
+ link = find_activation(&c->links, node_id);
+ if (link == NULL) {
+ res = -EINVAL;
+ goto exit;
+ }
+ clear_link(c, link);
+ }
+
+ if (c->driver_id == node_id)
+ update_driver_activation(c);
+
+ exit:
+ if (res < 0)
+ pw_proxy_error((struct pw_proxy*)c->node, res, spa_strerror(res));
+ return res;
+}
+
+static int client_node_port_set_mix_info(void *data,
+ enum spa_direction direction,
+ uint32_t port_id,
+ uint32_t mix_id,
+ uint32_t peer_id,
+ const struct spa_dict *props)
+{
+ struct client *c = (struct client *) data;
+ struct port *p = GET_PORT(c, direction, port_id);
+ struct mix *mix;
+ struct object *l;
+ uint32_t src, dst;
+ int res = 0;
+
+ if (p == NULL || !p->valid) {
+ res = -EINVAL;
+ goto exit;
+ }
+
+ if ((mix = ensure_mix(c, p, mix_id)) == NULL) {
+ res = -ENOMEM;
+ goto exit;
+ }
+ mix->peer_id = peer_id;
+
+ if (direction == SPA_DIRECTION_INPUT) {
+ src = peer_id;
+ dst = p->object->id;
+ } else {
+ src = p->object->id;
+ dst = peer_id;
+ }
+
+ if ((l = find_link(c, src, dst)) != NULL) {
+ if (direction == SPA_DIRECTION_INPUT)
+ mix->peer_port = l->port_link.our_output;
+ else
+ mix->peer_port = l->port_link.our_input;
+
+ pw_log_debug("peer port %p %p %p", mix->peer_port,
+ l->port_link.our_output, l->port_link.our_input);
+
+ if (!l->port_link.is_complete) {
+ l->port_link.is_complete = true;
+ pw_log_info("%p: our link %u/%u -> %u/%u completed", c,
+ l->port_link.src, l->port_link.src_serial,
+ l->port_link.dst, l->port_link.dst_serial);
+ do_callback(c, connect_callback,
+ l->port_link.src_serial, l->port_link.dst_serial, 1, c->connect_arg);
+ recompute_latencies(c);
+ do_callback(c, graph_callback, c->graph_arg);
+ }
+ }
+
+exit:
+ if (res < 0)
+ pw_proxy_error((struct pw_proxy*)c->node, res, spa_strerror(res));
+ return res;
+}
+
+static const struct pw_client_node_events client_node_events = {
+ PW_VERSION_CLIENT_NODE_EVENTS,
+ .transport = client_node_transport,
+ .set_param = client_node_set_param,
+ .set_io = client_node_set_io,
+ .event = client_node_event,
+ .command = client_node_command,
+ .add_port = client_node_add_port,
+ .remove_port = client_node_remove_port,
+ .port_set_param = client_node_port_set_param,
+ .port_use_buffers = client_node_port_use_buffers,
+ .port_set_io = client_node_port_set_io,
+ .set_activation = client_node_set_activation,
+ .port_set_mix_info = client_node_port_set_mix_info,
+};
+
+#define CHECK(expression,label) \
+do { \
+ if ((errno = expression) != 0) { \
+ res = -errno; \
+ pw_log_error(#expression ": %s", strerror(errno)); \
+ goto label; \
+ } \
+} while(false);
+
+static struct spa_thread *impl_create(void *object,
+ const struct spa_dict *props,
+ void *(*start)(void*), void *arg)
+{
+ struct client *c = (struct client *) object;
+ struct spa_thread *thr;
+ int res = 0;
+
+ pw_log_info("create thread");
+ if (globals.creator != NULL) {
+ pthread_t pt;
+ pthread_attr_t *attr = NULL, attributes;
+
+ attr = pw_thread_fill_attr(props, &attributes);
+
+ res = -globals.creator(&pt, attr, start, arg);
+ if (attr)
+ pthread_attr_destroy(attr);
+ if (res != 0)
+ goto error;
+ thr = (struct spa_thread*)pt;
+ } else {
+ thr = spa_thread_utils_create(c->context.old_thread_utils, props, start, arg);
+ }
+ return thr;
+error:
+ pw_log_warn("create RT thread failed: %s", strerror(res));
+ errno = -res;
+ return NULL;
+
+}
+
+static int impl_join(void *object,
+ struct spa_thread *thread, void **retval)
+{
+ struct client *c = (struct client *) object;
+ pw_log_info("join thread");
+ return spa_thread_utils_join(c->context.old_thread_utils, thread, retval);
+}
+
+static int impl_acquire_rt(void *object, struct spa_thread *thread, int priority)
+{
+ struct client *c = (struct client *) object;
+ return spa_thread_utils_acquire_rt(c->context.old_thread_utils, thread, priority);
+}
+
+static int impl_drop_rt(void *object, struct spa_thread *thread)
+{
+ struct client *c = (struct client *) object;
+ return spa_thread_utils_drop_rt(c->context.old_thread_utils, thread);
+}
+
+static struct spa_thread_utils_methods thread_utils_impl = {
+ SPA_VERSION_THREAD_UTILS_METHODS,
+ .create = impl_create,
+ .join = impl_join,
+ .acquire_rt = impl_acquire_rt,
+ .drop_rt = impl_drop_rt,
+};
+
+static jack_port_type_id_t string_to_type(const char *port_type)
+{
+ if (spa_streq(JACK_DEFAULT_AUDIO_TYPE, port_type))
+ return TYPE_ID_AUDIO;
+ else if (spa_streq(JACK_DEFAULT_MIDI_TYPE, port_type))
+ return TYPE_ID_MIDI;
+ else if (spa_streq(JACK_DEFAULT_VIDEO_TYPE, port_type))
+ return TYPE_ID_VIDEO;
+ else if (spa_streq("other", port_type))
+ return TYPE_ID_OTHER;
+ else
+ return SPA_ID_INVALID;
+}
+
+static const char* type_to_string(jack_port_type_id_t type_id)
+{
+ switch(type_id) {
+ case TYPE_ID_AUDIO:
+ return JACK_DEFAULT_AUDIO_TYPE;
+ case TYPE_ID_MIDI:
+ return JACK_DEFAULT_MIDI_TYPE;
+ case TYPE_ID_VIDEO:
+ return JACK_DEFAULT_VIDEO_TYPE;
+ case TYPE_ID_OTHER:
+ return "other";
+ default:
+ return NULL;
+ }
+}
+
+static jack_uuid_t client_make_uuid(uint32_t id, bool monitor)
+{
+ jack_uuid_t uuid = 0x2; /* JackUUIDClient */
+ uuid = (uuid << 32) | (id + 1);
+ if (monitor)
+ uuid |= (1 << 30);
+ pw_log_debug("uuid %d -> %"PRIu64, id, uuid);
+ return uuid;
+}
+
+static int json_object_find(const char *obj, const char *key, char *value, size_t len)
+{
+ struct spa_json it[2];
+ const char *v;
+ char k[128];
+
+ spa_json_init(&it[0], obj, strlen(obj));
+ if (spa_json_enter_object(&it[0], &it[1]) <= 0)
+ return -EINVAL;
+
+ while (spa_json_get_string(&it[1], k, sizeof(k)) > 0) {
+ if (spa_streq(k, key)) {
+ if (spa_json_get_string(&it[1], value, len) <= 0)
+ continue;
+ return 0;
+ } else {
+ if (spa_json_next(&it[1], &v) <= 0)
+ break;
+ }
+ }
+ return -ENOENT;
+}
+
+static int metadata_property(void *data, uint32_t id,
+ const char *key, const char *type, const char *value)
+{
+ struct client *c = (struct client *) data;
+ struct object *o;
+ jack_uuid_t uuid;
+
+ pw_log_debug("set id:%u key:'%s' value:'%s' type:'%s'", id, key, value, type);
+
+ if (id == PW_ID_CORE) {
+ if (key == NULL || spa_streq(key, "default.audio.sink")) {
+ if (value != NULL) {
+ if (json_object_find(value, "name",
+ c->metadata->default_audio_sink,
+ sizeof(c->metadata->default_audio_sink)) < 0)
+ value = NULL;
+ }
+ if (value == NULL)
+ c->metadata->default_audio_sink[0] = '\0';
+ }
+ if (key == NULL || spa_streq(key, "default.audio.source")) {
+ if (value != NULL) {
+ if (json_object_find(value, "name",
+ c->metadata->default_audio_source,
+ sizeof(c->metadata->default_audio_source)) < 0)
+ value = NULL;
+ }
+ if (value == NULL)
+ c->metadata->default_audio_source[0] = '\0';
+ }
+ } else {
+ if ((o = find_id(c, id, true)) == NULL)
+ return -EINVAL;
+
+ switch (o->type) {
+ case INTERFACE_Node:
+ uuid = client_make_uuid(o->serial, false);
+ break;
+ case INTERFACE_Port:
+ uuid = jack_port_uuid_generate(o->serial);
+ break;
+ default:
+ return -EINVAL;
+ }
+ update_property(c, uuid, key, type, value);
+ }
+
+ return 0;
+}
+
+static const struct pw_metadata_events metadata_events = {
+ PW_VERSION_METADATA_EVENTS,
+ .property = metadata_property
+};
+
+static void metadata_proxy_removed(void *data)
+{
+ struct client *c = data;
+ pw_proxy_destroy((struct pw_proxy*)c->metadata->proxy);
+}
+
+static void metadata_proxy_destroy(void *data)
+{
+ struct client *c = data;
+ spa_hook_remove(&c->metadata->proxy_listener);
+ spa_hook_remove(&c->metadata->listener);
+ c->metadata = NULL;
+}
+
+static const struct pw_proxy_events metadata_proxy_events = {
+ PW_VERSION_PROXY_EVENTS,
+ .removed = metadata_proxy_removed,
+ .destroy = metadata_proxy_destroy,
+};
+
+static void settings_proxy_removed(void *data)
+{
+ struct client *c = data;
+ pw_proxy_destroy((struct pw_proxy*)c->settings->proxy);
+}
+
+static void settings_proxy_destroy(void *data)
+{
+ struct client *c = data;
+ spa_hook_remove(&c->settings->proxy_listener);
+ c->settings = NULL;
+}
+
+static const struct pw_proxy_events settings_proxy_events = {
+ PW_VERSION_PROXY_EVENTS,
+ .removed = settings_proxy_removed,
+ .destroy = settings_proxy_destroy,
+};
+static void proxy_removed(void *data)
+{
+ struct object *o = data;
+ pw_proxy_destroy(o->proxy);
+}
+
+static void proxy_destroy(void *data)
+{
+ struct object *o = data;
+ spa_hook_remove(&o->proxy_listener);
+ spa_hook_remove(&o->object_listener);
+ o->proxy = NULL;
+}
+
+static const struct pw_proxy_events proxy_events = {
+ PW_VERSION_PROXY_EVENTS,
+ .removed = proxy_removed,
+ .destroy = proxy_destroy,
+};
+
+static void port_param(void *data, int seq,
+ uint32_t id, uint32_t index, uint32_t next,
+ const struct spa_pod *param)
+{
+ struct object *o = data;
+
+ switch (id) {
+ case SPA_PARAM_Latency:
+ {
+ struct spa_latency_info info;
+ if (spa_latency_parse(param, &info) < 0)
+ return;
+ o->port.latency[info.direction] = info;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static const struct pw_port_events port_events = {
+ PW_VERSION_PORT,
+ .param = port_param,
+};
+
+#define FILTER_NAME " ()[].:*$"
+#define FILTER_PORT " ()[].*$"
+
+static void filter_name(char *str, const char *filter, char filter_char)
+{
+ char *p;
+ for (p = str; *p; p++) {
+ if (strchr(filter, *p) != NULL)
+ *p = filter_char;
+ }
+}
+
+static void registry_event_global(void *data, uint32_t id,
+ uint32_t permissions, const char *type, uint32_t version,
+ const struct spa_dict *props)
+{
+ struct client *c = (struct client *) data;
+ struct object *o, *ot, *op;
+ const char *str;
+ bool is_first = false, graph_changed = false;
+ uint32_t serial;
+
+ if (props == NULL)
+ return;
+
+ str = spa_dict_lookup(props, PW_KEY_OBJECT_SERIAL);
+ if (!spa_atou32(str, &serial, 0))
+ serial = SPA_ID_INVALID;
+
+ pw_log_debug("new %s id:%u serial:%u", type, id, serial);
+
+ if (spa_streq(type, PW_TYPE_INTERFACE_Node)) {
+ const char *app, *node_name;
+ char tmp[JACK_CLIENT_NAME_SIZE+1];
+
+ o = alloc_object(c, INTERFACE_Node);
+
+ if ((str = spa_dict_lookup(props, PW_KEY_CLIENT_ID)) != NULL)
+ o->node.client_id = atoi(str);
+
+ node_name = spa_dict_lookup(props, PW_KEY_NODE_NAME);
+
+ if (id == c->node_id) {
+ pw_log_debug("%p: add our node %d", c, id);
+ if (node_name != NULL)
+ snprintf(c->name, sizeof(c->name), "%s", node_name);
+ c->serial = serial;
+ }
+ snprintf(o->node.node_name, sizeof(o->node.node_name),
+ "%s", node_name);
+
+ app = spa_dict_lookup(props, PW_KEY_APP_NAME);
+
+ if (c->short_name) {
+ str = spa_dict_lookup(props, PW_KEY_NODE_NICK);
+ if (str == NULL)
+ str = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION);
+ } else {
+ str = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION);
+ if (str == NULL)
+ str = spa_dict_lookup(props, PW_KEY_NODE_NICK);
+ }
+ if (str == NULL)
+ str = node_name;
+ if (str == NULL)
+ str = "node";
+
+ if (app && !spa_streq(app, str))
+ snprintf(tmp, sizeof(tmp), "%s/%s", app, str);
+ else
+ snprintf(tmp, sizeof(tmp), "%s", str);
+
+ if (c->filter_name)
+ filter_name(tmp, FILTER_NAME, c->filter_char);
+
+ ot = find_node(c, tmp);
+ if (ot != NULL && o->node.client_id != ot->node.client_id) {
+ snprintf(o->node.name, sizeof(o->node.name), "%.*s-%d",
+ (int)(sizeof(tmp)-11), tmp, id);
+ } else {
+ is_first = ot == NULL;
+ snprintf(o->node.name, sizeof(o->node.name), "%s", tmp);
+ }
+
+ if ((str = spa_dict_lookup(props, PW_KEY_PRIORITY_SESSION)) != NULL)
+ o->node.priority = pw_properties_parse_int(str);
+
+ pw_log_debug("%p: add node %d", c, id);
+
+ pthread_mutex_lock(&c->context.lock);
+ spa_list_append(&c->context.objects, &o->link);
+ pthread_mutex_unlock(&c->context.lock);
+ }
+ else if (spa_streq(type, PW_TYPE_INTERFACE_Port)) {
+ const struct spa_dict_item *item;
+ unsigned long flags = 0;
+ jack_port_type_id_t type_id;
+ uint32_t node_id;
+ bool is_monitor = false;
+ char tmp[REAL_JACK_PORT_NAME_SIZE+1];
+
+ if ((str = spa_dict_lookup(props, PW_KEY_FORMAT_DSP)) == NULL)
+ str = "other";
+ if ((type_id = string_to_type(str)) == SPA_ID_INVALID)
+ goto exit;
+
+ if ((str = spa_dict_lookup(props, PW_KEY_NODE_ID)) == NULL)
+ goto exit;
+
+ node_id = atoi(str);
+
+ if ((str = spa_dict_lookup(props, PW_KEY_PORT_EXTRA)) != NULL &&
+ spa_strstartswith(str, "jack:flags:"))
+ flags = atoi(str+11);
+
+ if ((str = spa_dict_lookup(props, PW_KEY_PORT_NAME)) == NULL)
+ goto exit;
+
+ spa_dict_for_each(item, props) {
+ if (spa_streq(item->key, PW_KEY_PORT_DIRECTION)) {
+ if (spa_streq(item->value, "in"))
+ flags |= JackPortIsInput;
+ else if (spa_streq(item->value, "out"))
+ flags |= JackPortIsOutput;
+ }
+ else if (spa_streq(item->key, PW_KEY_PORT_PHYSICAL)) {
+ if (pw_properties_parse_bool(item->value))
+ flags |= JackPortIsPhysical;
+ }
+ else if (spa_streq(item->key, PW_KEY_PORT_TERMINAL)) {
+ if (pw_properties_parse_bool(item->value))
+ flags |= JackPortIsTerminal;
+ }
+ else if (spa_streq(item->key, PW_KEY_PORT_CONTROL)) {
+ if (pw_properties_parse_bool(item->value))
+ type_id = TYPE_ID_MIDI;
+ }
+ else if (spa_streq(item->key, PW_KEY_PORT_MONITOR)) {
+ is_monitor = pw_properties_parse_bool(item->value);
+ }
+ }
+ if (is_monitor && !c->show_monitor)
+ goto exit;
+
+ o = NULL;
+ if (node_id == c->node_id) {
+ snprintf(tmp, sizeof(tmp), "%s:%s", c->name, str);
+ o = find_port_by_name(c, tmp);
+ if (o != NULL)
+ pw_log_info("%p: %s found our port %p", c, tmp, o);
+ }
+ if (o == NULL) {
+ if ((ot = find_type(c, node_id, INTERFACE_Node, true)) == NULL)
+ goto exit;
+
+ o = alloc_object(c, INTERFACE_Port);
+ if (o == NULL)
+ goto exit;
+
+ o->port.system_id = 0;
+ o->port.priority = ot->node.priority;
+ o->port.node = ot;
+ o->port.latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
+ o->port.latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT);
+
+ o->proxy = pw_registry_bind(c->registry,
+ id, type, PW_VERSION_PORT, 0);
+ if (o->proxy) {
+ uint32_t ids[1] = { SPA_PARAM_Latency };
+
+ pw_proxy_add_listener(o->proxy,
+ &o->proxy_listener, &proxy_events, o);
+ pw_proxy_add_object_listener(o->proxy,
+ &o->object_listener, &port_events, o);
+
+ pw_port_subscribe_params((struct pw_port*)o->proxy,
+ ids, 1);
+ }
+ pthread_mutex_lock(&c->context.lock);
+ spa_list_append(&c->context.objects, &o->link);
+ pthread_mutex_unlock(&c->context.lock);
+
+ if (is_monitor && !c->merge_monitor)
+ snprintf(tmp, sizeof(tmp), "%.*s%s:%s",
+ (int)(JACK_CLIENT_NAME_SIZE-(sizeof(MONITOR_EXT)-1)),
+ ot->node.name, MONITOR_EXT, str);
+ else
+ snprintf(tmp, sizeof(tmp), "%s:%s", ot->node.name, str);
+
+ if (c->filter_name)
+ filter_name(tmp, FILTER_PORT, c->filter_char);
+
+ op = find_port_by_name(c, tmp);
+ if (op != NULL)
+ snprintf(o->port.name, sizeof(o->port.name), "%.*s-%u",
+ (int)(sizeof(tmp)-11), tmp, serial);
+ else
+ snprintf(o->port.name, sizeof(o->port.name), "%s", tmp);
+ }
+
+ if ((str = spa_dict_lookup(props, PW_KEY_OBJECT_PATH)) != NULL)
+ snprintf(o->port.alias1, sizeof(o->port.alias1), "%s", str);
+
+ if ((str = spa_dict_lookup(props, PW_KEY_PORT_ALIAS)) != NULL)
+ snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", str);
+
+ if ((str = spa_dict_lookup(props, PW_KEY_PORT_ID)) != NULL) {
+ o->port.system_id = atoi(str);
+ snprintf(o->port.system, sizeof(o->port.system), "system:%s_%d",
+ flags & JackPortIsInput ? "playback" :
+ is_monitor ? "monitor" : "capture",
+ o->port.system_id+1);
+ }
+
+ o->port.flags = flags;
+ o->port.type_id = type_id;
+ o->port.node_id = node_id;
+ o->port.is_monitor = is_monitor;
+
+ pw_log_debug("%p: %p add port %d name:%s %d", c, o, id,
+ o->port.name, type_id);
+ }
+ else if (spa_streq(type, PW_TYPE_INTERFACE_Link)) {
+ struct object *p;
+
+ o = alloc_object(c, INTERFACE_Link);
+
+ pthread_mutex_lock(&c->context.lock);
+ spa_list_append(&c->context.objects, &o->link);
+ pthread_mutex_unlock(&c->context.lock);
+
+ if ((str = spa_dict_lookup(props, PW_KEY_LINK_OUTPUT_PORT)) == NULL)
+ goto exit_free;
+ o->port_link.src = pw_properties_parse_int(str);
+
+ if ((p = find_type(c, o->port_link.src, INTERFACE_Port, true)) == NULL)
+ goto exit_free;
+ o->port_link.src_serial = p->serial;
+
+ o->port_link.src_ours = p->port.port != NULL &&
+ p->port.port->client == c;
+ if (o->port_link.src_ours)
+ o->port_link.our_output = p->port.port;
+
+ if ((str = spa_dict_lookup(props, PW_KEY_LINK_INPUT_PORT)) == NULL)
+ goto exit_free;
+ o->port_link.dst = pw_properties_parse_int(str);
+
+ if ((p = find_type(c, o->port_link.dst, INTERFACE_Port, true)) == NULL)
+ goto exit_free;
+ o->port_link.dst_serial = p->serial;
+
+ o->port_link.dst_ours = p->port.port != NULL &&
+ p->port.port->client == c;
+ if (o->port_link.dst_ours)
+ o->port_link.our_input = p->port.port;
+
+ o->port_link.is_complete = !o->port_link.src_ours && !o->port_link.dst_ours;
+ pw_log_debug("%p: add link %d %u/%u->%u/%u", c, id,
+ o->port_link.src, o->port_link.src_serial,
+ o->port_link.dst, o->port_link.dst_serial);
+ }
+ else if (spa_streq(type, PW_TYPE_INTERFACE_Metadata)) {
+ struct pw_proxy *proxy;
+
+ if (c->metadata != NULL)
+ goto exit;
+ if ((str = spa_dict_lookup(props, PW_KEY_METADATA_NAME)) == NULL)
+ goto exit;
+
+ if (spa_streq(str, "default")) {
+ proxy = pw_registry_bind(c->registry,
+ id, type, PW_VERSION_METADATA, sizeof(struct metadata));
+
+ c->metadata = pw_proxy_get_user_data(proxy);
+ c->metadata->proxy = (struct pw_metadata*)proxy;
+ c->metadata->default_audio_sink[0] = '\0';
+ c->metadata->default_audio_source[0] = '\0';
+
+ pw_proxy_add_listener(proxy,
+ &c->metadata->proxy_listener,
+ &metadata_proxy_events, c);
+ pw_metadata_add_listener(proxy,
+ &c->metadata->listener,
+ &metadata_events, c);
+ } else if (spa_streq(str, "settings")) {
+ proxy = pw_registry_bind(c->registry,
+ id, type, PW_VERSION_METADATA, sizeof(struct metadata));
+
+ c->settings = pw_proxy_get_user_data(proxy);
+ c->settings->proxy = (struct pw_metadata*)proxy;
+ pw_proxy_add_listener(proxy,
+ &c->settings->proxy_listener,
+ &settings_proxy_events, c);
+ }
+ goto exit;
+ }
+ else {
+ goto exit;
+ }
+
+ o->id = id;
+ o->serial = serial;
+
+ switch (o->type) {
+ case INTERFACE_Node:
+ if (is_first) {
+ pw_log_info("%p: client added \"%s\"", c, o->node.name);
+ do_callback(c, registration_callback,
+ o->node.name, 1, c->registration_arg);
+ graph_changed = true;
+ }
+ break;
+
+ case INTERFACE_Port:
+ pw_log_info("%p: port added %u/%u \"%s\"", c, o->id, o->serial, o->port.name);
+ do_callback(c, portregistration_callback,
+ o->serial, 1, c->portregistration_arg);
+ graph_changed = true;
+ break;
+
+ case INTERFACE_Link:
+ pw_log_info("%p: link %u %u/%u -> %u/%u added complete:%d", c,
+ o->id, o->port_link.src, o->port_link.src_serial,
+ o->port_link.dst, o->port_link.dst_serial,
+ o->port_link.is_complete);
+ if (o->port_link.is_complete) {
+ do_callback(c, connect_callback,
+ o->port_link.src_serial,
+ o->port_link.dst_serial, 1, c->connect_arg);
+ graph_changed = true;
+ }
+ break;
+ }
+ if (graph_changed) {
+ recompute_latencies(c);
+ do_callback(c, graph_callback, c->graph_arg);
+ }
+
+ exit:
+ return;
+ exit_free:
+ free_object(c, o);
+ return;
+}
+
+static void registry_event_global_remove(void *data, uint32_t id)
+{
+ struct client *c = (struct client *) data;
+ struct object *o;
+ bool graph_changed = false;
+
+ pw_log_debug("%p: removed: %u", c, id);
+
+ if ((o = find_id(c, id, true)) == NULL)
+ return;
+
+ if (o->proxy) {
+ pw_proxy_destroy(o->proxy);
+ o->proxy = NULL;
+ }
+ o->removing = true;
+
+ switch (o->type) {
+ case INTERFACE_Node:
+ if (c->metadata) {
+ if (spa_streq(o->node.node_name, c->metadata->default_audio_sink))
+ c->metadata->default_audio_sink[0] = '\0';
+ if (spa_streq(o->node.node_name, c->metadata->default_audio_source))
+ c->metadata->default_audio_source[0] = '\0';
+ }
+ if (find_node(c, o->node.name) == NULL) {
+ pw_log_info("%p: client %u removed \"%s\"", c, o->id, o->node.name);
+ do_callback(c, registration_callback,
+ o->node.name, 0, c->registration_arg);
+ graph_changed = true;
+ }
+ break;
+ case INTERFACE_Port:
+ pw_log_info("%p: port %u/%u removed \"%s\"", c, o->id, o->serial, o->port.name);
+ do_callback(c, portregistration_callback,
+ o->serial, 0, c->portregistration_arg);
+ graph_changed = true;
+ break;
+ case INTERFACE_Link:
+ if (o->port_link.is_complete &&
+ find_type(c, o->port_link.src, INTERFACE_Port, true) != NULL &&
+ find_type(c, o->port_link.dst, INTERFACE_Port, true) != NULL) {
+ pw_log_info("%p: link %u %u/%u -> %u/%u removed", c, o->id,
+ o->port_link.src, o->port_link.src_serial,
+ o->port_link.dst, o->port_link.dst_serial);
+ o->port_link.is_complete = false;
+ do_callback(c, connect_callback,
+ o->port_link.src_serial, o->port_link.dst_serial, 0, c->connect_arg);
+ graph_changed = true;
+ } else
+ pw_log_warn("unlink between unknown ports %d and %d",
+ o->port_link.src, o->port_link.dst);
+ break;
+ }
+ if (graph_changed) {
+ recompute_latencies(c);
+ do_callback(c, graph_callback, c->graph_arg);
+ }
+
+ o->removing = false;
+ free_object(c, o);
+
+ return;
+}
+
+static const struct pw_registry_events registry_events = {
+ PW_VERSION_REGISTRY_EVENTS,
+ .global = registry_event_global,
+ .global_remove = registry_event_global_remove,
+};
+
+static void varargs_parse (struct client *c, jack_options_t options, va_list ap)
+{
+ if ((options & JackServerName))
+ c->server_name = va_arg(ap, char *);
+ if ((options & JackLoadName))
+ c->load_name = va_arg(ap, char *);
+ if ((options & JackLoadInit))
+ c->load_init = va_arg(ap, char *);
+ if ((options & JackSessionID)) {
+ char *sid = va_arg(ap, char *);
+ if (sid) {
+ const long long id = atoll(sid);
+ if (id > 0)
+ c->session_id = id;
+ }
+ }
+}
+
+
+static int execute_match(void *data, const char *location, const char *action,
+ const char *val, size_t len)
+{
+ struct client *client = data;
+ if (spa_streq(action, "update-props"))
+ pw_properties_update_string(client->props, val, len);
+ return 1;
+}
+
+SPA_EXPORT
+jack_client_t * jack_client_open (const char *client_name,
+ jack_options_t options,
+ jack_status_t *status, ...)
+{
+ struct client *client;
+ const struct spa_support *support;
+ uint32_t n_support;
+ const char *str;
+ struct spa_cpu *cpu_iface;
+ va_list ap;
+
+ if (getenv("PIPEWIRE_NOJACK") != NULL ||
+ getenv("PIPEWIRE_INTERNAL") != NULL ||
+ strstr(pw_get_library_version(), "0.2") != NULL)
+ goto disabled;
+
+ spa_return_val_if_fail(client_name != NULL, NULL);
+
+ client = calloc(1, sizeof(struct client));
+ if (client == NULL)
+ goto disabled;
+
+ pw_log_info("%p: open '%s' options:%d", client, client_name, options);
+
+ va_start(ap, status);
+ varargs_parse(client, options, ap);
+ va_end(ap);
+
+ snprintf(client->name, sizeof(client->name), "pw-%s", client_name);
+
+ pthread_mutex_init(&client->context.lock, NULL);
+ spa_list_init(&client->context.objects);
+
+ client->node_id = SPA_ID_INVALID;
+
+ client->buffer_frames = (uint32_t)-1;
+ client->sample_rate = (uint32_t)-1;
+ client->latency = SPA_FRACTION(-1, -1);
+
+ spa_list_init(&client->mix);
+ spa_list_init(&client->free_mix);
+
+ spa_list_init(&client->free_ports);
+ pw_map_init(&client->ports[SPA_DIRECTION_INPUT], 32, 32);
+ pw_map_init(&client->ports[SPA_DIRECTION_OUTPUT], 32, 32);
+
+ spa_list_init(&client->links);
+ client->driver_id = SPA_ID_INVALID;
+
+ spa_list_init(&client->rt.target_links);
+ pthread_mutex_init(&client->rt_lock, NULL);
+
+ if (client->server_name != NULL &&
+ spa_streq(client->server_name, "default"))
+ client->server_name = NULL;
+
+ client->props = pw_properties_new(
+ "loop.cancel", "true",
+ PW_KEY_REMOTE_NAME, client->server_name,
+ PW_KEY_CLIENT_NAME, client_name,
+ PW_KEY_CLIENT_API, "jack",
+ PW_KEY_CONFIG_NAME, "jack.conf",
+ NULL);
+ if (client->props == NULL)
+ goto no_props;
+
+ client->context.loop = pw_thread_loop_new(client->name, NULL);
+ client->context.l = pw_thread_loop_get_loop(client->context.loop);
+ client->context.context = pw_context_new(
+ client->context.l,
+ pw_properties_copy(client->props),
+ 0);
+ if (client->context.context == NULL)
+ goto no_props;
+
+ client->allow_mlock = client->context.context->settings.mem_allow_mlock;
+ client->warn_mlock = client->context.context->settings.mem_warn_mlock;
+
+ pw_context_conf_update_props(client->context.context,
+ "jack.properties", client->props);
+
+ pw_context_conf_section_match_rules(client->context.context, "jack.rules",
+ &client->context.context->properties->dict, execute_match, client);
+
+ support = pw_context_get_support(client->context.context, &n_support);
+
+ mix_function = mix_c;
+ cpu_iface = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
+ if (cpu_iface) {
+#if defined (__SSE__)
+ uint32_t flags = spa_cpu_get_flags(cpu_iface);
+ if (flags & SPA_CPU_FLAG_SSE)
+ mix_function = mix_sse;
+#endif
+ }
+ client->context.old_thread_utils =
+ pw_context_get_object(client->context.context,
+ SPA_TYPE_INTERFACE_ThreadUtils);
+ if (client->context.old_thread_utils == NULL)
+ client->context.old_thread_utils = pw_thread_utils_get();
+
+ globals.thread_utils = client->context.old_thread_utils;
+
+ client->context.thread_utils.iface = SPA_INTERFACE_INIT(
+ SPA_TYPE_INTERFACE_ThreadUtils,
+ SPA_VERSION_THREAD_UTILS,
+ &thread_utils_impl, client);
+
+ client->loop = client->context.context->data_loop_impl;
+ pw_data_loop_stop(client->loop);
+
+ pw_context_set_object(client->context.context,
+ SPA_TYPE_INTERFACE_ThreadUtils,
+ &client->context.thread_utils);
+
+ pw_thread_loop_start(client->context.loop);
+
+ pw_thread_loop_lock(client->context.loop);
+
+ client->core = pw_context_connect(client->context.context,
+ pw_properties_copy(client->props), 0);
+ if (client->core == NULL)
+ goto server_failed;
+
+ client->pool = pw_core_get_mempool(client->core);
+
+ pw_core_add_listener(client->core,
+ &client->core_listener,
+ &core_events, client);
+ client->registry = pw_core_get_registry(client->core,
+ PW_VERSION_REGISTRY, 0);
+ pw_registry_add_listener(client->registry,
+ &client->registry_listener,
+ &registry_events, client);
+
+ if ((str = getenv("PIPEWIRE_PROPS")) != NULL)
+ pw_properties_update_string(client->props, str, strlen(str));
+ if ((str = getenv("PIPEWIRE_QUANTUM")) != NULL) {
+ struct spa_fraction q;
+ if (sscanf(str, "%u/%u", &q.num, &q.denom) == 2 && q.denom != 0) {
+ pw_properties_setf(client->props, PW_KEY_NODE_RATE,
+ "1/%u", q.denom);
+ pw_properties_setf(client->props, PW_KEY_NODE_LATENCY,
+ "%u/%u", q.num, q.denom);
+ } else {
+ pw_log_warn("invalid PIPEWIRE_QUANTUM: %s", str);
+ }
+ }
+ if ((str = getenv("PIPEWIRE_LATENCY")) != NULL)
+ pw_properties_set(client->props, PW_KEY_NODE_LATENCY, str);
+ if ((str = getenv("PIPEWIRE_RATE")) != NULL)
+ pw_properties_set(client->props, PW_KEY_NODE_RATE, str);
+ if ((str = getenv("PIPEWIRE_LINK_PASSIVE")) != NULL)
+ pw_properties_set(client->props, PW_KEY_NODE_PASSIVE, str);
+
+ if ((str = pw_properties_get(client->props, PW_KEY_NODE_LATENCY)) != NULL) {
+ uint32_t num, denom;
+ if (sscanf(str, "%u/%u", &num, &denom) == 2 && denom != 0) {
+ client->latency = SPA_FRACTION(num, denom);
+ }
+ }
+ if (pw_properties_get(client->props, PW_KEY_NODE_NAME) == NULL)
+ pw_properties_set(client->props, PW_KEY_NODE_NAME, client_name);
+ if (pw_properties_get(client->props, PW_KEY_NODE_GROUP) == NULL)
+ pw_properties_setf(client->props, PW_KEY_NODE_GROUP, "jack-%d", getpid());
+ if (pw_properties_get(client->props, PW_KEY_NODE_DESCRIPTION) == NULL)
+ pw_properties_set(client->props, PW_KEY_NODE_DESCRIPTION, client_name);
+ if (pw_properties_get(client->props, PW_KEY_MEDIA_TYPE) == NULL)
+ pw_properties_set(client->props, PW_KEY_MEDIA_TYPE, "Audio");
+ if (pw_properties_get(client->props, PW_KEY_MEDIA_CATEGORY) == NULL)
+ pw_properties_set(client->props, PW_KEY_MEDIA_CATEGORY, "Duplex");
+ if (pw_properties_get(client->props, PW_KEY_MEDIA_ROLE) == NULL)
+ pw_properties_set(client->props, PW_KEY_MEDIA_ROLE, "DSP");
+ if (pw_properties_get(client->props, PW_KEY_NODE_ALWAYS_PROCESS) == NULL)
+ pw_properties_set(client->props, PW_KEY_NODE_ALWAYS_PROCESS, "true");
+ if (pw_properties_get(client->props, PW_KEY_NODE_LOCK_QUANTUM) == NULL)
+ pw_properties_set(client->props, PW_KEY_NODE_LOCK_QUANTUM, "true");
+ pw_properties_set(client->props, PW_KEY_NODE_TRANSPORT_SYNC, "true");
+
+ client->node = pw_core_create_object(client->core,
+ "client-node",
+ PW_TYPE_INTERFACE_ClientNode,
+ PW_VERSION_CLIENT_NODE,
+ &client->props->dict,
+ 0);
+ if (client->node == NULL)
+ goto init_failed;
+
+ pw_client_node_add_listener(client->node,
+ &client->node_listener, &client_node_events, client);
+ pw_proxy_add_listener((struct pw_proxy*)client->node,
+ &client->proxy_listener, &node_proxy_events, client);
+
+ client->info = SPA_NODE_INFO_INIT();
+ client->info.max_input_ports = UINT32_MAX;
+ client->info.max_output_ports = UINT32_MAX;
+ client->info.change_mask = SPA_NODE_CHANGE_MASK_FLAGS |
+ SPA_NODE_CHANGE_MASK_PROPS;
+ client->info.flags = SPA_NODE_FLAG_RT;
+ client->info.props = &client->props->dict;
+
+ pw_client_node_update(client->node,
+ PW_CLIENT_NODE_UPDATE_INFO,
+ 0, NULL, &client->info);
+ client->info.change_mask = 0;
+
+ client->show_monitor = pw_properties_get_bool(client->props, "jack.show-monitor", true);
+ client->merge_monitor = pw_properties_get_bool(client->props, "jack.merge-monitor", true);
+ client->short_name = pw_properties_get_bool(client->props, "jack.short-name", false);
+ client->filter_name = pw_properties_get_bool(client->props, "jack.filter-name", false);
+ client->filter_char = ' ';
+ if ((str = pw_properties_get(client->props, "jack.filter-char")) != NULL && str[0] != '\0')
+ client->filter_char = str[0];
+ client->locked_process = pw_properties_get_bool(client->props, "jack.locked-process", true);
+ client->default_as_system = pw_properties_get_bool(client->props, "jack.default-as-system", false);
+ client->fix_midi_events = pw_properties_get_bool(client->props, "jack.fix-midi-events", true);
+ client->global_buffer_size = pw_properties_get_bool(client->props, "jack.global-buffer-size", false);
+
+ client->self_connect_mode = SELF_CONNECT_ALLOW;
+ if ((str = pw_properties_get(client->props, "jack.self-connect-mode")) != NULL) {
+ if (spa_streq(str, "fail-external"))
+ client->self_connect_mode = SELF_CONNECT_FAIL_EXT;
+ else if (spa_streq(str, "ignore-external"))
+ client->self_connect_mode = SELF_CONNECT_IGNORE_EXT;
+ else if (spa_streq(str, "fail-all"))
+ client->self_connect_mode = SELF_CONNECT_FAIL_ALL;
+ else if (spa_streq(str, "ignore-all"))
+ client->self_connect_mode = SELF_CONNECT_IGNORE_ALL;
+ }
+ client->rt_max = pw_properties_get_int32(client->props, "rt.prio", DEFAULT_RT_MAX);
+
+ if (status)
+ *status = 0;
+
+ while (true) {
+ pw_thread_loop_wait(client->context.loop);
+
+ if (client->last_res < 0)
+ goto init_failed;
+
+ if (client->has_transport)
+ break;
+ }
+
+ if (!spa_streq(client->name, client_name)) {
+ if (status)
+ *status |= JackNameNotUnique;
+ if (options & JackUseExactName)
+ goto exit_unlock;
+ }
+ pw_thread_loop_unlock(client->context.loop);
+
+ pw_log_info("%p: opened", client);
+ return (jack_client_t *)client;
+
+no_props:
+ if (status)
+ *status = JackFailure | JackInitFailure;
+ goto exit;
+init_failed:
+ if (status)
+ *status = JackFailure | JackInitFailure;
+ goto exit_unlock;
+server_failed:
+ if (status)
+ *status = JackFailure | JackServerFailed;
+ goto exit_unlock;
+exit_unlock:
+ pw_thread_loop_unlock(client->context.loop);
+exit:
+ jack_client_close((jack_client_t *) client);
+ return NULL;
+disabled:
+ if (status)
+ *status = JackFailure | JackInitFailure;
+ return NULL;
+}
+
+SPA_EXPORT
+jack_client_t * jack_client_new (const char *client_name)
+{
+ jack_options_t options = JackUseExactName;
+ jack_status_t status;
+
+ if (getenv("JACK_START_SERVER") == NULL)
+ options |= JackNoStartServer;
+
+ return jack_client_open(client_name, options, &status, NULL);
+}
+
+SPA_EXPORT
+int jack_client_close (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ struct object *o;
+ int res;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ pw_log_info("%p: close", client);
+
+ c->destroyed = true;
+
+ res = jack_deactivate(client);
+
+ clean_transport(c);
+
+ if (c->context.loop)
+ pw_thread_loop_stop(c->context.loop);
+
+ if (c->registry) {
+ spa_hook_remove(&c->registry_listener);
+ pw_proxy_destroy((struct pw_proxy*)c->registry);
+ }
+ if (c->metadata && c->metadata->proxy) {
+ pw_proxy_destroy((struct pw_proxy*)c->metadata->proxy);
+ }
+ if (c->settings && c->settings->proxy) {
+ pw_proxy_destroy((struct pw_proxy*)c->settings->proxy);
+ }
+
+ if (c->core) {
+ spa_hook_remove(&c->core_listener);
+ pw_core_disconnect(c->core);
+ }
+
+ if (c->context.context)
+ pw_context_destroy(c->context.context);
+
+ if (c->context.loop)
+ pw_thread_loop_destroy(c->context.loop);
+
+ pw_log_debug("%p: free", client);
+
+ spa_list_consume(o, &c->context.objects, link)
+ free_object(c, o);
+ recycle_objects(c, 0);
+
+ pw_map_clear(&c->ports[SPA_DIRECTION_INPUT]);
+ pw_map_clear(&c->ports[SPA_DIRECTION_OUTPUT]);
+
+ pthread_mutex_destroy(&c->context.lock);
+ pthread_mutex_destroy(&c->rt_lock);
+ pw_properties_free(c->props);
+ free(c);
+
+ return res;
+}
+
+SPA_EXPORT
+jack_intclient_t jack_internal_client_handle (jack_client_t *client,
+ const char *client_name, jack_status_t *status)
+{
+ struct client *c = (struct client *) client;
+ spa_return_val_if_fail(c != NULL, 0);
+ if (status)
+ *status = JackNoSuchClient | JackFailure;
+ return 0;
+}
+
+SPA_EXPORT
+jack_intclient_t jack_internal_client_load (jack_client_t *client,
+ const char *client_name, jack_options_t options,
+ jack_status_t *status, ...)
+{
+ struct client *c = (struct client *) client;
+ spa_return_val_if_fail(c != NULL, 0);
+ if (status)
+ *status = JackNoSuchClient | JackFailure;
+ return 0;
+}
+
+SPA_EXPORT
+jack_status_t jack_internal_client_unload (jack_client_t *client,
+ jack_intclient_t intclient)
+{
+ struct client *c = (struct client *) client;
+ spa_return_val_if_fail(c != NULL, 0);
+ return JackFailure | JackNoSuchClient;
+}
+
+SPA_EXPORT
+char *jack_get_internal_client_name (jack_client_t *client,
+ jack_intclient_t intclient)
+{
+ struct client *c = (struct client *) client;
+ spa_return_val_if_fail(c != NULL, NULL);
+ return strdup(c->name);
+}
+
+SPA_EXPORT
+int jack_client_name_size (void)
+{
+ /* The JACK API specifies that this value includes the final NULL character. */
+ pw_log_trace("%d", JACK_CLIENT_NAME_SIZE+1);
+ return JACK_CLIENT_NAME_SIZE+1;
+}
+
+SPA_EXPORT
+char * jack_get_client_name (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ spa_return_val_if_fail(c != NULL, NULL);
+ return c->name;
+}
+
+SPA_EXPORT
+char *jack_get_uuid_for_client_name (jack_client_t *client,
+ const char *client_name)
+{
+ struct client *c = (struct client *) client;
+ struct object *o;
+ char *uuid = NULL;
+ bool monitor;
+
+ spa_return_val_if_fail(c != NULL, NULL);
+ spa_return_val_if_fail(client_name != NULL, NULL);
+
+ monitor = spa_strendswith(client_name, MONITOR_EXT);
+
+ pthread_mutex_lock(&c->context.lock);
+
+ spa_list_for_each(o, &c->context.objects, link) {
+ if (o->type != INTERFACE_Node)
+ continue;
+ if (spa_streq(o->node.name, client_name) ||
+ (monitor && spa_strneq(o->node.name, client_name,
+ strlen(client_name) - strlen(MONITOR_EXT)))) {
+ uuid = spa_aprintf( "%" PRIu64, client_make_uuid(o->serial, monitor));
+ break;
+ }
+ }
+ pw_log_debug("%p: name %s -> %s", client, client_name, uuid);
+ pthread_mutex_unlock(&c->context.lock);
+ return uuid;
+}
+
+SPA_EXPORT
+char *jack_get_client_name_by_uuid (jack_client_t *client,
+ const char *client_uuid )
+{
+ struct client *c = (struct client *) client;
+ struct object *o;
+ jack_uuid_t uuid;
+ char *name = NULL;
+ bool monitor;
+
+ spa_return_val_if_fail(c != NULL, NULL);
+ spa_return_val_if_fail(client_uuid != NULL, NULL);
+
+ if (jack_uuid_parse(client_uuid, &uuid) < 0)
+ return NULL;
+
+ monitor = uuid & (1 << 30);
+
+ pthread_mutex_lock(&c->context.lock);
+ spa_list_for_each(o, &c->context.objects, link) {
+ if (o->type != INTERFACE_Node)
+ continue;
+ if (client_make_uuid(o->serial, monitor) == uuid) {
+ pw_log_debug("%p: uuid %s (%"PRIu64")-> %s",
+ client, client_uuid, uuid, o->node.name);
+ name = spa_aprintf("%s%s", o->node.name, monitor ? MONITOR_EXT : "");
+ break;
+ }
+ }
+ pthread_mutex_unlock(&c->context.lock);
+ return name;
+}
+
+SPA_EXPORT
+int jack_internal_client_new (const char *client_name,
+ const char *load_name,
+ const char *load_init)
+{
+ pw_log_warn("not implemented %s %s %s", client_name, load_name, load_init);
+ return -ENOTSUP;
+}
+
+SPA_EXPORT
+void jack_internal_client_close (const char *client_name)
+{
+ pw_log_warn("not implemented %s", client_name);
+}
+
+static int do_activate(struct client *c)
+{
+ int res;
+ pw_client_node_set_active(c->node, true);
+ res = do_sync(c);
+ return res;
+}
+
+SPA_EXPORT
+int jack_activate (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ int res = 0;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ pw_log_info("%p: active:%d", c, c->active);
+
+ if (c->active)
+ return 0;
+
+ pw_thread_loop_lock(c->context.loop);
+ pw_data_loop_start(c->loop);
+
+ if ((res = do_activate(c)) < 0)
+ goto done;
+
+ c->activation->pending_new_pos = true;
+ c->activation->pending_sync = true;
+
+ c->active = true;
+
+ do_callback(c, graph_callback, c->graph_arg);
+
+done:
+ if (res < 0)
+ pw_data_loop_stop(c->loop);
+
+ pw_thread_loop_unlock(c->context.loop);
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_deactivate (jack_client_t *client)
+{
+ struct object *l;
+ struct client *c = (struct client *) client;
+ int res;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ pw_log_info("%p: active:%d", c, c->active);
+
+ if (!c->active)
+ return 0;
+
+ pw_thread_loop_lock(c->context.loop);
+ pw_data_loop_stop(c->loop);
+
+ pw_client_node_set_active(c->node, false);
+
+ c->activation->pending_new_pos = false;
+ c->activation->pending_sync = false;
+
+ spa_list_for_each(l, &c->context.objects, link) {
+ if (l->type != INTERFACE_Link || l->removed)
+ continue;
+ if (l->port_link.src_ours || l->port_link.dst_ours)
+ pw_registry_destroy(c->registry, l->id);
+ }
+
+ res = do_sync(c);
+
+ pw_thread_loop_unlock(c->context.loop);
+
+ if (res < 0)
+ return res;
+
+ c->active = false;
+
+ return 0;
+}
+
+SPA_EXPORT
+int jack_get_client_pid (const char *name)
+{
+ pw_log_error("not implemented on library side");
+ return 0;
+}
+
+SPA_EXPORT
+jack_native_thread_t jack_client_thread_id (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, (pthread_t){0});
+
+ return (jack_native_thread_t)pw_data_loop_get_thread(c->loop);
+}
+
+SPA_EXPORT
+int jack_is_realtime (jack_client_t *client)
+{
+ return 1;
+}
+
+SPA_EXPORT
+jack_nframes_t jack_thread_wait (jack_client_t *client, int status)
+{
+ pw_log_error("%p: jack_thread_wait: deprecated, use jack_cycle_wait/jack_cycle_signal", client);
+ return 0;
+}
+
+SPA_EXPORT
+jack_nframes_t jack_cycle_wait (jack_client_t* client)
+{
+ struct client *c = (struct client *) client;
+ jack_nframes_t res;
+
+ spa_return_val_if_fail(c != NULL, 0);
+
+ res = cycle_wait(c);
+ pw_log_trace("%p: result:%d", c, res);
+ return res;
+}
+
+SPA_EXPORT
+void jack_cycle_signal (jack_client_t* client, int status)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_if_fail(c != NULL);
+
+ pw_log_trace("%p: status:%d", c, status);
+ cycle_signal(c, status);
+}
+
+SPA_EXPORT
+int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_callback, void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ } else if (c->process_callback) {
+ pw_log_error("%p: process callback was already set", c);
+ return -EIO;
+ }
+ pw_log_debug("%p: %p %p", c, thread_callback, arg);
+ c->thread_callback = thread_callback;
+ c->thread_arg = arg;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_thread_init_callback (jack_client_t *client,
+ JackThreadInitCallback thread_init_callback,
+ void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ pw_log_debug("%p: %p %p", c, thread_init_callback, arg);
+ c->thread_init_callback = thread_init_callback;
+ c->thread_init_arg = arg;
+ return 0;
+}
+
+SPA_EXPORT
+void jack_on_shutdown (jack_client_t *client,
+ JackShutdownCallback shutdown_callback, void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_if_fail(c != NULL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ } else {
+ pw_log_debug("%p: %p %p", c, shutdown_callback, arg);
+ c->shutdown_callback = shutdown_callback;
+ c->shutdown_arg = arg;
+ }
+}
+
+SPA_EXPORT
+void jack_on_info_shutdown (jack_client_t *client,
+ JackInfoShutdownCallback shutdown_callback, void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_if_fail(c != NULL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ } else {
+ pw_log_debug("%p: %p %p", c, shutdown_callback, arg);
+ c->info_shutdown_callback = shutdown_callback;
+ c->info_shutdown_arg = arg;
+ }
+}
+
+SPA_EXPORT
+int jack_set_process_callback (jack_client_t *client,
+ JackProcessCallback process_callback,
+ void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ } else if (c->thread_callback) {
+ pw_log_error("%p: thread callback was already set", c);
+ return -EIO;
+ }
+
+ pw_log_debug("%p: %p %p", c, process_callback, arg);
+ c->process_callback = process_callback;
+ c->process_arg = arg;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_freewheel_callback (jack_client_t *client,
+ JackFreewheelCallback freewheel_callback,
+ void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ }
+ pw_log_debug("%p: %p %p", c, freewheel_callback, arg);
+ c->freewheel_callback = freewheel_callback;
+ c->freewheel_arg = arg;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_buffer_size_callback (jack_client_t *client,
+ JackBufferSizeCallback bufsize_callback,
+ void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ }
+ pw_log_debug("%p: %p %p", c, bufsize_callback, arg);
+ c->bufsize_callback = bufsize_callback;
+ c->bufsize_arg = arg;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_sample_rate_callback (jack_client_t *client,
+ JackSampleRateCallback srate_callback,
+ void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ }
+ pw_log_debug("%p: %p %p", c, srate_callback, arg);
+ c->srate_callback = srate_callback;
+ c->srate_arg = arg;
+ if (c->srate_callback && c->sample_rate != (uint32_t)-1)
+ c->srate_callback(c->sample_rate, c->srate_arg);
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_client_registration_callback (jack_client_t *client,
+ JackClientRegistrationCallback
+ registration_callback, void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ }
+ pw_log_debug("%p: %p %p", c, registration_callback, arg);
+ c->registration_callback = registration_callback;
+ c->registration_arg = arg;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_port_registration_callback (jack_client_t *client,
+ JackPortRegistrationCallback
+ registration_callback, void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ }
+ pw_log_debug("%p: %p %p", c, registration_callback, arg);
+ c->portregistration_callback = registration_callback;
+ c->portregistration_arg = arg;
+ return 0;
+}
+
+
+SPA_EXPORT
+int jack_set_port_connect_callback (jack_client_t *client,
+ JackPortConnectCallback
+ connect_callback, void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ }
+ pw_log_debug("%p: %p %p", c, connect_callback, arg);
+ c->connect_callback = connect_callback;
+ c->connect_arg = arg;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_port_rename_callback (jack_client_t *client,
+ JackPortRenameCallback rename_callback,
+ void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ }
+ pw_log_debug("%p: %p %p", c, rename_callback, arg);
+ c->rename_callback = rename_callback;
+ c->rename_arg = arg;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_graph_order_callback (jack_client_t *client,
+ JackGraphOrderCallback graph_callback,
+ void *data)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ }
+ pw_log_debug("%p: %p %p", c, graph_callback, data);
+ c->graph_callback = graph_callback;
+ c->graph_arg = data;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_xrun_callback (jack_client_t *client,
+ JackXRunCallback xrun_callback, void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ }
+ pw_log_debug("%p: %p %p", c, xrun_callback, arg);
+ c->xrun_callback = xrun_callback;
+ c->xrun_arg = arg;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_latency_callback (jack_client_t *client,
+ JackLatencyCallback latency_callback,
+ void *data)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ }
+ pw_log_debug("%p: %p %p", c, latency_callback, data);
+ c->latency_callback = latency_callback;
+ c->latency_arg = data;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_freewheel(jack_client_t* client, int onoff)
+{
+ struct client *c = (struct client *) client;
+
+ pw_log_info("%p: freewheel %d", client, onoff);
+
+ pw_thread_loop_lock(c->context.loop);
+ pw_properties_set(c->props, "node.group",
+ onoff ? "pipewire.freewheel" : "");
+
+ c->info.change_mask |= SPA_NODE_CHANGE_MASK_PROPS;
+ c->info.props = &c->props->dict;
+
+ pw_client_node_update(c->node,
+ PW_CLIENT_NODE_UPDATE_INFO,
+ 0, NULL, &c->info);
+ c->info.change_mask = 0;
+ pw_thread_loop_unlock(c->context.loop);
+
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ pw_log_info("%p: buffer-size %u", client, nframes);
+
+ pw_thread_loop_lock(c->context.loop);
+ if (c->global_buffer_size && c->settings && c->settings->proxy) {
+ char val[256];
+ snprintf(val, sizeof(val), "%u", nframes == 1 ? 0: nframes);
+ pw_metadata_set_property(c->settings->proxy, 0,
+ "clock.force-quantum", "", val);
+ } else {
+ pw_properties_setf(c->props, PW_KEY_NODE_FORCE_QUANTUM, "%u", nframes);
+
+ c->info.change_mask |= SPA_NODE_CHANGE_MASK_PROPS;
+ c->info.props = &c->props->dict;
+
+ pw_client_node_update(c->node,
+ PW_CLIENT_NODE_UPDATE_INFO,
+ 0, NULL, &c->info);
+ c->info.change_mask = 0;
+ }
+ pw_thread_loop_unlock(c->context.loop);
+
+ return 0;
+}
+
+SPA_EXPORT
+jack_nframes_t jack_get_sample_rate (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ jack_nframes_t res = -1;
+
+ spa_return_val_if_fail(c != NULL, 0);
+
+ if (!c->active)
+ res = c->latency.denom;
+ if (c->active || res == (uint32_t)-1) {
+ res = c->sample_rate;
+ if (res == (uint32_t)-1) {
+ if (c->rt.position)
+ res = c->rt.position->clock.rate.denom;
+ else if (c->position)
+ res = c->position->clock.rate.denom;
+ }
+ }
+ pw_log_debug("sample_rate: %u", res);
+ return res;
+}
+
+SPA_EXPORT
+jack_nframes_t jack_get_buffer_size (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ jack_nframes_t res = -1;
+
+ spa_return_val_if_fail(c != NULL, 0);
+
+ if (!c->active)
+ res = c->latency.num;
+ if (c->active || res == (uint32_t)-1) {
+ res = c->buffer_frames;
+ if (res == (uint32_t)-1) {
+ if (c->rt.position)
+ res = c->rt.position->clock.duration;
+ else if (c->position)
+ res = c->position->clock.duration;
+ }
+ }
+ c->buffer_frames = res;
+ pw_log_debug("buffer_frames: %u", res);
+ return res;
+}
+
+SPA_EXPORT
+int jack_engine_takeover_timebase (jack_client_t *client)
+{
+ pw_log_error("%p: deprecated", client);
+ return 0;
+}
+
+SPA_EXPORT
+float jack_cpu_load (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ float res = 0.0f;
+
+ spa_return_val_if_fail(c != NULL, 0.0);
+
+ if (c->driver_activation)
+ res = c->driver_activation->cpu_load[0] * 100.0f;
+
+ pw_log_trace("%p: cpu load %f", client, res);
+ return res;
+}
+
+#include "statistics.c"
+
+static void *get_buffer_input_float(struct port *p, jack_nframes_t frames);
+static void *get_buffer_input_midi(struct port *p, jack_nframes_t frames);
+static void *get_buffer_input_empty(struct port *p, jack_nframes_t frames);
+static void *get_buffer_output_float(struct port *p, jack_nframes_t frames);
+static void *get_buffer_output_midi(struct port *p, jack_nframes_t frames);
+static void *get_buffer_output_empty(struct port *p, jack_nframes_t frames);
+
+SPA_EXPORT
+jack_port_t * jack_port_register (jack_client_t *client,
+ const char *port_name,
+ const char *port_type,
+ unsigned long flags,
+ unsigned long buffer_frames)
+{
+ struct client *c = (struct client *) client;
+ enum spa_direction direction;
+ struct object *o;
+ jack_port_type_id_t type_id;
+ uint8_t buffer[1024];
+ struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
+ struct spa_pod *params[6];
+ uint32_t n_params = 0;
+ struct port *p;
+ int res;
+
+ spa_return_val_if_fail(c != NULL, NULL);
+ spa_return_val_if_fail(port_name != NULL, NULL);
+ spa_return_val_if_fail(port_type != NULL, NULL);
+
+ pw_log_info("%p: port register \"%s:%s\" \"%s\" %08lx %ld",
+ c, c->name, port_name, port_type, flags, buffer_frames);
+
+ if (flags & JackPortIsInput)
+ direction = PW_DIRECTION_INPUT;
+ else if (flags & JackPortIsOutput)
+ direction = PW_DIRECTION_OUTPUT;
+ else {
+ pw_log_warn("invalid port flags %lu for %s", flags, port_name);
+ return NULL;
+ }
+
+ if ((type_id = string_to_type(port_type)) == SPA_ID_INVALID) {
+ pw_log_warn("unknown port type %s", port_type);
+ return NULL;
+ }
+
+ if ((p = alloc_port(c, direction)) == NULL) {
+ pw_log_warn("can't allocate port %s: %m", port_name);
+ return NULL;
+ }
+
+ o = p->object;
+ o->port.flags = flags;
+ snprintf(o->port.name, sizeof(o->port.name), "%s:%s", c->name, port_name);
+ o->port.type_id = type_id;
+
+ init_buffer(p);
+
+ if (direction == SPA_DIRECTION_INPUT) {
+ switch (type_id) {
+ case TYPE_ID_AUDIO:
+ case TYPE_ID_VIDEO:
+ p->get_buffer = get_buffer_input_float;
+ break;
+ case TYPE_ID_MIDI:
+ p->get_buffer = get_buffer_input_midi;
+ break;
+ default:
+ p->get_buffer = get_buffer_input_empty;
+ break;
+ }
+ } else {
+ switch (type_id) {
+ case TYPE_ID_AUDIO:
+ case TYPE_ID_VIDEO:
+ p->get_buffer = get_buffer_output_float;
+ break;
+ case TYPE_ID_MIDI:
+ p->get_buffer = get_buffer_output_midi;
+ break;
+ default:
+ p->get_buffer = get_buffer_output_empty;
+ break;
+ }
+ }
+
+ pw_log_debug("%p: port %p", c, p);
+
+ spa_list_init(&p->mix);
+
+ pw_properties_set(p->props, PW_KEY_FORMAT_DSP, port_type);
+ pw_properties_set(p->props, PW_KEY_PORT_NAME, port_name);
+ if (flags > 0x1f) {
+ pw_properties_setf(p->props, PW_KEY_PORT_EXTRA,
+ "jack:flags:%lu", flags & ~0x1f);
+ }
+ if (flags & JackPortIsPhysical)
+ pw_properties_set(p->props, PW_KEY_PORT_PHYSICAL, "true");
+ if (flags & JackPortIsTerminal)
+ pw_properties_set(p->props, PW_KEY_PORT_TERMINAL, "true");
+
+ p->info = SPA_PORT_INFO_INIT();
+ p->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS;
+ p->info.flags = SPA_PORT_FLAG_NO_REF;
+ p->info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS;
+ p->info.props = &p->props->dict;
+ p->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
+ p->params[IDX_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
+ p->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ);
+ p->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
+ p->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
+ p->params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
+ p->info.params = p->params;
+ p->info.n_params = N_PORT_PARAMS;
+
+ param_enum_format(c, p, &params[n_params++], &b);
+ param_buffers(c, p, &params[n_params++], &b);
+ param_io(c, p, &params[n_params++], &b);
+ param_latency(c, p, &params[n_params++], &b);
+ param_latency_other(c, p, &params[n_params++], &b);
+
+ pw_thread_loop_lock(c->context.loop);
+
+ pw_client_node_port_update(c->node,
+ direction,
+ p->port_id,
+ PW_CLIENT_NODE_PORT_UPDATE_PARAMS |
+ PW_CLIENT_NODE_PORT_UPDATE_INFO,
+ n_params,
+ (const struct spa_pod **) params,
+ &p->info);
+
+ p->info.change_mask = 0;
+
+ res = do_sync(c);
+
+ pw_thread_loop_unlock(c->context.loop);
+
+ if (res < 0) {
+ pw_log_warn("can't create port %s: %s", port_name,
+ spa_strerror(res));
+ return NULL;
+ }
+
+ return (jack_port_t *) o;
+}
+
+static int
+do_invalidate_port(struct spa_loop *loop,
+ bool async, uint32_t seq, const void *data, size_t size, void *user_data)
+{
+ struct port *p = user_data;
+ p->valid = false;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_port_unregister (jack_client_t *client, jack_port_t *port)
+{
+ struct client *c = (struct client *) client;
+ struct object *o = (struct object *) port;
+ struct port *p;
+ int res;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+ spa_return_val_if_fail(o != NULL, -EINVAL);
+
+ pw_thread_loop_lock(c->context.loop);
+
+ p = o->port.port;
+ if (o->type != INTERFACE_Port || p == NULL || !p->valid ||
+ o->client != c) {
+ pw_log_error("%p: invalid port %p", client, port);
+ res = -EINVAL;
+ goto done;
+ }
+ pw_data_loop_invoke(c->loop,
+ do_invalidate_port, 1, NULL, 0, !c->data_locked, p);
+
+ pw_log_info("%p: port %p unregister \"%s\"", client, port, o->port.name);
+
+ pw_client_node_port_update(c->node,
+ p->direction,
+ p->port_id,
+ 0, 0, NULL, NULL);
+
+ res = do_sync(c);
+ if (res < 0) {
+ pw_log_warn("can't unregister port %s: %s", o->port.name,
+ spa_strerror(res));
+ }
+ free_port(c, p);
+done:
+ pw_thread_loop_unlock(c->context.loop);
+
+ return res;
+}
+
+static struct buffer *get_mix_buffer(struct mix *mix, jack_nframes_t frames)
+{
+ struct spa_io_buffers *io;
+
+ if (mix->peer_port != NULL)
+ prepare_output(mix->peer_port, frames);
+
+ io = mix->io;
+ if (io == NULL ||
+ io->status != SPA_STATUS_HAVE_DATA ||
+ io->buffer_id >= mix->n_buffers)
+ return NULL;
+
+ return &mix->buffers[io->buffer_id];
+}
+
+static void *get_buffer_input_float(struct port *p, jack_nframes_t frames)
+{
+ struct mix *mix;
+ struct buffer *b;
+ void *ptr = NULL;
+ float *mix_ptr[MAX_MIX], *np;
+ uint32_t n_ptr = 0;
+ bool ptr_aligned = true;
+
+ spa_list_for_each(mix, &p->mix, port_link) {
+ struct spa_data *d;
+ uint32_t offset, size;
+
+ pw_log_trace_fp("%p: port %s mix %d.%d get buffer %d",
+ p->client, p->object->port.name, p->port_id, mix->id, frames);
+
+ if ((b = get_mix_buffer(mix, frames)) == NULL)
+ continue;
+
+ d = &b->datas[0];
+ offset = SPA_MIN(d->chunk->offset, d->maxsize);
+ size = SPA_MIN(d->chunk->size, d->maxsize - offset);
+ if (size / sizeof(float) < frames)
+ continue;
+
+ np = SPA_PTROFF(d->data, offset, float);
+ if (!SPA_IS_ALIGNED(np, 16))
+ ptr_aligned = false;
+
+ mix_ptr[n_ptr++] = np;
+ if (n_ptr == MAX_MIX)
+ break;
+ }
+ if (n_ptr == 1) {
+ ptr = mix_ptr[0];
+ } else if (n_ptr > 1) {
+ ptr = p->emptyptr;
+ mix_function(ptr, mix_ptr, n_ptr, ptr_aligned, frames);
+ p->zeroed = false;
+ }
+ if (ptr == NULL)
+ ptr = init_buffer(p);
+ return ptr;
+}
+
+static void *get_buffer_input_midi(struct port *p, jack_nframes_t frames)
+{
+ struct mix *mix;
+ void *ptr = p->emptyptr;
+ struct spa_pod_sequence *seq[MAX_MIX];
+ uint32_t n_seq = 0;
+
+ jack_midi_clear_buffer(ptr);
+
+ spa_list_for_each(mix, &p->mix, port_link) {
+ struct spa_data *d;
+ struct buffer *b;
+ void *pod;
+
+ pw_log_trace_fp("%p: port %p mix %d.%d get buffer %d",
+ p->client, p, p->port_id, mix->id, frames);
+
+ if ((b = get_mix_buffer(mix, frames)) == NULL)
+ continue;
+
+ d = &b->datas[0];
+
+ if ((pod = spa_pod_from_data(d->data, d->maxsize, d->chunk->offset, d->chunk->size)) == NULL)
+ continue;
+ if (!spa_pod_is_sequence(pod))
+ continue;
+
+ seq[n_seq++] = pod;
+ if (n_seq == MAX_MIX)
+ break;
+ }
+ convert_to_midi(seq, n_seq, ptr, p->client->fix_midi_events);
+
+ return ptr;
+}
+
+static void *get_buffer_output_float(struct port *p, jack_nframes_t frames)
+{
+ void *ptr;
+
+ ptr = get_buffer_output(p, frames, sizeof(float), NULL);
+ if (SPA_UNLIKELY(p->empty_out = (ptr == NULL)))
+ ptr = p->emptyptr;
+ return ptr;
+}
+
+static void *get_buffer_output_midi(struct port *p, jack_nframes_t frames)
+{
+ p->empty_out = true;
+ return p->emptyptr;
+}
+
+static void *get_buffer_output_empty(struct port *p, jack_nframes_t frames)
+{
+ p->empty_out = true;
+ return p->emptyptr;
+}
+
+static void *get_buffer_input_empty(struct port *p, jack_nframes_t frames)
+{
+ return init_buffer(p);
+}
+
+SPA_EXPORT
+void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames)
+{
+ struct object *o = (struct object *) port;
+ struct port *p;
+ void *ptr;
+
+ spa_return_val_if_fail(o != NULL, NULL);
+
+ if (o->type != INTERFACE_Port || o->client == NULL)
+ return NULL;
+
+ if ((p = o->port.port) == NULL) {
+ struct mix *mix;
+ struct buffer *b;
+ struct spa_data *d;
+ uint32_t offset, size;
+
+ if ((mix = find_mix_peer(o->client, o->id)) == NULL)
+ return NULL;
+
+ pw_log_trace("peer mix: %p %d", mix, mix->peer_id);
+
+ if ((b = get_mix_buffer(mix, frames)) == NULL)
+ return NULL;
+
+ d = &b->datas[0];
+ offset = SPA_MIN(d->chunk->offset, d->maxsize);
+ size = SPA_MIN(d->chunk->size, d->maxsize - offset);
+ if (size / sizeof(float) < frames)
+ return NULL;
+
+ return SPA_PTROFF(d->data, offset, void);
+ }
+ if (!p->valid)
+ return NULL;
+
+ ptr = p->get_buffer(p, frames);
+ pw_log_trace_fp("%p: port %p buffer %p empty:%u", p->client, p, ptr, p->empty_out);
+ return ptr;
+}
+
+SPA_EXPORT
+jack_uuid_t jack_port_uuid (const jack_port_t *port)
+{
+ struct object *o = (struct object *) port;
+ spa_return_val_if_fail(o != NULL, 0);
+ return jack_port_uuid_generate(o->serial);
+}
+
+static const char *port_name(struct object *o)
+{
+ const char *name;
+ struct client *c = o->client;
+ if (c->default_as_system && is_port_default(c, o))
+ name = o->port.system;
+ else
+ name = o->port.name;
+ return name;
+}
+
+SPA_EXPORT
+const char * jack_port_name (const jack_port_t *port)
+{
+ struct object *o = (struct object *) port;
+ spa_return_val_if_fail(o != NULL, NULL);
+ return port_name(o);
+}
+
+SPA_EXPORT
+const char * jack_port_short_name (const jack_port_t *port)
+{
+ struct object *o = (struct object *) port;
+ spa_return_val_if_fail(o != NULL, NULL);
+ return strchr(port_name(o), ':') + 1;
+}
+
+SPA_EXPORT
+int jack_port_flags (const jack_port_t *port)
+{
+ struct object *o = (struct object *) port;
+ spa_return_val_if_fail(o != NULL, 0);
+ return o->port.flags;
+}
+
+SPA_EXPORT
+const char * jack_port_type (const jack_port_t *port)
+{
+ struct object *o = (struct object *) port;
+ spa_return_val_if_fail(o != NULL, NULL);
+ return type_to_string(o->port.type_id);
+}
+
+SPA_EXPORT
+jack_port_type_id_t jack_port_type_id (const jack_port_t *port)
+{
+ struct object *o = (struct object *) port;
+ spa_return_val_if_fail(o != NULL, 0);
+ return o->port.type_id;
+}
+
+SPA_EXPORT
+int jack_port_is_mine (const jack_client_t *client, const jack_port_t *port)
+{
+ struct object *o = (struct object *) port;
+ spa_return_val_if_fail(o != NULL, 0);
+ return o->type == INTERFACE_Port &&
+ o->port.port != NULL &&
+ o->port.port->client == (struct client*)client;
+}
+
+SPA_EXPORT
+int jack_port_connected (const jack_port_t *port)
+{
+ struct object *o = (struct object *) port;
+ struct client *c;
+ struct object *l;
+ int res = 0;
+
+ spa_return_val_if_fail(o != NULL, 0);
+ if (o->type != INTERFACE_Port || o->client == NULL)
+ return 0;
+
+ c = o->client;
+
+ pthread_mutex_lock(&c->context.lock);
+ spa_list_for_each(l, &c->context.objects, link) {
+ if (l->type != INTERFACE_Link || l->removed)
+ continue;
+ if (!l->port_link.is_complete)
+ continue;
+ if (l->port_link.src_serial == o->serial ||
+ l->port_link.dst_serial == o->serial)
+ res++;
+ }
+ pthread_mutex_unlock(&c->context.lock);
+
+ pw_log_debug("%p: id:%u/%u res:%d", port, o->id, o->serial, res);
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_port_connected_to (const jack_port_t *port,
+ const char *port_name)
+{
+ struct object *o = (struct object *) port;
+ struct client *c;
+ struct object *p, *l;
+ int res = 0;
+
+ spa_return_val_if_fail(o != NULL, 0);
+ spa_return_val_if_fail(port_name != NULL, 0);
+ if (o->type != INTERFACE_Port || o->client == NULL)
+ return 0;
+
+ c = o->client;
+
+ pthread_mutex_lock(&c->context.lock);
+
+ p = find_port_by_name(c, port_name);
+ if (p == NULL)
+ goto exit;
+
+ if (GET_DIRECTION(p->port.flags) == GET_DIRECTION(o->port.flags))
+ goto exit;
+
+ if (p->port.flags & JackPortIsOutput) {
+ l = p;
+ p = o;
+ o = l;
+ }
+ if ((l = find_link(c, o->id, p->id)) != NULL &&
+ l->port_link.is_complete)
+ res = 1;
+
+ exit:
+ pthread_mutex_unlock(&c->context.lock);
+ pw_log_debug("%p: id:%u/%u name:%s res:%d", port, o->id,
+ o->serial, port_name, res);
+
+ return res;
+}
+
+SPA_EXPORT
+const char ** jack_port_get_connections (const jack_port_t *port)
+{
+ struct object *o = (struct object *) port;
+
+ spa_return_val_if_fail(o != NULL, NULL);
+ if (o->type != INTERFACE_Port || o->client == NULL)
+ return NULL;
+
+ return jack_port_get_all_connections((jack_client_t *)o->client, port);
+}
+
+SPA_EXPORT
+const char ** jack_port_get_all_connections (const jack_client_t *client,
+ const jack_port_t *port)
+{
+ struct client *c = (struct client *) client;
+ struct object *o = (struct object *) port;
+ struct object *p, *l;
+ const char **res;
+ int count = 0;
+ struct pw_array tmp;
+
+ spa_return_val_if_fail(c != NULL, NULL);
+ spa_return_val_if_fail(o != NULL, NULL);
+
+ pw_array_init(&tmp, sizeof(void*) * 32);
+
+ pthread_mutex_lock(&c->context.lock);
+ spa_list_for_each(l, &c->context.objects, link) {
+ if (l->type != INTERFACE_Link || l->removed)
+ continue;
+ if (l->port_link.src_serial == o->serial)
+ p = find_type(c, l->port_link.dst, INTERFACE_Port, true);
+ else if (l->port_link.dst_serial == o->serial)
+ p = find_type(c, l->port_link.src, INTERFACE_Port, true);
+ else
+ continue;
+
+ if (p == NULL)
+ continue;
+
+ pw_array_add_ptr(&tmp, (void*)port_name(p));
+ count++;
+ }
+ pthread_mutex_unlock(&c->context.lock);
+
+ if (count == 0) {
+ pw_array_clear(&tmp);
+ res = NULL;
+ } else {
+ pw_array_add_ptr(&tmp, NULL);
+ res = tmp.data;
+ }
+ return res;
+}
+
+SPA_EXPORT
+int jack_port_tie (jack_port_t *src, jack_port_t *dst)
+{
+ pw_log_warn("not implemented %p %p", src, dst);
+ return -ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_port_untie (jack_port_t *port)
+{
+ pw_log_warn("not implemented %p", port);
+ return -ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_port_set_name (jack_port_t *port, const char *port_name)
+{
+ pw_log_warn("deprecated");
+ return 0;
+}
+
+SPA_EXPORT
+int jack_port_rename (jack_client_t* client, jack_port_t *port, const char *port_name)
+{
+ struct client *c = (struct client *) client;
+ struct object *o = (struct object *) port;
+ struct port *p;
+ int res = 0;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+ spa_return_val_if_fail(o != NULL, -EINVAL);
+ spa_return_val_if_fail(port_name != NULL, -EINVAL);
+
+ pw_thread_loop_lock(c->context.loop);
+
+ pw_log_info("%p: port rename %p %s -> %s:%s",
+ client, port, o->port.name, c->name, port_name);
+
+ p = o->port.port;
+ if (p == NULL || !p->valid) {
+ res = -EINVAL;
+ goto done;
+ }
+
+ pw_properties_set(p->props, PW_KEY_PORT_NAME, port_name);
+ snprintf(o->port.name, sizeof(o->port.name), "%s:%s", c->name, port_name);
+
+ p->info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS;
+ p->info.props = &p->props->dict;
+
+ pw_client_node_port_update(c->node,
+ p->direction,
+ p->port_id,
+ PW_CLIENT_NODE_PORT_UPDATE_INFO,
+ 0, NULL,
+ &p->info);
+ p->info.change_mask = 0;
+
+done:
+ pw_thread_loop_unlock(c->context.loop);
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_port_set_alias (jack_port_t *port, const char *alias)
+{
+ struct object *o = (struct object *) port;
+ struct client *c;
+ struct port *p;
+ const char *key;
+ int res = 0;
+
+ spa_return_val_if_fail(o != NULL, -EINVAL);
+ spa_return_val_if_fail(alias != NULL, -EINVAL);
+
+ c = o->client;
+ if (o->type != INTERFACE_Port || c == NULL)
+ return -EINVAL;
+
+ pw_thread_loop_lock(c->context.loop);
+
+ p = o->port.port;
+ if (p == NULL || !p->valid) {
+ res = -EINVAL;
+ goto done;
+ }
+
+ if (o->port.alias1[0] == '\0') {
+ key = PW_KEY_OBJECT_PATH;
+ snprintf(o->port.alias1, sizeof(o->port.alias1), "%s", alias);
+ }
+ else if (o->port.alias2[0] == '\0') {
+ key = PW_KEY_PORT_ALIAS;
+ snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", alias);
+ }
+ else {
+ res = -1;
+ goto done;
+ }
+
+ pw_properties_set(p->props, key, alias);
+
+ p->info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS;
+ p->info.props = &p->props->dict;
+
+ pw_client_node_port_update(c->node,
+ p->direction,
+ p->port_id,
+ PW_CLIENT_NODE_PORT_UPDATE_INFO,
+ 0, NULL,
+ &p->info);
+ p->info.change_mask = 0;
+
+done:
+ pw_thread_loop_unlock(c->context.loop);
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_port_unset_alias (jack_port_t *port, const char *alias)
+{
+ struct object *o = (struct object *) port;
+ struct client *c;
+ struct port *p;
+ const char *key;
+ int res = 0;
+
+ spa_return_val_if_fail(o != NULL, -EINVAL);
+ spa_return_val_if_fail(alias != NULL, -EINVAL);
+
+ c = o->client;
+ if (o->type != INTERFACE_Port || c == NULL)
+ return -EINVAL;
+
+ pw_thread_loop_lock(c->context.loop);
+ p = o->port.port;
+ if (p == NULL || !p->valid) {
+ res = -EINVAL;
+ goto done;
+ }
+
+ if (spa_streq(o->port.alias1, alias))
+ key = PW_KEY_OBJECT_PATH;
+ else if (spa_streq(o->port.alias2, alias))
+ key = PW_KEY_PORT_ALIAS;
+ else {
+ res = -1;
+ goto done;
+ }
+
+ pw_properties_set(p->props, key, NULL);
+
+ p->info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS;
+ p->info.props = &p->props->dict;
+
+ pw_client_node_port_update(c->node,
+ p->direction,
+ p->port_id,
+ PW_CLIENT_NODE_PORT_UPDATE_INFO,
+ 0, NULL,
+ &p->info);
+ p->info.change_mask = 0;
+
+done:
+ pw_thread_loop_unlock(c->context.loop);
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2])
+{
+ struct object *o = (struct object *) port;
+ int res = 0;
+
+ spa_return_val_if_fail(o != NULL, -EINVAL);
+ spa_return_val_if_fail(aliases != NULL, -EINVAL);
+ spa_return_val_if_fail(aliases[0] != NULL, -EINVAL);
+ spa_return_val_if_fail(aliases[1] != NULL, -EINVAL);
+
+ if (o->port.alias1[0] != '\0') {
+ snprintf(aliases[0], REAL_JACK_PORT_NAME_SIZE+1, "%s", o->port.alias1);
+ res++;
+ }
+ if (o->port.alias2[0] != '\0') {
+ snprintf(aliases[1], REAL_JACK_PORT_NAME_SIZE+1, "%s", o->port.alias2);
+ res++;
+ }
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_port_request_monitor (jack_port_t *port, int onoff)
+{
+ struct object *o = (struct object *) port;
+
+ spa_return_val_if_fail(o != NULL, -EINVAL);
+
+ if (onoff)
+ o->port.monitor_requests++;
+ else if (o->port.monitor_requests > 0)
+ o->port.monitor_requests--;
+ return 0;
+}
+
+SPA_EXPORT
+int jack_port_request_monitor_by_name (jack_client_t *client,
+ const char *port_name, int onoff)
+{
+ struct client *c = (struct client *) client;
+ struct object *p;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+ spa_return_val_if_fail(port_name != NULL, -EINVAL);
+
+ pthread_mutex_lock(&c->context.lock);
+ p = find_port_by_name(c, port_name);
+ pthread_mutex_unlock(&c->context.lock);
+
+ if (p == NULL) {
+ pw_log_error("%p: jack_port_request_monitor_by_name called"
+ " with an incorrect port %s", client, port_name);
+ return -1;
+ }
+
+ return jack_port_request_monitor((jack_port_t*)p, onoff);
+}
+
+SPA_EXPORT
+int jack_port_ensure_monitor (jack_port_t *port, int onoff)
+{
+ struct object *o = (struct object *) port;
+
+ spa_return_val_if_fail(o != NULL, -EINVAL);
+
+ if (onoff) {
+ if (o->port.monitor_requests == 0)
+ o->port.monitor_requests++;
+ } else {
+ if (o->port.monitor_requests > 0)
+ o->port.monitor_requests = 0;
+ }
+ return 0;
+}
+
+SPA_EXPORT
+int jack_port_monitoring_input (jack_port_t *port)
+{
+ struct object *o = (struct object *) port;
+ spa_return_val_if_fail(o != NULL, -EINVAL);
+ return o->port.monitor_requests > 0;
+}
+
+static void link_proxy_error(void *data, int seq, int res, const char *message)
+{
+ int *link_res = data;
+ *link_res = res;
+}
+
+static const struct pw_proxy_events link_proxy_events = {
+ PW_VERSION_PROXY_EVENTS,
+ .error = link_proxy_error,
+};
+
+static int check_connect(struct client *c, struct object *src, struct object *dst)
+{
+ int src_self, dst_self, sum;
+
+ if (c->self_connect_mode == SELF_CONNECT_ALLOW)
+ return 1;
+
+ src_self = src->port.node_id == c->node_id ? 1 : 0;
+ dst_self = dst->port.node_id == c->node_id ? 1 : 0;
+ sum = src_self + dst_self;
+ /* check for no self connection first */
+ if (sum == 0)
+ return 1;
+
+ /* internal connection */
+ if (sum == 2 &&
+ (c->self_connect_mode == SELF_CONNECT_FAIL_EXT ||
+ c->self_connect_mode == SELF_CONNECT_IGNORE_EXT))
+ return 1;
+
+ /* failure -> -1 */
+ if (c->self_connect_mode < 0)
+ return -1;
+
+ /* ignore -> 0 */
+ return 0;
+}
+
+SPA_EXPORT
+int jack_connect (jack_client_t *client,
+ const char *source_port,
+ const char *destination_port)
+{
+ struct client *c = (struct client *) client;
+ struct object *src, *dst;
+ struct spa_dict props;
+ struct spa_dict_item items[6];
+ struct pw_proxy *proxy;
+ struct spa_hook listener;
+ char val[4][16];
+ const char *str;
+ int res, link_res = 0;
+
+ spa_return_val_if_fail(c != NULL, EINVAL);
+ spa_return_val_if_fail(source_port != NULL, EINVAL);
+ spa_return_val_if_fail(destination_port != NULL, EINVAL);
+
+ pw_log_info("%p: connect %s %s", client, source_port, destination_port);
+
+ pw_thread_loop_lock(c->context.loop);
+
+ src = find_port_by_name(c, source_port);
+ dst = find_port_by_name(c, destination_port);
+
+ if (src == NULL || dst == NULL ||
+ !(src->port.flags & JackPortIsOutput) ||
+ !(dst->port.flags & JackPortIsInput) ||
+ src->port.type_id != dst->port.type_id) {
+ res = -EINVAL;
+ goto exit;
+ }
+ if ((res = check_connect(c, src, dst)) != 1)
+ goto exit;
+
+ snprintf(val[0], sizeof(val[0]), "%d", src->port.node_id);
+ snprintf(val[1], sizeof(val[1]), "%d", src->id);
+ snprintf(val[2], sizeof(val[2]), "%d", dst->port.node_id);
+ snprintf(val[3], sizeof(val[3]), "%d", dst->id);
+
+ props = SPA_DICT_INIT(items, 0);
+ items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_OUTPUT_NODE, val[0]);
+ items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_OUTPUT_PORT, val[1]);
+ items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_INPUT_NODE, val[2]);
+ items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_INPUT_PORT, val[3]);
+ items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_OBJECT_LINGER, "true");
+ if ((str = pw_properties_get(c->props, PW_KEY_NODE_PASSIVE)) != NULL &&
+ pw_properties_parse_bool(str))
+ items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_PASSIVE, "true");
+
+ proxy = pw_core_create_object(c->core,
+ "link-factory",
+ PW_TYPE_INTERFACE_Link,
+ PW_VERSION_LINK,
+ &props,
+ 0);
+ if (proxy == NULL) {
+ res = -errno;
+ goto exit;
+ }
+
+ spa_zero(listener);
+ pw_proxy_add_listener(proxy, &listener, &link_proxy_events, &link_res);
+
+ res = do_sync(c);
+
+ spa_hook_remove(&listener);
+
+ if (link_res < 0)
+ res = link_res;
+
+ pw_proxy_destroy(proxy);
+
+ exit:
+ pw_thread_loop_unlock(c->context.loop);
+
+ return -res;
+}
+
+SPA_EXPORT
+int jack_disconnect (jack_client_t *client,
+ const char *source_port,
+ const char *destination_port)
+{
+ struct client *c = (struct client *) client;
+ struct object *src, *dst, *l;
+ int res;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+ spa_return_val_if_fail(source_port != NULL, -EINVAL);
+ spa_return_val_if_fail(destination_port != NULL, -EINVAL);
+
+ pw_log_info("%p: disconnect %s %s", client, source_port, destination_port);
+
+ pw_thread_loop_lock(c->context.loop);
+
+ src = find_port_by_name(c, source_port);
+ dst = find_port_by_name(c, destination_port);
+
+ pw_log_debug("%p: %d %d", client, src->id, dst->id);
+
+ if (src == NULL || dst == NULL ||
+ !(src->port.flags & JackPortIsOutput) ||
+ !(dst->port.flags & JackPortIsInput)) {
+ res = -EINVAL;
+ goto exit;
+ }
+
+ if ((res = check_connect(c, src, dst)) != 1)
+ goto exit;
+
+ if ((l = find_link(c, src->id, dst->id)) == NULL) {
+ res = -ENOENT;
+ goto exit;
+ }
+
+ pw_registry_destroy(c->registry, l->id);
+
+ res = do_sync(c);
+
+ exit:
+ pw_thread_loop_unlock(c->context.loop);
+
+ return -res;
+}
+
+SPA_EXPORT
+int jack_port_disconnect (jack_client_t *client, jack_port_t *port)
+{
+ struct client *c = (struct client *) client;
+ struct object *o = (struct object *) port;
+ struct object *l;
+ int res;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+ spa_return_val_if_fail(o != NULL, -EINVAL);
+
+ pw_log_debug("%p: disconnect %p", client, port);
+
+ pw_thread_loop_lock(c->context.loop);
+
+ spa_list_for_each(l, &c->context.objects, link) {
+ if (l->type != INTERFACE_Link || l->removed)
+ continue;
+ if (l->port_link.src_serial == o->serial ||
+ l->port_link.dst_serial == o->serial) {
+ pw_registry_destroy(c->registry, l->id);
+ }
+ }
+ res = do_sync(c);
+
+ pw_thread_loop_unlock(c->context.loop);
+
+ return -res;
+}
+
+SPA_EXPORT
+int jack_port_name_size(void)
+{
+ return REAL_JACK_PORT_NAME_SIZE+1;
+}
+
+SPA_EXPORT
+int jack_port_type_size(void)
+{
+ return JACK_PORT_TYPE_SIZE+1;
+}
+
+SPA_EXPORT
+size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_type)
+{
+ spa_return_val_if_fail(client != NULL, 0);
+ spa_return_val_if_fail(port_type != NULL, 0);
+
+ if (spa_streq(JACK_DEFAULT_AUDIO_TYPE, port_type))
+ return jack_get_buffer_size(client) * sizeof(float);
+ else if (spa_streq(JACK_DEFAULT_MIDI_TYPE, port_type))
+ return MAX_BUFFER_FRAMES * sizeof(float);
+ else if (spa_streq(JACK_DEFAULT_VIDEO_TYPE, port_type))
+ return 320 * 240 * 4 * sizeof(float);
+ else
+ return 0;
+}
+
+SPA_EXPORT
+void jack_port_set_latency (jack_port_t *port, jack_nframes_t frames)
+{
+ struct object *o = (struct object *) port;
+ struct client *c;
+ jack_latency_range_t range = { frames, frames };
+
+ spa_return_if_fail(o != NULL);
+ c = o->client;
+
+ pw_log_debug("%p: %s set latency %d", c, o->port.name, frames);
+
+ if (o->port.flags & JackPortIsOutput) {
+ jack_port_set_latency_range(port, JackCaptureLatency, &range);
+ }
+ if (o->port.flags & JackPortIsInput) {
+ jack_port_set_latency_range(port, JackPlaybackLatency, &range);
+ }
+}
+
+SPA_EXPORT
+void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range)
+{
+ struct object *o = (struct object *) port;
+ struct client *c;
+ jack_nframes_t nframes, rate;
+ int direction;
+ struct spa_latency_info *info;
+
+ spa_return_if_fail(o != NULL);
+ if (o->type != INTERFACE_Port || o->client == NULL)
+ return;
+ c = o->client;
+
+ if (mode == JackCaptureLatency)
+ direction = SPA_DIRECTION_OUTPUT;
+ else
+ direction = SPA_DIRECTION_INPUT;
+
+ nframes = jack_get_buffer_size((jack_client_t*)c);
+ rate = jack_get_sample_rate((jack_client_t*)c);
+ info = &o->port.latency[direction];
+
+ range->min = (info->min_quantum * nframes) +
+ info->min_rate + (info->min_ns * rate) / SPA_NSEC_PER_SEC;
+ range->max = (info->max_quantum * nframes) +
+ info->max_rate + (info->max_ns * rate) / SPA_NSEC_PER_SEC;
+
+ pw_log_debug("%p: %s get %d latency range %d %d", c, o->port.name,
+ mode, range->min, range->max);
+}
+
+static int
+do_port_update_latency(struct spa_loop *loop,
+ bool async, uint32_t seq, const void *data, size_t size, void *user_data)
+{
+ struct port *p = user_data;
+ port_update_latency(p);
+ return 0;
+}
+
+SPA_EXPORT
+void jack_port_set_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range)
+{
+ struct object *o = (struct object *) port;
+ struct client *c;
+ enum spa_direction direction;
+ struct spa_latency_info *current, latency;
+ jack_nframes_t nframes;
+ struct port *p;
+
+ spa_return_if_fail(o != NULL);
+ if (o->type != INTERFACE_Port || o->client == NULL)
+ return;
+ c = o->client;
+
+ if (mode == JackCaptureLatency)
+ direction = SPA_DIRECTION_OUTPUT;
+ else
+ direction = SPA_DIRECTION_INPUT;
+
+ pw_log_info("%p: %s set %d latency range %d %d", c, o->port.name, mode, range->min, range->max);
+
+ latency = SPA_LATENCY_INFO(direction);
+
+ nframes = jack_get_buffer_size((jack_client_t*)c);
+
+ latency.min_rate = range->min;
+ if (latency.min_rate >= nframes) {
+ latency.min_quantum = latency.min_rate / nframes;
+ latency.min_rate %= nframes;
+ }
+
+ latency.max_rate = range->max;
+ if (latency.max_rate >= nframes) {
+ latency.max_quantum = latency.max_rate / nframes;
+ latency.max_rate %= nframes;
+ }
+
+ current = &o->port.latency[direction];
+
+ if ((p = o->port.port) == NULL)
+ return;
+ if (spa_latency_info_compare(current, &latency) == 0)
+ return;
+
+ pw_log_info("%p: %s update %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, c,
+ o->port.name,
+ latency.direction == SPA_DIRECTION_INPUT ? "playback" : "capture",
+ latency.min_quantum, latency.max_quantum,
+ latency.min_rate, latency.max_rate,
+ latency.min_ns, latency.max_ns);
+
+ *current = latency;
+
+ pw_loop_invoke(c->context.l, do_port_update_latency, 0,
+ NULL, 0, false, p);
+}
+
+static int
+do_recompute_latencies(struct spa_loop *loop,
+ bool async, uint32_t seq, const void *data, size_t size, void *user_data)
+{
+ struct client *c = user_data;
+ pw_log_debug("start");
+ recompute_latencies(c);
+ pw_log_debug("stop");
+ return 0;
+}
+
+SPA_EXPORT
+int jack_recompute_total_latencies (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ pw_loop_invoke(c->context.l, do_recompute_latencies, 0,
+ NULL, 0, false, c);
+ return 0;
+}
+
+static jack_nframes_t port_get_latency (jack_port_t *port)
+{
+ struct object *o = (struct object *) port;
+ jack_latency_range_t range = { 0, 0 };
+
+ spa_return_val_if_fail(o != NULL, 0);
+
+ if (o->port.flags & JackPortIsOutput) {
+ jack_port_get_latency_range(port, JackCaptureLatency, &range);
+ }
+ if (o->port.flags & JackPortIsInput) {
+ jack_port_get_latency_range(port, JackPlaybackLatency, &range);
+ }
+ return (range.min + range.max) / 2;
+}
+
+SPA_EXPORT
+jack_nframes_t jack_port_get_latency (jack_port_t *port)
+{
+ return port_get_latency(port);
+}
+
+SPA_EXPORT
+jack_nframes_t jack_port_get_total_latency (jack_client_t *client,
+ jack_port_t *port)
+{
+ return port_get_latency(port);
+}
+
+SPA_EXPORT
+int jack_recompute_total_latency (jack_client_t *client, jack_port_t* port)
+{
+ pw_log_warn("%p: not implemented %p", client, port);
+ return 0;
+}
+
+static int port_compare_func(const void *v1, const void *v2)
+{
+ const struct object *const*o1 = v1, *const*o2 = v2;
+ struct client *c = (*o1)->client;
+ int res;
+ bool is_cap1, is_cap2, is_def1 = false, is_def2 = false;
+
+ is_cap1 = ((*o1)->port.flags & JackPortIsOutput) == JackPortIsOutput &&
+ !(*o1)->port.is_monitor;
+ is_cap2 = ((*o2)->port.flags & JackPortIsOutput) == JackPortIsOutput &&
+ !(*o2)->port.is_monitor;
+
+ if (c->metadata) {
+ struct object *ot1, *ot2;
+
+ ot1 = (*o1)->port.node;
+
+ if (is_cap1)
+ is_def1 = ot1 != NULL && spa_streq(ot1->node.node_name,
+ c->metadata->default_audio_source);
+ else if (!is_cap1)
+ is_def1 = ot1 != NULL && spa_streq(ot1->node.node_name,
+ c->metadata->default_audio_sink);
+ ot2 = (*o2)->port.node;
+
+ if (is_cap2)
+ is_def2 = ot2 != NULL && spa_streq(ot2->node.node_name,
+ c->metadata->default_audio_source);
+ else if (!is_cap2)
+ is_def2 = ot2 != NULL && spa_streq(ot2->node.node_name,
+ c->metadata->default_audio_sink);
+ }
+ if ((*o1)->port.type_id != (*o2)->port.type_id)
+ res = (*o1)->port.type_id - (*o2)->port.type_id;
+ else if ((is_cap1 || is_cap2) && is_cap1 != is_cap2)
+ res = is_cap2 - is_cap1;
+ else if ((is_def1 || is_def2) && is_def1 != is_def2)
+ res = is_def2 - is_def1;
+ else if ((*o1)->port.priority != (*o2)->port.priority)
+ res = (*o2)->port.priority - (*o1)->port.priority;
+ else if ((res = (*o1)->port.node_id - (*o2)->port.node_id) == 0) {
+ if ((*o1)->port.is_monitor != (*o2)->port.is_monitor)
+ res = (*o1)->port.is_monitor - (*o2)->port.is_monitor;
+ if (res == 0)
+ res = (*o1)->port.system_id - (*o2)->port.system_id;
+ if (res == 0)
+ res = (*o1)->serial - (*o2)->serial;
+ }
+ pw_log_debug("port %s<->%s type:%d<->%d def:%d<->%d prio:%d<->%d id:%d<->%d res:%d",
+ (*o1)->port.name, (*o2)->port.name,
+ (*o1)->port.type_id, (*o2)->port.type_id,
+ is_def1, is_def2,
+ (*o1)->port.priority, (*o2)->port.priority,
+ (*o1)->serial, (*o2)->serial, res);
+ return res;
+}
+
+SPA_EXPORT
+const char ** jack_get_ports (jack_client_t *client,
+ const char *port_name_pattern,
+ const char *type_name_pattern,
+ unsigned long flags)
+{
+ struct client *c = (struct client *) client;
+ const char **res;
+ struct object *o;
+ struct pw_array tmp;
+ const char *str;
+ uint32_t i, count;
+ int r;
+ regex_t port_regex, type_regex;
+
+ spa_return_val_if_fail(c != NULL, NULL);
+
+ str = getenv("PIPEWIRE_NODE");
+
+ if (port_name_pattern && port_name_pattern[0]) {
+ if ((r = regcomp(&port_regex, port_name_pattern, REG_EXTENDED | REG_NOSUB)) != 0) {
+ pw_log_error("cant compile regex %s: %d", port_name_pattern, r);
+ return NULL;
+ }
+ }
+ if (type_name_pattern && type_name_pattern[0]) {
+ if ((r = regcomp(&type_regex, type_name_pattern, REG_EXTENDED | REG_NOSUB)) != 0) {
+ pw_log_error("cant compile regex %s: %d", type_name_pattern, r);
+ return NULL;
+ }
+ }
+
+ pw_log_debug("%p: ports target:%s name:\"%s\" type:\"%s\" flags:%08lx", c, str,
+ port_name_pattern, type_name_pattern, flags);
+
+ pthread_mutex_lock(&c->context.lock);
+ pw_array_init(&tmp, sizeof(void*) * 32);
+ count = 0;
+
+ spa_list_for_each(o, &c->context.objects, link) {
+ if (o->type != INTERFACE_Port || o->removed)
+ continue;
+ pw_log_debug("%p: check port type:%d flags:%08lx name:\"%s\"", c,
+ o->port.type_id, o->port.flags, o->port.name);
+ if (o->port.type_id > TYPE_ID_VIDEO)
+ continue;
+ if (!SPA_FLAG_IS_SET(o->port.flags, flags))
+ continue;
+ if (str != NULL && o->port.node != NULL) {
+ if (!spa_strstartswith(o->port.name, str) &&
+ o->port.node->serial != atoll(str))
+ continue;
+ }
+
+ if (port_name_pattern && port_name_pattern[0]) {
+ bool match;
+ match = regexec(&port_regex, o->port.name, 0, NULL, 0) == 0;
+ if (!match && is_port_default(c, o))
+ match = regexec(&port_regex, o->port.system, 0, NULL, 0) == 0;
+ if (!match)
+ continue;
+ }
+ if (type_name_pattern && type_name_pattern[0]) {
+ if (regexec(&type_regex, type_to_string(o->port.type_id),
+ 0, NULL, 0) == REG_NOMATCH)
+ continue;
+ }
+ pw_log_debug("%p: port \"%s\" prio:%d matches (%d)",
+ c, o->port.name, o->port.priority, count);
+
+ pw_array_add_ptr(&tmp, o);
+ count++;
+ }
+ pthread_mutex_unlock(&c->context.lock);
+
+ if (count > 0) {
+ qsort(tmp.data, count, sizeof(struct object *), port_compare_func);
+ pw_array_add_ptr(&tmp, NULL);
+ res = tmp.data;
+ for (i = 0; i < count; i++)
+ res[i] = port_name((struct object*)res[i]);
+ } else {
+ pw_array_clear(&tmp);
+ res = NULL;
+ }
+
+ if (port_name_pattern && port_name_pattern[0])
+ regfree(&port_regex);
+ if (type_name_pattern && type_name_pattern[0])
+ regfree(&type_regex);
+
+ return res;
+}
+
+SPA_EXPORT
+jack_port_t * jack_port_by_name (jack_client_t *client, const char *port_name)
+{
+ struct client *c = (struct client *) client;
+ struct object *res;
+
+ spa_return_val_if_fail(c != NULL, NULL);
+
+ pthread_mutex_lock(&c->context.lock);
+ res = find_port_by_name(c, port_name);
+ pthread_mutex_unlock(&c->context.lock);
+
+ if (res == NULL)
+ pw_log_info("%p: port \"%s\" not found", c, port_name);
+
+ return (jack_port_t *)res;
+}
+
+SPA_EXPORT
+jack_port_t * jack_port_by_id (jack_client_t *client,
+ jack_port_id_t port_id)
+{
+ struct client *c = (struct client *) client;
+ struct object *res = NULL;
+
+ spa_return_val_if_fail(c != NULL, NULL);
+
+ pthread_mutex_lock(&c->context.lock);
+ res = find_by_serial(c, port_id);
+ if (res && res->type != INTERFACE_Port)
+ res = NULL;
+ pw_log_debug("%p: port %d -> %p", c, port_id, res);
+ pthread_mutex_unlock(&c->context.lock);
+
+ if (res == NULL)
+ pw_log_info("%p: port %d not found", c, port_id);
+
+ return (jack_port_t *)res;
+}
+
+SPA_EXPORT
+jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ struct spa_io_position *pos;
+ struct timespec ts;
+ uint64_t diff;
+
+ spa_return_val_if_fail(c != NULL, 0);
+
+ if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
+ return 0;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ diff = SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec;
+ return (jack_nframes_t) floor(((double)c->sample_rate * diff) / SPA_NSEC_PER_SEC);
+}
+
+SPA_EXPORT
+jack_nframes_t jack_frame_time (const jack_client_t *client)
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return jack_time_to_frames(client, SPA_TIMESPEC_TO_USEC(&ts));
+}
+
+SPA_EXPORT
+jack_nframes_t jack_last_frame_time (const jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ struct spa_io_position *pos;
+
+ spa_return_val_if_fail(c != NULL, 0);
+
+ if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
+ return 0;
+
+ return pos->clock.position;
+}
+
+SPA_EXPORT
+int jack_get_cycle_times(const jack_client_t *client,
+ jack_nframes_t *current_frames,
+ jack_time_t *current_usecs,
+ jack_time_t *next_usecs,
+ float *period_usecs)
+{
+ struct client *c = (struct client *) client;
+ struct spa_io_position *pos;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
+ return -EIO;
+
+ *current_frames = pos->clock.position;
+ *current_usecs = pos->clock.nsec / SPA_NSEC_PER_USEC;
+ *period_usecs = pos->clock.duration * (float)SPA_USEC_PER_SEC / (c->sample_rate * pos->clock.rate_diff);
+ *next_usecs = pos->clock.next_nsec / SPA_NSEC_PER_USEC;
+
+ pw_log_trace("%p: %d %"PRIu64" %"PRIu64" %f", c, *current_frames,
+ *current_usecs, *next_usecs, *period_usecs);
+ return 0;
+}
+
+SPA_EXPORT
+jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames)
+{
+ struct client *c = (struct client *) client;
+ struct spa_io_position *pos;
+ double df;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
+ return 0;
+
+ df = (frames - pos->clock.position) * (double)SPA_NSEC_PER_SEC / c->sample_rate;
+ return (pos->clock.nsec + (int64_t)rint(df)) / SPA_NSEC_PER_USEC;
+}
+
+SPA_EXPORT
+jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usecs)
+{
+ struct client *c = (struct client *) client;
+ struct spa_io_position *pos;
+ double du;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
+ return 0;
+
+ du = (usecs - pos->clock.nsec/SPA_NSEC_PER_USEC) * (double)c->sample_rate / SPA_USEC_PER_SEC;
+ return pos->clock.position + (int32_t)rint(du);
+}
+
+SPA_EXPORT
+jack_time_t jack_get_time(void)
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return SPA_TIMESPEC_TO_USEC(&ts);
+}
+
+SPA_EXPORT
+void default_jack_error_callback(const char *desc)
+{
+ pw_log_error("pw jack error: %s",desc);
+}
+
+SPA_EXPORT
+void silent_jack_error_callback(const char *desc)
+{
+}
+
+SPA_EXPORT
+void (*jack_error_callback)(const char *msg);
+
+SPA_EXPORT
+void jack_set_error_function (void (*func)(const char *))
+{
+ jack_error_callback = (func == NULL) ? &default_jack_error_callback : func;
+}
+
+SPA_EXPORT
+void default_jack_info_callback(const char *desc)
+{
+ pw_log_info("pw jack info: %s", desc);
+}
+
+SPA_EXPORT
+void silent_jack_info_callback(const char *desc)
+{
+}
+
+SPA_EXPORT
+void (*jack_info_callback)(const char *msg);
+
+
+SPA_EXPORT
+void jack_set_info_function (void (*func)(const char *))
+{
+ jack_info_callback = (func == NULL) ? &default_jack_info_callback : func;
+}
+
+SPA_EXPORT
+void jack_free(void* ptr)
+{
+ free(ptr);
+}
+
+SPA_EXPORT
+int jack_release_timebase (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ struct pw_node_activation *a;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if ((a = c->driver_activation) == NULL)
+ return -EIO;
+
+ if (!ATOMIC_CAS(a->segment_owner[0], c->node_id, 0))
+ return -EINVAL;
+
+ c->timebase_callback = NULL;
+ c->timebase_arg = NULL;
+ c->activation->pending_new_pos = false;
+
+ return 0;
+}
+
+SPA_EXPORT
+int jack_set_sync_callback (jack_client_t *client,
+ JackSyncCallback sync_callback,
+ void *arg)
+{
+ int res = 0;
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ pw_thread_loop_lock(c->context.loop);
+
+ c->sync_callback = sync_callback;
+ c->sync_arg = arg;
+
+ if ((res = do_activate(c)) < 0)
+ goto done;
+
+ c->activation->pending_sync = true;
+done:
+ pw_thread_loop_unlock(c->context.loop);
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_set_sync_timeout (jack_client_t *client,
+ jack_time_t timeout)
+{
+ int res = 0;
+ struct client *c = (struct client *) client;
+ struct pw_node_activation *a;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ pw_thread_loop_lock(c->context.loop);
+
+ if ((a = c->activation) == NULL)
+ res = -EIO;
+ else
+ a->sync_timeout = timeout;
+ pw_thread_loop_unlock(c->context.loop);
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_set_timebase_callback (jack_client_t *client,
+ int conditional,
+ JackTimebaseCallback timebase_callback,
+ void *arg)
+{
+ int res = 0;
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+ spa_return_val_if_fail(timebase_callback != NULL, -EINVAL);
+
+ pw_thread_loop_lock(c->context.loop);
+
+ c->timebase_callback = timebase_callback;
+ c->timebase_arg = arg;
+ c->timeowner_conditional = conditional;
+ install_timeowner(c);
+
+ pw_log_debug("%p: timebase set id:%u", c, c->node_id);
+
+ if ((res = do_activate(c)) < 0)
+ goto done;
+
+ c->activation->pending_new_pos = true;
+done:
+ pw_thread_loop_unlock(c->context.loop);
+
+ return res;
+}
+
+SPA_EXPORT
+int jack_transport_locate (jack_client_t *client,
+ jack_nframes_t frame)
+{
+ jack_position_t pos;
+ pos.frame = frame;
+ pos.valid = (jack_position_bits_t)0;
+ return jack_transport_reposition(client, &pos);
+}
+
+SPA_EXPORT
+jack_transport_state_t jack_transport_query (const jack_client_t *client,
+ jack_position_t *pos)
+{
+ struct client *c = (struct client *) client;
+ struct pw_node_activation *a;
+ jack_transport_state_t jack_state = JackTransportStopped;
+
+ spa_return_val_if_fail(c != NULL, JackTransportStopped);
+
+ if (SPA_LIKELY((a = c->rt.driver_activation) != NULL)) {
+ jack_state = position_to_jack(a, pos);
+ } else if ((a = c->driver_activation) != NULL) {
+ jack_state = position_to_jack(a, pos);
+ } else if (pos != NULL) {
+ memset(pos, 0, sizeof(jack_position_t));
+ pos->frame_rate = jack_get_sample_rate((jack_client_t*)client);
+ }
+ return jack_state;
+}
+
+SPA_EXPORT
+jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ struct pw_node_activation *a;
+ struct spa_io_position *pos;
+ struct spa_io_segment *seg;
+ uint64_t running;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (SPA_UNLIKELY((a = c->rt.driver_activation) == NULL))
+ return -EIO;
+
+ pos = &a->position;
+ running = pos->clock.position - pos->offset;
+
+ if (pos->state == SPA_IO_POSITION_STATE_RUNNING) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ uint64_t nsecs = SPA_TIMESPEC_TO_NSEC(&ts) - pos->clock.nsec;
+ running += (uint64_t)floor((((double) c->sample_rate) / SPA_NSEC_PER_SEC) * nsecs);
+ }
+ seg = &pos->segments[0];
+
+ return (running - seg->start) * seg->rate + seg->position;
+}
+
+SPA_EXPORT
+int jack_transport_reposition (jack_client_t *client,
+ const jack_position_t *pos)
+{
+ struct client *c = (struct client *) client;
+ struct pw_node_activation *a, *na;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ a = c->rt.driver_activation;
+ na = c->activation;
+ if (!a || !na)
+ return -EIO;
+
+ if (pos->valid & ~(JackPositionBBT|JackPositionTimecode))
+ return -EINVAL;
+
+ pw_log_debug("frame:%u", pos->frame);
+ spa_zero(na->reposition);
+ na->reposition.flags = 0;
+ na->reposition.start = 0;
+ na->reposition.duration = 0;
+ na->reposition.position = pos->frame;
+ na->reposition.rate = 1.0;
+ ATOMIC_STORE(a->reposition_owner, c->node_id);
+
+ return 0;
+}
+
+static void update_command(struct client *c, uint32_t command)
+{
+ struct pw_node_activation *a = c->rt.driver_activation;
+ if (!a)
+ return;
+ ATOMIC_STORE(a->command, command);
+}
+
+SPA_EXPORT
+void jack_transport_start (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ spa_return_if_fail(c != NULL);
+ update_command(c, PW_NODE_ACTIVATION_COMMAND_START);
+}
+
+SPA_EXPORT
+void jack_transport_stop (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ spa_return_if_fail(c != NULL);
+ update_command(c, PW_NODE_ACTIVATION_COMMAND_STOP);
+}
+
+SPA_EXPORT
+void jack_get_transport_info (jack_client_t *client,
+ jack_transport_info_t *tinfo)
+{
+ pw_log_error("%p: deprecated", client);
+ if (tinfo)
+ memset(tinfo, 0, sizeof(jack_transport_info_t));
+}
+
+SPA_EXPORT
+void jack_set_transport_info (jack_client_t *client,
+ jack_transport_info_t *tinfo)
+{
+ pw_log_error("%p: deprecated", client);
+ if (tinfo)
+ memset(tinfo, 0, sizeof(jack_transport_info_t));
+}
+
+SPA_EXPORT
+int jack_set_session_callback (jack_client_t *client,
+ JackSessionCallback session_callback,
+ void *arg)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, -EINVAL);
+
+ if (c->active) {
+ pw_log_error("%p: can't set callback on active client", c);
+ return -EIO;
+ }
+ pw_log_warn("%p: not implemented", client);
+ return -ENOTSUP;
+}
+
+SPA_EXPORT
+int jack_session_reply (jack_client_t *client,
+ jack_session_event_t *event)
+{
+ pw_log_warn("%p: not implemented", client);
+ return -ENOTSUP;
+}
+
+
+SPA_EXPORT
+void jack_session_event_free (jack_session_event_t *event)
+{
+ if (event) {
+ free((void *)event->session_dir);
+ free((void *)event->client_uuid);
+ free(event->command_line);
+ free(event);
+ }
+}
+
+SPA_EXPORT
+char *jack_client_get_uuid (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_val_if_fail(c != NULL, NULL);
+
+ return spa_aprintf("%"PRIu64, client_make_uuid(c->serial, false));
+}
+
+SPA_EXPORT
+jack_session_command_t *jack_session_notify (
+ jack_client_t* client,
+ const char *target,
+ jack_session_event_type_t type,
+ const char *path)
+{
+ struct client *c = (struct client *) client;
+ jack_session_command_t *cmds;
+ spa_return_val_if_fail(c != NULL, NULL);
+ pw_log_warn("not implemented");
+ cmds = calloc(1, sizeof(jack_session_command_t));
+ return cmds;
+}
+
+SPA_EXPORT
+void jack_session_commands_free (jack_session_command_t *cmds)
+{
+ int i;
+ if (cmds == NULL)
+ return;
+
+ for (i = 0; cmds[i].uuid != NULL; i++) {
+ free((char*)cmds[i].client_name);
+ free((char*)cmds[i].command);
+ free((char*)cmds[i].uuid);
+ }
+ free(cmds);
+}
+
+SPA_EXPORT
+int jack_reserve_client_name (jack_client_t *client,
+ const char *name,
+ const char *uuid)
+{
+ struct client *c = (struct client *) client;
+ spa_return_val_if_fail(c != NULL, -1);
+ pw_log_warn("not implemented");
+ return 0;
+}
+
+SPA_EXPORT
+int jack_client_has_session_callback (jack_client_t *client, const char *client_name)
+{
+ struct client *c = (struct client *) client;
+ spa_return_val_if_fail(c != NULL, -1);
+ return 0;
+}
+
+
+SPA_EXPORT
+int jack_client_real_time_priority (jack_client_t * client)
+{
+ return jack_client_max_real_time_priority(client) - 5;
+}
+
+SPA_EXPORT
+int jack_client_max_real_time_priority (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ int min, max;
+
+ spa_return_val_if_fail(c != NULL, -1);
+
+ spa_thread_utils_get_rt_range(&c->context.thread_utils, NULL, &min, &max);
+ return SPA_MIN(max, c->rt_max) - 1;
+}
+
+SPA_EXPORT
+int jack_acquire_real_time_scheduling (jack_native_thread_t thread, int priority)
+{
+ struct spa_thread *t = (struct spa_thread*)thread;
+ pw_log_info("acquire %p", t);
+ spa_return_val_if_fail(globals.thread_utils != NULL, -1);
+ spa_return_val_if_fail(t != NULL, -1);
+ return spa_thread_utils_acquire_rt(globals.thread_utils, t, priority);
+}
+
+SPA_EXPORT
+int jack_drop_real_time_scheduling (jack_native_thread_t thread)
+{
+ struct spa_thread *t = (struct spa_thread*)thread;
+ pw_log_info("drop %p", t);
+ spa_return_val_if_fail(globals.thread_utils != NULL, -1);
+ spa_return_val_if_fail(t != NULL, -1);
+ return spa_thread_utils_drop_rt(globals.thread_utils, t);
+}
+
+/**
+ * Create a thread for JACK or one of its clients. The thread is
+ * created executing @a start_routine with @a arg as its sole
+ * argument.
+ *
+ * @param client the JACK client for whom the thread is being created. May be
+ * NULL if the client is being created within the JACK server.
+ * @param thread place to return POSIX thread ID.
+ * @param priority thread priority, if realtime.
+ * @param realtime true for the thread to use realtime scheduling. On
+ * some systems that may require special privileges.
+ * @param start_routine function the thread calls when it starts.
+ * @param arg parameter passed to the @a start_routine.
+ *
+ * @returns 0, if successful; otherwise some error number.
+ */
+SPA_EXPORT
+int jack_client_create_thread (jack_client_t* client,
+ jack_native_thread_t *thread,
+ int priority,
+ int realtime, /* boolean */
+ void *(*start_routine)(void*),
+ void *arg)
+{
+ struct client *c = (struct client *) client;
+ int res = 0;
+ struct spa_thread *thr;
+
+ spa_return_val_if_fail(client != NULL, -EINVAL);
+ spa_return_val_if_fail(thread != NULL, -EINVAL);
+ spa_return_val_if_fail(start_routine != NULL, -EINVAL);
+
+ pw_log_info("client %p: create thread rt:%d prio:%d", client, realtime, priority);
+
+ thr = spa_thread_utils_create(&c->context.thread_utils, NULL, start_routine, arg);
+ if (thr == NULL)
+ res = -errno;
+ *thread = (pthread_t)thr;
+
+ if (res != 0) {
+ pw_log_warn("client %p: create RT thread failed: %s",
+ client, strerror(res));
+ } else if (realtime) {
+ /* Try to acquire RT scheduling, we don't fail here but the
+ * function will emit a warning. Real JACK fails here. */
+ jack_acquire_real_time_scheduling(*thread, priority);
+ }
+ return res;
+}
+
+SPA_EXPORT
+int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread)
+{
+ struct client *c = (struct client *) client;
+ void* status;
+
+ if (thread == (jack_native_thread_t)NULL)
+ return -EINVAL;
+
+ spa_return_val_if_fail(client != NULL, -EINVAL);
+
+ pw_log_debug("join thread %lu", thread);
+ spa_thread_utils_join(&c->context.thread_utils, (struct spa_thread*)thread, &status);
+ pw_log_debug("stopped thread %lu", thread);
+ return 0;
+}
+
+SPA_EXPORT
+int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread)
+{
+ struct client *c = (struct client *) client;
+ void* status;
+
+ if (thread == (jack_native_thread_t)NULL)
+ return -EINVAL;
+
+ spa_return_val_if_fail(client != NULL, -EINVAL);
+
+ pw_log_debug("cancel thread %lu", thread);
+ pthread_cancel(thread);
+ pw_log_debug("join thread %lu", thread);
+ spa_thread_utils_join(&c->context.thread_utils, (struct spa_thread*)thread, &status);
+ pw_log_debug("stopped thread %lu", thread);
+ return 0;
+}
+
+SPA_EXPORT
+void jack_set_thread_creator (jack_thread_creator_t creator)
+{
+ globals.creator = creator;
+}
+
+static inline uint8_t * midi_event_data (void* port_buffer,
+ const struct midi_event* event)
+{
+ if (SPA_LIKELY(event->size <= MIDI_INLINE_MAX))
+ return (uint8_t *)event->inline_data;
+ else
+ return SPA_PTROFF(port_buffer, event->byte_offset, uint8_t);
+}
+
+SPA_EXPORT
+uint32_t jack_midi_get_event_count(void* port_buffer)
+{
+ struct midi_buffer *mb = port_buffer;
+ if (mb == NULL || mb->magic != MIDI_BUFFER_MAGIC)
+ return 0;
+ return mb->event_count;
+}
+
+SPA_EXPORT
+int jack_midi_event_get(jack_midi_event_t *event,
+ void *port_buffer,
+ uint32_t event_index)
+{
+ struct midi_buffer *mb = port_buffer;
+ struct midi_event *ev = SPA_PTROFF(mb, sizeof(*mb), struct midi_event);
+ spa_return_val_if_fail(mb != NULL, -EINVAL);
+ spa_return_val_if_fail(ev != NULL, -EINVAL);
+ if (event_index >= mb->event_count)
+ return -ENOBUFS;
+ ev += event_index;
+ event->time = ev->time;
+ event->size = ev->size;
+ event->buffer = midi_event_data (port_buffer, ev);
+ return 0;
+}
+
+SPA_EXPORT
+void jack_midi_clear_buffer(void *port_buffer)
+{
+ struct midi_buffer *mb = port_buffer;
+ spa_return_if_fail(mb != NULL);
+ mb->event_count = 0;
+ mb->write_pos = 0;
+ mb->lost_events = 0;
+}
+
+SPA_EXPORT
+void jack_midi_reset_buffer(void *port_buffer)
+{
+ jack_midi_clear_buffer(port_buffer);
+}
+
+SPA_EXPORT
+size_t jack_midi_max_event_size(void* port_buffer)
+{
+ struct midi_buffer *mb = port_buffer;
+ size_t buffer_size;
+
+ spa_return_val_if_fail(mb != NULL, 0);
+
+ buffer_size = mb->buffer_size;
+
+ /* (event_count + 1) below accounts for jack_midi_port_internal_event_t
+ * which would be needed to store the next event */
+ size_t used_size = sizeof(struct midi_buffer)
+ + mb->write_pos
+ + ((mb->event_count + 1)
+ * sizeof(struct midi_event));
+
+ if (SPA_UNLIKELY(used_size > buffer_size)) {
+ return 0;
+ } else if (SPA_LIKELY((buffer_size - used_size) < MIDI_INLINE_MAX)) {
+ return MIDI_INLINE_MAX;
+ } else {
+ return buffer_size - used_size;
+ }
+}
+
+SPA_EXPORT
+jack_midi_data_t* jack_midi_event_reserve(void *port_buffer,
+ jack_nframes_t time,
+ size_t data_size)
+{
+ struct midi_buffer *mb = port_buffer;
+ struct midi_event *events = SPA_PTROFF(mb, sizeof(*mb), struct midi_event);
+ size_t buffer_size;
+
+ spa_return_val_if_fail(mb != NULL, NULL);
+
+ buffer_size = mb->buffer_size;
+
+ if (SPA_UNLIKELY(time >= mb->nframes)) {
+ pw_log_warn("midi %p: time:%d frames:%d", port_buffer, time, mb->nframes);
+ goto failed;
+ }
+
+ if (SPA_UNLIKELY(mb->event_count > 0 && time < events[mb->event_count - 1].time)) {
+ pw_log_warn("midi %p: time:%d ev:%d", port_buffer, time, mb->event_count);
+ goto failed;
+ }
+
+ /* Check if data_size is >0 and there is enough space in the buffer for the event. */
+ if (SPA_UNLIKELY(data_size <= 0)) {
+ pw_log_warn("midi %p: data_size:%zd", port_buffer, data_size);
+ goto failed; // return NULL?
+ } else if (SPA_UNLIKELY(jack_midi_max_event_size (port_buffer) < data_size)) {
+ pw_log_warn("midi %p: event too large: data_size:%zd", port_buffer, data_size);
+ goto failed;
+ } else {
+ struct midi_event *ev = &events[mb->event_count];
+ uint8_t *res;
+
+ ev->time = time;
+ ev->size = data_size;
+ if (SPA_LIKELY(data_size <= MIDI_INLINE_MAX)) {
+ res = ev->inline_data;
+ } else {
+ mb->write_pos += data_size;
+ ev->byte_offset = buffer_size - 1 - mb->write_pos;
+ res = SPA_PTROFF(mb, ev->byte_offset, uint8_t);
+ }
+ mb->event_count += 1;
+ return res;
+ }
+failed:
+ mb->lost_events++;
+ return NULL;
+}
+
+SPA_EXPORT
+int jack_midi_event_write(void *port_buffer,
+ jack_nframes_t time,
+ const jack_midi_data_t *data,
+ size_t data_size)
+{
+ jack_midi_data_t *retbuf = jack_midi_event_reserve (port_buffer, time, data_size);
+ if (SPA_UNLIKELY(retbuf == NULL))
+ return -ENOBUFS;
+ memcpy (retbuf, data, data_size);
+ return 0;
+}
+
+SPA_EXPORT
+uint32_t jack_midi_get_lost_event_count(void *port_buffer)
+{
+ struct midi_buffer *mb = port_buffer;
+ spa_return_val_if_fail(mb != NULL, 0);
+ return mb->lost_events;
+}
+
+/** extensions */
+
+SPA_EXPORT
+int jack_get_video_image_size(jack_client_t *client, jack_image_size_t *size)
+{
+ struct client *c = (struct client *) client;
+ struct pw_node_activation *a;
+
+ spa_return_val_if_fail(c != NULL, 0);
+
+ a = c->rt.driver_activation;
+ if (SPA_UNLIKELY(a == NULL))
+ a = c->activation;
+ if (SPA_UNLIKELY(a == NULL))
+ return -EIO;
+
+ if (SPA_UNLIKELY(!(a->position.video.flags & SPA_IO_VIDEO_SIZE_VALID)))
+ return -EIO;
+
+ size->width = a->position.video.size.width;
+ size->height = a->position.video.size.height;
+ size->stride = a->position.video.stride;
+ size->flags = 0;
+ return size->stride * size->height;
+}
+
+
+static void reg(void) __attribute__ ((constructor));
+static void reg(void)
+{
+ pw_init(NULL, NULL);
+ PW_LOG_TOPIC_INIT(jack_log_topic);
+ pthread_mutex_init(&globals.lock, NULL);
+ pw_array_init(&globals.descriptions, 16);
+ spa_list_init(&globals.free_objects);
+}
diff --git a/pipewire-jack/src/pw-jack.in b/pipewire-jack/src/pw-jack.in
new file mode 100755
index 0000000..f3dba1d
--- /dev/null
+++ b/pipewire-jack/src/pw-jack.in
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+# This file is part of PipeWire.
+#
+# Copyright © 2020 Wim Taymans
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+
+DEFAULT_SAMPLERATE=48000
+
+while getopts 'hr:vs:p:' param ; do
+ case $param in
+ r)
+ PIPEWIRE_REMOTE="$OPTARG"
+ export PIPEWIRE_REMOTE
+ ;;
+ v)
+ if [ -z "$PIPEWIRE_DEBUG" ]; then
+ PIPEWIRE_DEBUG=3
+ else
+ PIPEWIRE_DEBUG=$(( PIPEWIRE_DEBUG + 1 ))
+ fi
+ export PIPEWIRE_DEBUG
+ ;;
+ s)
+ SAMPLERATE="$OPTARG"
+ ;;
+ p)
+ PERIOD="$OPTARG"
+ ;;
+ *)
+ echo "$0 - run JACK applications on PipeWire"
+ echo " "
+ echo "$0 [options] application [arguments]"
+ echo " "
+ echo "options:"
+ echo " -h show brief help"
+ echo " -r <remote> remote daemon name"
+ echo " -v verbose debug info"
+ echo " -s samplerate (default \"$DEFAULT_SAMPLERATE\")"
+ echo " -p period in samples"
+ exit 0
+ ;;
+ esac
+done
+
+shift $(( OPTIND - 1 ))
+
+if [ -n "$PERIOD" ]; then
+ if [ -n "$SAMPLERATE" ]; then
+ PIPEWIRE_QUANTUM="$PERIOD/$SAMPLERATE"
+ else
+ PIPEWIRE_QUANTUM="$PERIOD/$DEFAULT_SAMPLERATE"
+ fi
+ export PIPEWIRE_QUANTUM
+fi
+LD_LIBRARY_PATH='@LIBJACK_PATH@'"${LD_LIBRARY_PATH+":$LD_LIBRARY_PATH"}"
+export LD_LIBRARY_PATH
+
+exec "$@"
diff --git a/pipewire-jack/src/ringbuffer.c b/pipewire-jack/src/ringbuffer.c
new file mode 100644
index 0000000..887c7dc
--- /dev/null
+++ b/pipewire-jack/src/ringbuffer.c
@@ -0,0 +1,302 @@
+/* PipeWire
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <spa/utils/defs.h>
+
+#include <jack/ringbuffer.h>
+
+SPA_EXPORT
+jack_ringbuffer_t *jack_ringbuffer_create(size_t sz)
+{
+ size_t power_of_two;
+ jack_ringbuffer_t *rb;
+
+ rb = calloc(1, sizeof(jack_ringbuffer_t));
+ if (rb == NULL)
+ return NULL;
+
+ for (power_of_two = 1; 1u << power_of_two < sz; power_of_two++);
+
+ rb->size = 1 << power_of_two;
+ rb->size_mask = rb->size - 1;
+ if ((rb->buf = calloc(1, rb->size)) == NULL) {
+ free (rb);
+ return NULL;
+ }
+ rb->mlocked = 0;
+
+ return rb;
+}
+
+SPA_EXPORT
+void jack_ringbuffer_free(jack_ringbuffer_t *rb)
+{
+#ifdef USE_MLOCK
+ if (rb->mlocked)
+ munlock (rb->buf, rb->size);
+#endif /* USE_MLOCK */
+ free (rb->buf);
+ free (rb);
+}
+
+SPA_EXPORT
+void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb,
+ jack_ringbuffer_data_t *vec)
+{
+ size_t free_cnt;
+ size_t cnt2;
+ size_t w, r;
+
+ w = rb->write_ptr;
+ r = rb->read_ptr;
+
+ if (w > r)
+ free_cnt = w - r;
+ else
+ free_cnt = (w - r + rb->size) & rb->size_mask;
+
+ cnt2 = r + free_cnt;
+
+ if (cnt2 > rb->size) {
+ vec[0].buf = &(rb->buf[r]);
+ vec[0].len = rb->size - r;
+ vec[1].buf = rb->buf;
+ vec[1].len = cnt2 & rb->size_mask;
+ } else {
+ vec[0].buf = &(rb->buf[r]);
+ vec[0].len = free_cnt;
+ vec[1].len = 0;
+ }
+}
+
+SPA_EXPORT
+void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb,
+ jack_ringbuffer_data_t *vec)
+{
+ size_t free_cnt;
+ size_t cnt2;
+ size_t w, r;
+
+ w = rb->write_ptr;
+ r = rb->read_ptr;
+
+ if (w > r)
+ free_cnt = ((r - w + rb->size) & rb->size_mask) - 1;
+ else if (w < r)
+ free_cnt = (r - w) - 1;
+ else
+ free_cnt = rb->size - 1;
+
+ cnt2 = w + free_cnt;
+
+ if (cnt2 > rb->size) {
+ vec[0].buf = &(rb->buf[w]);
+ vec[0].len = rb->size - w;
+ vec[1].buf = rb->buf;
+ vec[1].len = cnt2 & rb->size_mask;
+ } else {
+ vec[0].buf = &(rb->buf[w]);
+ vec[0].len = free_cnt;
+ vec[1].len = 0;
+ }
+}
+
+SPA_EXPORT
+size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt)
+{
+ size_t free_cnt;
+ size_t cnt2;
+ size_t to_read;
+ size_t n1, n2;
+
+ if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0)
+ return 0;
+
+ to_read = cnt > free_cnt ? free_cnt : cnt;
+
+ cnt2 = rb->read_ptr + to_read;
+
+ if (cnt2 > rb->size) {
+ n1 = rb->size - rb->read_ptr;
+ n2 = cnt2 & rb->size_mask;
+ } else {
+ n1 = to_read;
+ n2 = 0;
+ }
+
+ memcpy (dest, &(rb->buf[rb->read_ptr]), n1);
+ rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask;
+ if (n2) {
+ memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2);
+ rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask;
+ }
+ return to_read;
+}
+
+SPA_EXPORT
+size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, char *dest, size_t cnt)
+{
+ size_t free_cnt;
+ size_t cnt2;
+ size_t to_read;
+ size_t n1, n2;
+ size_t tmp_read_ptr;
+
+ tmp_read_ptr = rb->read_ptr;
+
+ if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0)
+ return 0;
+
+ to_read = cnt > free_cnt ? free_cnt : cnt;
+
+ cnt2 = tmp_read_ptr + to_read;
+
+ if (cnt2 > rb->size) {
+ n1 = rb->size - tmp_read_ptr;
+ n2 = cnt2 & rb->size_mask;
+ } else {
+ n1 = to_read;
+ n2 = 0;
+ }
+
+ memcpy (dest, &(rb->buf[tmp_read_ptr]), n1);
+ tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask;
+
+ if (n2)
+ memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2);
+
+ return to_read;
+}
+
+SPA_EXPORT
+void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt)
+{
+ size_t tmp = (rb->read_ptr + cnt) & rb->size_mask;
+ rb->read_ptr = tmp;
+}
+
+SPA_EXPORT
+size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb)
+{
+ size_t w, r;
+
+ w = rb->write_ptr;
+ r = rb->read_ptr;
+
+ if (w > r)
+ return w - r;
+ else
+ return (w - r + rb->size) & rb->size_mask;
+}
+
+SPA_EXPORT
+int jack_ringbuffer_mlock(jack_ringbuffer_t *rb)
+{
+#ifdef USE_MLOCK
+ if (mlock (rb->buf, rb->size))
+ return -1;
+#endif /* USE_MLOCK */
+ rb->mlocked = 1;
+ return 0;
+}
+
+SPA_EXPORT
+void jack_ringbuffer_reset(jack_ringbuffer_t *rb)
+{
+ rb->read_ptr = 0;
+ rb->write_ptr = 0;
+ memset(rb->buf, 0, rb->size);
+}
+
+SPA_EXPORT
+void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz)
+{
+ rb->size = sz;
+ rb->size_mask = rb->size - 1;
+ rb->read_ptr = 0;
+ rb->write_ptr = 0;
+}
+
+SPA_EXPORT
+size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const char *src,
+ size_t cnt)
+{
+ size_t free_cnt;
+ size_t cnt2;
+ size_t to_write;
+ size_t n1, n2;
+
+ if ((free_cnt = jack_ringbuffer_write_space (rb)) == 0)
+ return 0;
+
+ to_write = cnt > free_cnt ? free_cnt : cnt;
+
+ cnt2 = rb->write_ptr + to_write;
+
+ if (cnt2 > rb->size) {
+ n1 = rb->size - rb->write_ptr;
+ n2 = cnt2 & rb->size_mask;
+ } else {
+ n1 = to_write;
+ n2 = 0;
+ }
+
+ memcpy (&(rb->buf[rb->write_ptr]), src, n1);
+ rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask;
+ if (n2) {
+ memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2);
+ rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask;
+ }
+ return to_write;
+}
+
+SPA_EXPORT
+void jack_ringbuffer_write_advance(jack_ringbuffer_t *rb, size_t cnt)
+{
+ size_t tmp = (rb->write_ptr + cnt) & rb->size_mask;
+ rb->write_ptr = tmp;
+}
+
+SPA_EXPORT
+size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb)
+{
+ size_t w, r;
+
+ w = rb->write_ptr;
+ r = rb->read_ptr;
+
+ if (w > r)
+ return ((r - w + rb->size) & rb->size_mask) - 1;
+ else if (w < r)
+ return (r - w) - 1;
+ else
+ return rb->size - 1;
+}
diff --git a/pipewire-jack/src/statistics.c b/pipewire-jack/src/statistics.c
new file mode 100644
index 0000000..25ffb3e
--- /dev/null
+++ b/pipewire-jack/src/statistics.c
@@ -0,0 +1,66 @@
+/* PipeWire
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <jack/statistics.h>
+
+SPA_EXPORT
+float jack_get_max_delayed_usecs (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ float res = 0.0f;
+
+ spa_return_val_if_fail(c != NULL, 0.0);
+
+ if (c->driver_activation)
+ res = (float)c->driver_activation->max_delay / SPA_USEC_PER_SEC;
+
+ pw_log_trace("%p: max delay %f", client, res);
+ return res;
+}
+
+SPA_EXPORT
+float jack_get_xrun_delayed_usecs (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+ float res = 0.0f;
+
+ spa_return_val_if_fail(c != NULL, 0.0);
+
+ if (c->driver_activation)
+ res = (float)c->driver_activation->xrun_delay / SPA_USEC_PER_SEC;
+
+ pw_log_trace("%p: xrun delay %f", client, res);
+ return res;
+}
+
+SPA_EXPORT
+void jack_reset_max_delayed_usecs (jack_client_t *client)
+{
+ struct client *c = (struct client *) client;
+
+ spa_return_if_fail(c != NULL);
+
+ if (c->driver_activation)
+ c->driver_activation->max_delay = 0;
+}
diff --git a/pipewire-jack/src/uuid.c b/pipewire-jack/src/uuid.c
new file mode 100644
index 0000000..8584e11
--- /dev/null
+++ b/pipewire-jack/src/uuid.c
@@ -0,0 +1,111 @@
+/* PipeWire
+ *
+ * Copyright © 2018 Wim Taymans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <jack/uuid.h>
+
+#include <pipewire/pipewire.h>
+
+SPA_EXPORT
+jack_uuid_t jack_client_uuid_generate (void)
+{
+ static uint32_t uuid_cnt = 0;
+ jack_uuid_t uuid = 0x2; /* JackUUIDClient */;
+ uuid = (uuid << 32) | ++uuid_cnt;
+ pw_log_debug("uuid %"PRIu64, uuid);
+ return uuid;
+}
+
+SPA_EXPORT
+jack_uuid_t jack_port_uuid_generate (uint32_t port_id)
+{
+ jack_uuid_t uuid = 0x1; /* JackUUIDPort */
+ uuid = (uuid << 32) | (port_id + 1);
+ pw_log_debug("uuid %d -> %"PRIu64, port_id, uuid);
+ return uuid;
+}
+
+SPA_EXPORT
+uint32_t jack_uuid_to_index (jack_uuid_t id)
+{
+ return (id & 0xffffff) - 1;
+}
+
+SPA_EXPORT
+int jack_uuid_compare (jack_uuid_t id1, jack_uuid_t id2)
+{
+ if (id1 == id2)
+ return 0;
+ if (id1 < id2)
+ return -1;
+ return 1;
+}
+
+SPA_EXPORT
+void jack_uuid_copy (jack_uuid_t* dst, jack_uuid_t src)
+{
+ spa_return_if_fail(dst != NULL);
+ *dst = src;
+}
+
+SPA_EXPORT
+void jack_uuid_clear (jack_uuid_t *id)
+{
+ spa_return_if_fail(id != NULL);
+ *id = 0;
+}
+
+SPA_EXPORT
+int jack_uuid_parse (const char *buf, jack_uuid_t *id)
+{
+ spa_return_val_if_fail(buf != NULL, -EINVAL);
+ spa_return_val_if_fail(id != NULL, -EINVAL);
+
+ if (sscanf (buf, "%" PRIu64, id) == 1) {
+ if (*id < (0x1LL << 32)) {
+ /* has not type bits set - not legal */
+ return -1;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+SPA_EXPORT
+void jack_uuid_unparse (jack_uuid_t id, char buf[JACK_UUID_STRING_SIZE])
+{
+ spa_return_if_fail(buf != NULL);
+ snprintf (buf, JACK_UUID_STRING_SIZE, "%" PRIu64, id);
+}
+
+SPA_EXPORT
+int jack_uuid_empty (jack_uuid_t id)
+{
+ return id == 0;
+}