2023-02-08 18:12:00 +01:00
|
|
|
/* PipeWire */
|
|
|
|
|
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
|
|
|
|
|
/* SPDX-License-Identifier: MIT */
|
2018-02-09 18:23:36 +01:00
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2018-02-13 18:05:55 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/mman.h>
|
2018-05-24 16:11:54 +02:00
|
|
|
#include <regex.h>
|
2018-11-07 09:58:20 +01:00
|
|
|
#include <math.h>
|
2018-02-09 18:23:36 +01:00
|
|
|
|
|
|
|
|
#include <jack/jack.h>
|
2023-07-03 04:15:37 +02:00
|
|
|
#include <jack/intclient.h>
|
2018-05-21 13:12:26 +02:00
|
|
|
#include <jack/session.h>
|
|
|
|
|
#include <jack/thread.h>
|
2018-10-03 20:13:57 +02:00
|
|
|
#include <jack/midiport.h>
|
2019-08-21 18:37:02 +02:00
|
|
|
#include <jack/uuid.h>
|
2019-11-05 10:01:43 +01:00
|
|
|
#include <jack/metadata.h>
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2019-01-28 17:10:37 +01:00
|
|
|
#include <spa/support/cpu.h>
|
2018-02-09 18:23:36 +01:00
|
|
|
#include <spa/param/audio/format-utils.h>
|
2019-10-28 15:03:31 +01:00
|
|
|
#include <spa/param/video/format-utils.h>
|
2023-07-11 19:31:27 +02:00
|
|
|
#include <spa/param/latency-utils.h>
|
2018-08-24 11:01:04 +02:00
|
|
|
#include <spa/debug/types.h>
|
2018-10-03 20:13:57 +02:00
|
|
|
#include <spa/debug/pod.h>
|
2021-03-05 17:37:12 +01:00
|
|
|
#include <spa/utils/json.h>
|
2023-07-11 19:31:27 +02:00
|
|
|
#include <spa/utils/result.h>
|
2021-05-18 11:36:13 +10:00
|
|
|
#include <spa/utils/string.h>
|
2023-05-11 18:21:57 +02:00
|
|
|
#include <spa/utils/ringbuffer.h>
|
2018-02-09 18:23:36 +01:00
|
|
|
|
|
|
|
|
#include <pipewire/pipewire.h>
|
2018-02-13 18:05:55 +01:00
|
|
|
#include <pipewire/private.h>
|
2021-07-05 10:40:25 +02:00
|
|
|
#include <pipewire/thread.h>
|
2019-09-09 17:16:18 +02:00
|
|
|
#include <pipewire/data-loop.h>
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2021-06-18 17:51:24 +03:00
|
|
|
#include "pipewire/extensions/client-node.h"
|
|
|
|
|
#include "pipewire/extensions/metadata.h"
|
2020-02-07 13:31:49 +01:00
|
|
|
#include "pipewire-jack-extensions.h"
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2019-10-28 13:19:46 +01:00
|
|
|
#define JACK_DEFAULT_VIDEO_TYPE "32 bit float RGBA video"
|
|
|
|
|
|
2021-06-28 11:46:41 +02:00
|
|
|
/* 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
|
|
|
|
|
|
2022-08-20 09:45:24 +03:00
|
|
|
#define JACK_CLIENT_NAME_SIZE 256
|
2018-02-09 18:23:36 +01:00
|
|
|
#define JACK_PORT_NAME_SIZE 256
|
|
|
|
|
#define JACK_PORT_TYPE_SIZE 32
|
2021-02-24 22:23:04 +01:00
|
|
|
#define MONITOR_EXT " Monitor"
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2022-07-10 21:59:05 +02:00
|
|
|
#define MAX_MIX 1024
|
2019-10-24 10:23:01 +02:00
|
|
|
#define MAX_BUFFER_FRAMES 8192
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2023-04-21 15:37:52 +02:00
|
|
|
#define MAX_CLIENT_PORTS 768
|
|
|
|
|
|
2019-10-24 10:23:01 +02:00
|
|
|
#define MAX_ALIGN 16
|
2018-02-20 13:06:07 +01:00
|
|
|
#define MAX_BUFFERS 2
|
2020-02-19 10:17:00 +01:00
|
|
|
#define MAX_BUFFER_DATAS 1u
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2018-05-21 13:12:26 +02:00
|
|
|
#define REAL_JACK_PORT_NAME_SIZE (JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE)
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
PW_LOG_TOPIC_STATIC(jack_log_topic, "jack");
|
|
|
|
|
#define PW_LOG_TOPIC_DEFAULT jack_log_topic
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2020-03-02 16:50:37 +01:00
|
|
|
#define TYPE_ID_AUDIO 0
|
|
|
|
|
#define TYPE_ID_MIDI 1
|
|
|
|
|
#define TYPE_ID_VIDEO 2
|
|
|
|
|
#define TYPE_ID_OTHER 3
|
|
|
|
|
|
2021-03-23 12:24:38 +01:00
|
|
|
#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
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
#define NOTIFY_BUFFER_SIZE (1u<<13)
|
|
|
|
|
#define NOTIFY_BUFFER_MASK (NOTIFY_BUFFER_SIZE-1)
|
|
|
|
|
|
|
|
|
|
struct notify {
|
|
|
|
|
#define NOTIFY_ACTIVE_FLAG (1<<0)
|
|
|
|
|
|
|
|
|
|
#define NOTIFY_TYPE_NONE ((0<<4)|NOTIFY_ACTIVE_FLAG)
|
|
|
|
|
#define NOTIFY_TYPE_REGISTRATION ((1<<4))
|
|
|
|
|
#define NOTIFY_TYPE_PORTREGISTRATION ((2<<4)|NOTIFY_ACTIVE_FLAG)
|
|
|
|
|
#define NOTIFY_TYPE_CONNECT ((3<<4)|NOTIFY_ACTIVE_FLAG)
|
2023-08-23 17:28:24 +02:00
|
|
|
#define NOTIFY_TYPE_BUFFER_FRAMES ((4<<4)|NOTIFY_ACTIVE_FLAG)
|
|
|
|
|
#define NOTIFY_TYPE_SAMPLE_RATE ((5<<4)|NOTIFY_ACTIVE_FLAG)
|
|
|
|
|
#define NOTIFY_TYPE_FREEWHEEL ((6<<4)|NOTIFY_ACTIVE_FLAG)
|
|
|
|
|
#define NOTIFY_TYPE_SHUTDOWN ((7<<4)|NOTIFY_ACTIVE_FLAG)
|
|
|
|
|
#define NOTIFY_TYPE_LATENCY ((8<<4)|NOTIFY_ACTIVE_FLAG)
|
|
|
|
|
#define NOTIFY_TYPE_TOTAL_LATENCY ((9<<4)|NOTIFY_ACTIVE_FLAG)
|
2023-05-11 18:21:57 +02:00
|
|
|
int type;
|
|
|
|
|
struct object *object;
|
|
|
|
|
int arg1;
|
|
|
|
|
const char *msg;
|
|
|
|
|
};
|
|
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
struct client;
|
2018-05-21 13:12:26 +02:00
|
|
|
struct port;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2018-05-21 13:12:26 +02:00
|
|
|
struct globals {
|
|
|
|
|
jack_thread_creator_t creator;
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_t lock;
|
2020-04-20 15:19:06 +02:00
|
|
|
struct pw_array descriptions;
|
2022-01-13 13:45:53 +01:00
|
|
|
struct spa_list free_objects;
|
2022-10-13 12:09:36 +02:00
|
|
|
struct spa_thread_utils *thread_utils;
|
2018-05-21 13:12:26 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct globals globals;
|
2021-01-19 14:47:44 +01:00
|
|
|
static bool mlock_warned = false;
|
2018-05-21 13:12:26 +02:00
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
#define OBJECT_CHUNK 8
|
|
|
|
|
#define RECYCLE_THRESHOLD 128
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2022-07-10 21:59:05 +02:00
|
|
|
typedef void (*mix_func) (float *dst, float *src[], uint32_t n_src, bool aligned, uint32_t n_samples);
|
2019-01-28 17:10:37 +01:00
|
|
|
|
2022-07-10 21:59:05 +02:00
|
|
|
static mix_func mix_function;
|
2019-01-28 17:10:37 +01:00
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object {
|
|
|
|
|
struct spa_list link;
|
|
|
|
|
|
|
|
|
|
struct client *client;
|
|
|
|
|
|
2021-11-18 12:14:38 +01:00
|
|
|
#define INTERFACE_Port 0
|
|
|
|
|
#define INTERFACE_Node 1
|
|
|
|
|
#define INTERFACE_Link 2
|
2018-02-15 17:57:43 +01:00
|
|
|
uint32_t type;
|
2018-02-09 18:23:36 +01:00
|
|
|
uint32_t id;
|
2022-01-14 21:04:08 +01:00
|
|
|
uint32_t serial;
|
2018-02-15 17:57:43 +01:00
|
|
|
|
|
|
|
|
union {
|
|
|
|
|
struct {
|
2018-05-21 13:12:26 +02:00
|
|
|
char name[JACK_CLIENT_NAME_SIZE+1];
|
2021-03-05 17:37:12 +01:00
|
|
|
char node_name[512];
|
2019-10-22 12:21:37 +02:00
|
|
|
int32_t priority;
|
2020-02-05 10:27:37 +01:00
|
|
|
uint32_t client_id;
|
2023-08-02 17:30:35 +02:00
|
|
|
unsigned is_jack:1;
|
|
|
|
|
unsigned is_running:1;
|
2018-02-15 17:57:43 +01:00
|
|
|
} node;
|
|
|
|
|
struct {
|
|
|
|
|
uint32_t src;
|
|
|
|
|
uint32_t dst;
|
2022-01-14 21:04:08 +01:00
|
|
|
uint32_t src_serial;
|
|
|
|
|
uint32_t dst_serial;
|
2021-10-01 09:30:39 +02:00
|
|
|
bool src_ours;
|
|
|
|
|
bool dst_ours;
|
2021-12-07 15:27:18 +01:00
|
|
|
struct port *our_input;
|
|
|
|
|
struct port *our_output;
|
2018-02-15 17:57:43 +01:00
|
|
|
} port_link;
|
|
|
|
|
struct {
|
|
|
|
|
unsigned long flags;
|
2018-05-21 13:12:26 +02:00
|
|
|
char name[REAL_JACK_PORT_NAME_SIZE+1];
|
|
|
|
|
char alias1[REAL_JACK_PORT_NAME_SIZE+1];
|
|
|
|
|
char alias2[REAL_JACK_PORT_NAME_SIZE+1];
|
2021-02-17 11:16:02 +01:00
|
|
|
char system[REAL_JACK_PORT_NAME_SIZE+1];
|
2021-11-08 09:23:20 +01:00
|
|
|
uint32_t system_id;
|
2018-02-15 17:57:43 +01:00
|
|
|
uint32_t type_id;
|
2019-08-21 18:37:02 +02:00
|
|
|
uint32_t node_id;
|
2019-09-03 13:44:12 +02:00
|
|
|
uint32_t monitor_requests;
|
2019-10-22 12:21:37 +02:00
|
|
|
int32_t priority;
|
2020-09-30 11:59:41 +02:00
|
|
|
struct port *port;
|
2021-01-05 15:38:13 +01:00
|
|
|
bool is_monitor;
|
2021-03-05 17:37:12 +01:00
|
|
|
struct object *node;
|
2021-05-24 18:01:19 +02:00
|
|
|
struct spa_latency_info latency[2];
|
2018-02-15 17:57:43 +01:00
|
|
|
} port;
|
|
|
|
|
};
|
2021-05-21 10:02:34 +02:00
|
|
|
struct pw_proxy *proxy;
|
|
|
|
|
struct spa_hook proxy_listener;
|
|
|
|
|
struct spa_hook object_listener;
|
2023-05-12 15:25:38 +02:00
|
|
|
int registered;
|
2023-08-07 19:57:01 +02:00
|
|
|
unsigned int visible;
|
2021-07-27 12:07:33 +02:00
|
|
|
unsigned int removing:1;
|
2022-01-13 13:45:53 +01:00
|
|
|
unsigned int removed:1;
|
2018-02-09 18:23:36 +01:00
|
|
|
};
|
|
|
|
|
|
2018-05-24 16:12:59 +02:00
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
2018-10-03 20:13:57 +02:00
|
|
|
#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];
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
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;
|
|
|
|
|
|
2019-07-23 17:46:06 +02:00
|
|
|
struct pw_memmap *mem[MAX_BUFFER_DATAS+1];
|
2018-02-13 18:05:55 +01:00
|
|
|
uint32_t n_mem;
|
|
|
|
|
};
|
|
|
|
|
|
2018-05-21 13:12:26 +02:00
|
|
|
struct mix {
|
|
|
|
|
struct spa_list link;
|
|
|
|
|
struct spa_list port_link;
|
|
|
|
|
uint32_t id;
|
2021-08-03 17:39:14 +02:00
|
|
|
uint32_t peer_id;
|
2018-05-21 13:12:26 +02:00
|
|
|
struct port *port;
|
2021-12-07 15:27:18 +01:00
|
|
|
struct port *peer_port;
|
2018-05-21 13:12:26 +02:00
|
|
|
|
|
|
|
|
struct spa_io_buffers *io;
|
|
|
|
|
|
|
|
|
|
struct buffer buffers[MAX_BUFFERS];
|
|
|
|
|
uint32_t n_buffers;
|
|
|
|
|
struct spa_list queue;
|
|
|
|
|
};
|
|
|
|
|
|
2018-02-09 18:23:36 +01:00
|
|
|
struct port {
|
|
|
|
|
bool valid;
|
2018-05-21 13:12:26 +02:00
|
|
|
struct spa_list link;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
|
|
|
|
struct client *client;
|
|
|
|
|
|
2018-05-21 13:12:26 +02:00
|
|
|
enum spa_direction direction;
|
2022-01-22 20:55:40 +01:00
|
|
|
uint32_t port_id;
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *object;
|
2021-05-25 17:55:13 +02:00
|
|
|
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];
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2019-09-16 21:08:35 +02:00
|
|
|
struct spa_io_buffers io;
|
2018-05-21 13:12:26 +02:00
|
|
|
struct spa_list mix;
|
2020-09-30 11:59:41 +02:00
|
|
|
struct mix *global_mix;
|
2018-07-31 21:42:28 +02:00
|
|
|
|
2023-08-25 14:28:38 +02:00
|
|
|
struct port *tied;
|
|
|
|
|
|
2020-01-29 10:29:26 +01:00
|
|
|
unsigned int empty_out:1;
|
|
|
|
|
unsigned int zeroed:1;
|
|
|
|
|
|
2019-01-24 18:25:44 +01:00
|
|
|
float *emptyptr;
|
2019-10-24 10:23:01 +02:00
|
|
|
float empty[MAX_BUFFER_FRAMES + MAX_ALIGN];
|
2020-09-30 11:59:41 +02:00
|
|
|
|
|
|
|
|
void *(*get_buffer) (struct port *p, jack_nframes_t frames);
|
2018-02-09 18:23:36 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-07 16:45:58 +01:00
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
2018-02-09 18:23:36 +01:00
|
|
|
struct context {
|
2020-08-11 11:11:00 +02:00
|
|
|
struct pw_loop *l;
|
2020-04-30 12:57:56 +02:00
|
|
|
struct pw_thread_loop *loop; /* thread_lock protects all below */
|
2019-12-10 18:19:56 +01:00
|
|
|
struct pw_context *context;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2022-03-30 20:31:42 +02:00
|
|
|
struct spa_thread_utils *old_thread_utils;
|
|
|
|
|
struct spa_thread_utils thread_utils;
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_t lock; /* protects map and lists below, in addition to thread_lock */
|
2022-01-13 13:45:53 +01:00
|
|
|
struct spa_list objects;
|
|
|
|
|
uint32_t free_count;
|
2018-02-09 18:23:36 +01:00
|
|
|
};
|
|
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
#define GET_DIRECTION(f) ((f) & JackPortIsInput ? SPA_DIRECTION_INPUT : SPA_DIRECTION_OUTPUT)
|
|
|
|
|
|
2022-01-22 20:55:40 +01:00
|
|
|
#define GET_PORT(c,d,p) (pw_map_lookup(&c->ports[d], p))
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2019-11-05 10:01:43 +01:00
|
|
|
struct metadata {
|
|
|
|
|
struct pw_metadata *proxy;
|
2022-01-31 15:04:08 +01:00
|
|
|
struct spa_hook proxy_listener;
|
2019-11-05 10:01:43 +01:00
|
|
|
struct spa_hook listener;
|
2020-07-17 17:23:43 +02:00
|
|
|
|
2021-03-05 17:37:12 +01:00
|
|
|
char default_audio_sink[1024];
|
|
|
|
|
char default_audio_source[1024];
|
2019-11-05 10:01:43 +01:00
|
|
|
};
|
|
|
|
|
|
2018-02-09 18:23:36 +01:00
|
|
|
struct client {
|
2018-05-21 13:12:26 +02:00
|
|
|
char name[JACK_CLIENT_NAME_SIZE+1];
|
2018-02-09 18:23:36 +01:00
|
|
|
|
|
|
|
|
struct context context;
|
|
|
|
|
|
2020-07-23 13:18:14 +02:00
|
|
|
char *server_name;
|
|
|
|
|
char *load_name; /* load module name */
|
|
|
|
|
char *load_init; /* initialization string */
|
|
|
|
|
jack_uuid_t session_id; /* requested session_id */
|
|
|
|
|
|
2023-07-11 19:31:27 +02:00
|
|
|
struct pw_loop *l;
|
2019-09-09 17:16:18 +02:00
|
|
|
struct pw_data_loop *loop;
|
2020-07-21 16:50:45 +02:00
|
|
|
struct pw_properties *props;
|
2019-09-09 17:16:18 +02:00
|
|
|
|
2019-12-11 07:46:59 +01:00
|
|
|
struct pw_core *core;
|
2019-01-10 09:31:00 +01:00
|
|
|
struct spa_hook core_listener;
|
2019-12-06 11:48:40 +01:00
|
|
|
struct pw_mempool *pool;
|
2021-09-07 17:13:15 +02:00
|
|
|
int pending_sync;
|
2019-02-25 17:15:41 +01:00
|
|
|
int last_sync;
|
2020-02-24 11:25:31 +01:00
|
|
|
int last_res;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2021-05-25 17:55:13 +02:00
|
|
|
struct spa_node_info info;
|
|
|
|
|
|
2019-12-11 09:44:48 +01:00
|
|
|
struct pw_registry *registry;
|
2018-02-09 18:23:36 +01:00
|
|
|
struct spa_hook registry_listener;
|
|
|
|
|
|
2019-12-11 16:13:46 +01:00
|
|
|
struct pw_client_node *node;
|
2018-02-09 18:23:36 +01:00
|
|
|
struct spa_hook node_listener;
|
|
|
|
|
struct spa_hook proxy_listener;
|
|
|
|
|
|
2019-11-05 10:01:43 +01:00
|
|
|
struct metadata *metadata;
|
2022-10-20 16:34:47 +02:00
|
|
|
struct metadata *settings;
|
2019-11-05 10:01:43 +01:00
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
uint32_t node_id;
|
2022-01-26 17:44:49 +01:00
|
|
|
uint32_t serial;
|
2023-05-11 18:21:57 +02:00
|
|
|
struct object *object;
|
|
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
struct spa_source *socket_source;
|
2023-05-11 18:21:57 +02:00
|
|
|
struct spa_source *notify_source;
|
|
|
|
|
void *notify_buffer;
|
|
|
|
|
struct spa_ringbuffer notify_ring;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
|
|
|
|
JackThreadCallback thread_callback;
|
|
|
|
|
void *thread_arg;
|
2018-02-15 17:57:43 +01:00
|
|
|
JackThreadInitCallback thread_init_callback;
|
|
|
|
|
void *thread_init_arg;
|
|
|
|
|
JackShutdownCallback shutdown_callback;
|
|
|
|
|
void *shutdown_arg;
|
|
|
|
|
JackInfoShutdownCallback info_shutdown_callback;
|
|
|
|
|
void *info_shutdown_arg;
|
2018-02-13 18:05:55 +01:00
|
|
|
JackProcessCallback process_callback;
|
|
|
|
|
void *process_arg;
|
2018-02-15 17:57:43 +01:00
|
|
|
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;
|
2019-08-29 14:01:20 +02:00
|
|
|
JackPortRenameCallback rename_callback;
|
|
|
|
|
void *rename_arg;
|
2018-02-16 12:08:16 +01:00
|
|
|
JackGraphOrderCallback graph_callback;
|
|
|
|
|
void *graph_arg;
|
2019-08-22 13:56:39 +02:00
|
|
|
JackXRunCallback xrun_callback;
|
|
|
|
|
void *xrun_arg;
|
2019-09-02 17:16:57 +02:00
|
|
|
JackLatencyCallback latency_callback;
|
|
|
|
|
void *latency_arg;
|
2018-05-24 16:13:23 +02:00
|
|
|
JackSyncCallback sync_callback;
|
|
|
|
|
void *sync_arg;
|
|
|
|
|
JackTimebaseCallback timebase_callback;
|
|
|
|
|
void *timebase_arg;
|
2019-11-05 10:01:43 +01:00
|
|
|
JackPropertyChangeCallback property_callback;
|
|
|
|
|
void *property_arg;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2018-10-24 10:34:49 +02:00
|
|
|
struct spa_io_position *position;
|
2018-02-13 18:05:55 +01:00
|
|
|
uint32_t sample_rate;
|
2019-10-24 10:23:01 +02:00
|
|
|
uint32_t buffer_frames;
|
2021-03-12 15:58:34 +01:00
|
|
|
struct spa_fraction latency;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2021-08-03 17:39:14 +02:00
|
|
|
struct spa_list mix;
|
2018-05-21 13:12:26 +02:00
|
|
|
struct spa_list free_mix;
|
|
|
|
|
|
2022-01-22 20:55:40 +01:00
|
|
|
struct spa_list free_ports;
|
|
|
|
|
struct pw_map ports[2];
|
2023-04-21 15:37:52 +02:00
|
|
|
uint32_t n_ports;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2020-02-07 16:45:58 +01:00
|
|
|
struct spa_list links;
|
2019-08-22 10:59:16 +02:00
|
|
|
uint32_t driver_id;
|
|
|
|
|
struct pw_node_activation *driver_activation;
|
2019-02-12 17:40:46 +01:00
|
|
|
|
2019-07-25 12:05:54 +02:00
|
|
|
struct pw_memmap *mem;
|
2019-02-12 17:40:46 +01:00
|
|
|
struct pw_node_activation *activation;
|
2019-08-22 13:56:39 +02:00
|
|
|
uint32_t xrun_count;
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
struct {
|
2020-04-30 16:15:22 +02:00
|
|
|
struct spa_io_position *position;
|
2020-04-30 12:57:56 +02:00
|
|
|
struct pw_node_activation *driver_activation;
|
|
|
|
|
struct spa_list target_links;
|
|
|
|
|
} rt;
|
2020-02-07 16:45:58 +01:00
|
|
|
|
2021-06-17 10:46:04 +02:00
|
|
|
pthread_mutex_t rt_lock;
|
2022-02-10 17:13:48 +01:00
|
|
|
unsigned int rt_locked:1;
|
2022-04-26 20:15:58 +02:00
|
|
|
unsigned int data_locked:1;
|
2020-08-11 11:11:00 +02:00
|
|
|
|
2019-08-27 14:41:15 +02:00
|
|
|
unsigned int started:1;
|
2019-08-30 15:41:25 +02:00
|
|
|
unsigned int active:1;
|
|
|
|
|
unsigned int destroyed:1;
|
2019-09-03 13:45:13 +02:00
|
|
|
unsigned int first:1;
|
2019-09-09 17:16:18 +02:00
|
|
|
unsigned int thread_entered:1;
|
2019-11-28 12:57:06 +01:00
|
|
|
unsigned int has_transport:1;
|
2020-02-04 17:09:41 +01:00
|
|
|
unsigned int allow_mlock:1;
|
2021-02-08 10:59:02 +01:00
|
|
|
unsigned int warn_mlock:1;
|
2020-08-06 13:49:33 +02:00
|
|
|
unsigned int timeowner_conditional:1;
|
2021-10-29 09:27:46 +02:00
|
|
|
unsigned int show_monitor:1;
|
2023-04-21 15:04:45 +02:00
|
|
|
unsigned int show_midi:1;
|
2021-02-16 13:23:47 +01:00
|
|
|
unsigned int merge_monitor:1;
|
2021-02-23 16:13:08 +01:00
|
|
|
unsigned int short_name:1;
|
|
|
|
|
unsigned int filter_name:1;
|
2021-05-06 18:14:07 +02:00
|
|
|
unsigned int freewheeling:1;
|
2021-09-08 13:02:04 +02:00
|
|
|
unsigned int locked_process:1;
|
2022-02-04 17:06:54 +01:00
|
|
|
unsigned int default_as_system:1;
|
2021-03-23 12:24:38 +01:00
|
|
|
int self_connect_mode;
|
2021-06-28 11:46:41 +02:00
|
|
|
int rt_max;
|
2022-06-20 15:51:33 +02:00
|
|
|
unsigned int fix_midi_events:1;
|
2022-10-20 16:34:47 +02:00
|
|
|
unsigned int global_buffer_size:1;
|
2023-03-08 16:51:40 +01:00
|
|
|
unsigned int passive_links:1;
|
2023-05-02 14:41:46 +02:00
|
|
|
unsigned int graph_callback_pending:1;
|
2023-05-10 12:10:03 +02:00
|
|
|
unsigned int pending_callbacks:1;
|
2023-05-10 16:46:42 +02:00
|
|
|
int frozen_callbacks;
|
2022-11-10 17:40:25 +01:00
|
|
|
char filter_char;
|
2023-04-21 15:37:52 +02:00
|
|
|
uint32_t max_ports;
|
2023-05-12 17:55:00 +02:00
|
|
|
unsigned int fill_aliases:1;
|
2019-09-02 12:02:34 +02:00
|
|
|
|
|
|
|
|
jack_position_t jack_position;
|
|
|
|
|
jack_transport_state_t jack_state;
|
2018-02-09 18:23:36 +01:00
|
|
|
};
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
#define return_val_if_fail(expr, val) \
|
|
|
|
|
({ \
|
|
|
|
|
if (SPA_UNLIKELY(!(expr))) { \
|
|
|
|
|
pw_log_warn("'%s' failed at %s:%u %s()", \
|
|
|
|
|
#expr , __FILE__, __LINE__, __func__); \
|
|
|
|
|
return (val); \
|
|
|
|
|
} \
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
#define return_if_fail(expr) \
|
|
|
|
|
({ \
|
|
|
|
|
if (SPA_UNLIKELY(!(expr))) { \
|
|
|
|
|
pw_log_warn("'%s' failed at %s:%u %s()", \
|
|
|
|
|
#expr , __FILE__, __LINE__, __func__); \
|
|
|
|
|
return; \
|
|
|
|
|
} \
|
|
|
|
|
})
|
|
|
|
|
|
2020-04-20 11:50:37 +02:00
|
|
|
static int do_sync(struct client *client);
|
2022-01-26 17:44:49 +01:00
|
|
|
static struct object *find_by_serial(struct client *c, uint32_t serial);
|
2020-04-20 11:50:37 +02:00
|
|
|
|
2019-11-05 10:01:43 +01:00
|
|
|
#include "metadata.c"
|
|
|
|
|
|
2021-07-27 15:50:05 +02:00
|
|
|
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);
|
|
|
|
|
|
2021-03-30 12:41:59 +02:00
|
|
|
static struct object * alloc_object(struct client *c, int type)
|
2018-02-15 17:57:43 +01:00
|
|
|
{
|
|
|
|
|
struct object *o;
|
|
|
|
|
int i;
|
|
|
|
|
|
2021-08-25 11:56:19 +02:00
|
|
|
pthread_mutex_lock(&globals.lock);
|
2022-01-13 13:45:53 +01:00
|
|
|
if (spa_list_is_empty(&globals.free_objects)) {
|
2018-02-15 17:57:43 +01:00
|
|
|
o = calloc(OBJECT_CHUNK, sizeof(struct object));
|
2021-08-25 11:56:19 +02:00
|
|
|
if (o == NULL) {
|
|
|
|
|
pthread_mutex_unlock(&globals.lock);
|
2018-02-15 17:57:43 +01:00
|
|
|
return NULL;
|
2021-08-25 11:56:19 +02:00
|
|
|
}
|
2022-01-13 13:45:53 +01:00
|
|
|
for (i = 0; i < OBJECT_CHUNK; i++)
|
|
|
|
|
spa_list_append(&globals.free_objects, &o[i].link);
|
2018-02-15 17:57:43 +01:00
|
|
|
}
|
2022-01-13 13:45:53 +01:00
|
|
|
o = spa_list_first(&globals.free_objects, struct object, link);
|
2021-03-30 12:41:59 +02:00
|
|
|
spa_list_remove(&o->link);
|
2021-08-25 11:56:19 +02:00
|
|
|
pthread_mutex_unlock(&globals.lock);
|
|
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
o->client = c;
|
2022-01-13 13:45:53 +01:00
|
|
|
o->removed = false;
|
2021-03-30 12:41:59 +02:00
|
|
|
o->type = type;
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: object:%p type:%d", c, o, type);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
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) {
|
2022-01-14 21:04:08 +01:00
|
|
|
pw_log_info("%p: recycle object:%p type:%d id:%u/%u",
|
|
|
|
|
c, o, o->type, o->id, o->serial);
|
2022-01-13 13:45:53 +01:00
|
|
|
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. */
|
2021-08-25 11:56:19 +02:00
|
|
|
static void free_object(struct client *c, struct object *o)
|
2018-02-15 17:57:43 +01:00
|
|
|
{
|
2022-01-13 13:45:53 +01:00
|
|
|
pw_log_debug("%p: object:%p type:%d", c, o, o->type);
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2021-06-24 15:46:07 +02:00
|
|
|
spa_list_remove(&o->link);
|
2022-01-13 13:45:53 +01:00
|
|
|
o->removed = true;
|
2022-01-14 21:04:08 +01:00
|
|
|
o->id = SPA_ID_INVALID;
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_append(&c->context.objects, &o->link);
|
|
|
|
|
if (++c->context.free_count > RECYCLE_THRESHOLD)
|
|
|
|
|
recycle_objects(c, RECYCLE_THRESHOLD / 2);
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
2021-06-24 15:46:07 +02:00
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-20 19:24:38 +02:00
|
|
|
static void init_mix(struct mix *mix, uint32_t mix_id, struct port *port, uint32_t peer_id)
|
2020-09-30 11:59:41 +02:00
|
|
|
{
|
2023-06-20 19:24:38 +02:00
|
|
|
pw_log_debug("create %p mix:%d peer:%d", port, mix_id, peer_id);
|
2020-09-30 11:59:41 +02:00
|
|
|
mix->id = mix_id;
|
2023-06-20 20:59:27 +02:00
|
|
|
mix->peer_id = peer_id;
|
2020-09-30 11:59:41 +02:00
|
|
|
mix->port = port;
|
2023-06-20 19:24:38 +02:00
|
|
|
mix->peer_port = NULL;
|
2020-09-30 11:59:41 +02:00
|
|
|
mix->io = NULL;
|
|
|
|
|
mix->n_buffers = 0;
|
|
|
|
|
spa_list_init(&mix->queue);
|
|
|
|
|
if (mix_id == SPA_ID_INVALID)
|
|
|
|
|
port->global_mix = mix;
|
|
|
|
|
}
|
2021-08-03 17:39:14 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-30 11:59:41 +02:00
|
|
|
|
2023-06-20 20:09:29 +02:00
|
|
|
static struct mix *find_port_peer(struct port *port, uint32_t peer_id)
|
|
|
|
|
{
|
|
|
|
|
struct mix *mix;
|
|
|
|
|
spa_list_for_each(mix, &port->mix, port_link) {
|
|
|
|
|
pw_log_info("%p %d %d", port, mix->peer_id, peer_id);
|
|
|
|
|
if (mix->peer_id == peer_id)
|
|
|
|
|
return mix;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-03 13:45:33 +02:00
|
|
|
static struct mix *find_mix(struct client *c, struct port *port, uint32_t mix_id)
|
2018-05-21 13:12:26 +02:00
|
|
|
{
|
|
|
|
|
struct mix *mix;
|
|
|
|
|
|
|
|
|
|
spa_list_for_each(mix, &port->mix, port_link) {
|
|
|
|
|
if (mix->id == mix_id)
|
|
|
|
|
return mix;
|
|
|
|
|
}
|
2019-09-03 13:45:33 +02:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-20 19:24:38 +02:00
|
|
|
static struct mix *create_mix(struct client *c, struct port *port,
|
|
|
|
|
uint32_t mix_id, uint32_t peer_id)
|
2019-09-03 13:45:33 +02:00
|
|
|
{
|
|
|
|
|
struct mix *mix;
|
2020-06-16 21:12:12 +02:00
|
|
|
uint32_t i;
|
2019-09-03 13:45:33 +02:00
|
|
|
|
2020-06-16 21:12:12 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2018-05-21 13:12:26 +02:00
|
|
|
mix = spa_list_first(&c->free_mix, struct mix, link);
|
|
|
|
|
spa_list_remove(&mix->link);
|
2021-08-03 17:39:14 +02:00
|
|
|
spa_list_append(&c->mix, &mix->link);
|
2018-05-21 13:12:26 +02:00
|
|
|
|
|
|
|
|
spa_list_append(&port->mix, &mix->port_link);
|
|
|
|
|
|
2023-06-20 19:24:38 +02:00
|
|
|
init_mix(mix, mix_id, port, peer_id);
|
2018-05-21 13:12:26 +02:00
|
|
|
|
|
|
|
|
return mix;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-21 15:53:43 +02:00
|
|
|
static int clear_buffers(struct client *c, struct mix *mix)
|
|
|
|
|
{
|
|
|
|
|
struct port *port = mix->port;
|
|
|
|
|
struct buffer *b;
|
|
|
|
|
uint32_t i, j;
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: port %p clear buffers", c, port);
|
2020-04-21 15:53:43 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-21 13:12:26 +02:00
|
|
|
static void free_mix(struct client *c, struct mix *mix)
|
|
|
|
|
{
|
2020-04-21 15:53:43 +02:00
|
|
|
clear_buffers(c, mix);
|
2018-05-21 13:12:26 +02:00
|
|
|
spa_list_remove(&mix->port_link);
|
2020-09-30 11:59:41 +02:00
|
|
|
if (mix->id == SPA_ID_INVALID)
|
|
|
|
|
mix->port->global_mix = NULL;
|
2021-08-03 17:39:14 +02:00
|
|
|
spa_list_remove(&mix->link);
|
2018-05-21 13:12:26 +02:00
|
|
|
spa_list_append(&c->free_mix, &mix->link);
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
static struct port * alloc_port(struct client *c, enum spa_direction direction)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2018-02-13 18:05:55 +01:00
|
|
|
struct port *p;
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o;
|
2022-01-22 20:55:40 +01:00
|
|
|
uint32_t i;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2023-04-21 15:37:52 +02:00
|
|
|
if (c->n_ports >= c->max_ports) {
|
|
|
|
|
errno = ENOSPC;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 20:55:40 +01:00
|
|
|
if (spa_list_is_empty(&c->free_ports)) {
|
2020-06-16 20:57:52 +02:00
|
|
|
p = calloc(OBJECT_CHUNK, sizeof(struct port));
|
|
|
|
|
if (p == NULL)
|
|
|
|
|
return NULL;
|
2022-01-22 20:55:40 +01:00
|
|
|
for (i = 0; i < OBJECT_CHUNK; i++)
|
|
|
|
|
spa_list_append(&c->free_ports, &p[i].link);
|
2020-06-16 20:57:52 +02:00
|
|
|
}
|
2022-01-22 20:55:40 +01:00
|
|
|
p = spa_list_first(&c->free_ports, struct port, link);
|
2018-05-21 13:12:26 +02:00
|
|
|
spa_list_remove(&p->link);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2021-03-30 12:41:59 +02:00
|
|
|
o = alloc_object(c, INTERFACE_Port);
|
2023-04-21 15:37:52 +02:00
|
|
|
if (o == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
o->id = SPA_ID_INVALID;
|
2019-08-21 18:37:02 +02:00
|
|
|
o->port.node_id = c->node_id;
|
2020-09-30 11:59:41 +02:00
|
|
|
o->port.port = p;
|
2021-05-26 15:37:23 +02:00
|
|
|
o->port.latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
|
|
|
|
|
o->port.latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
p->valid = true;
|
2018-08-15 21:26:05 +02:00
|
|
|
p->zeroed = false;
|
2018-02-13 18:05:55 +01:00
|
|
|
p->client = c;
|
2018-02-15 17:57:43 +01:00
|
|
|
p->object = o;
|
2018-05-21 13:12:26 +02:00
|
|
|
spa_list_init(&p->mix);
|
2021-05-25 17:55:13 +02:00
|
|
|
p->props = pw_properties_new(NULL, NULL);
|
2018-05-21 13:12:26 +02:00
|
|
|
|
2022-01-22 20:55:40 +01:00
|
|
|
p->direction = direction;
|
|
|
|
|
p->emptyptr = SPA_PTR_ALIGN(p->empty, MAX_ALIGN, float);
|
|
|
|
|
p->port_id = pw_map_insert_new(&c->ports[direction], p);
|
2023-04-21 15:37:52 +02:00
|
|
|
c->n_ports++;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_append(&c->context.objects, &o->link);
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
|
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
return p;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-10 16:49:41 +02:00
|
|
|
static void free_port(struct client *c, struct port *p, bool free)
|
2018-02-13 18:05:55 +01:00
|
|
|
{
|
2020-02-18 17:37:02 +01:00
|
|
|
struct mix *m;
|
2018-05-21 13:12:26 +02:00
|
|
|
|
2020-02-18 17:37:02 +01:00
|
|
|
spa_list_consume(m, &p->mix, port_link)
|
2018-05-21 13:12:26 +02:00
|
|
|
free_mix(c, m);
|
|
|
|
|
|
2023-04-21 15:37:52 +02:00
|
|
|
c->n_ports--;
|
2022-01-22 20:55:40 +01:00
|
|
|
pw_map_remove(&c->ports[p->direction], p->port_id);
|
2021-05-25 17:55:13 +02:00
|
|
|
pw_properties_free(p->props);
|
2022-01-22 20:55:40 +01:00
|
|
|
spa_list_append(&c->free_ports, &p->link);
|
2023-05-10 16:49:41 +02:00
|
|
|
if (free)
|
|
|
|
|
free_object(c, p->object);
|
|
|
|
|
else
|
|
|
|
|
p->object->removing = true;
|
2018-02-13 18:05:55 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-05 10:27:37 +01:00
|
|
|
static struct object *find_node(struct client *c, const char *name)
|
|
|
|
|
{
|
|
|
|
|
struct object *o;
|
|
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
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))
|
2020-02-05 10:27:37 +01:00
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-05 17:37:12 +01:00
|
|
|
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 &&
|
2021-05-18 11:36:13 +10:00
|
|
|
(spa_streq(ot->node.node_name, c->metadata->default_audio_source) ||
|
|
|
|
|
spa_streq(ot->node.node_name, c->metadata->default_audio_sink)))
|
2021-03-05 17:37:12 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-24 19:28:57 +02:00
|
|
|
static inline bool client_port_visible(struct client *c, struct object *o)
|
|
|
|
|
{
|
|
|
|
|
if (o->port.port != NULL && o->port.port->client == c)
|
|
|
|
|
return true;
|
|
|
|
|
return o->visible;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-14 21:04:08 +01:00
|
|
|
static struct object *find_port_by_name(struct client *c, const char *name)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_for_each(o, &c->context.objects, link) {
|
2023-08-24 19:28:57 +02:00
|
|
|
if (o->type != INTERFACE_Port || o->removed ||
|
|
|
|
|
(!client_port_visible(c, o)))
|
2022-01-13 13:45:53 +01:00
|
|
|
continue;
|
2021-05-18 11:36:13 +10:00
|
|
|
if (spa_streq(o->port.name, name) ||
|
|
|
|
|
spa_streq(o->port.alias1, name) ||
|
|
|
|
|
spa_streq(o->port.alias2, name))
|
2018-02-15 17:57:43 +01:00
|
|
|
return o;
|
2021-05-18 11:36:13 +10:00
|
|
|
if (is_port_default(c, o) && spa_streq(o->port.system, name))
|
2021-02-17 11:16:02 +01:00
|
|
|
return o;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2021-06-24 15:46:07 +02:00
|
|
|
|
2022-01-14 21:04:08 +01:00
|
|
|
static struct object *find_by_id(struct client *c, uint32_t id)
|
2022-01-13 13:45:53 +01:00
|
|
|
{
|
|
|
|
|
struct object *o;
|
|
|
|
|
spa_list_for_each(o, &c->context.objects, link) {
|
|
|
|
|
if (o->id == id)
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-26 17:44:49 +01:00
|
|
|
static struct object *find_by_serial(struct client *c, uint32_t serial)
|
2022-01-14 21:04:08 +01:00
|
|
|
{
|
|
|
|
|
struct object *o;
|
|
|
|
|
spa_list_for_each(o, &c->context.objects, link) {
|
2022-01-26 17:44:49 +01:00
|
|
|
if (o->serial == serial)
|
|
|
|
|
return o;
|
2022-01-14 21:04:08 +01:00
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-03 15:46:21 +02:00
|
|
|
static struct object *find_id(struct client *c, uint32_t id, bool valid)
|
2021-06-22 11:16:45 +02:00
|
|
|
{
|
2022-01-14 21:04:08 +01:00
|
|
|
struct object *o = find_by_id(c, id);
|
2021-09-03 15:46:21 +02:00
|
|
|
if (o != NULL && (!valid || o->client == c))
|
|
|
|
|
return o;
|
|
|
|
|
return NULL;
|
2021-06-24 15:46:07 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-03 15:46:21 +02:00
|
|
|
static struct object *find_type(struct client *c, uint32_t id, uint32_t type, bool valid)
|
2021-06-24 15:46:07 +02:00
|
|
|
{
|
2021-09-03 15:46:21 +02:00
|
|
|
struct object *o = find_id(c, id, valid);
|
2021-06-22 11:16:45 +02:00
|
|
|
if (o != NULL && o->type == type)
|
|
|
|
|
return o;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2018-02-16 12:08:16 +01:00
|
|
|
static struct object *find_link(struct client *c, uint32_t src, uint32_t dst)
|
|
|
|
|
{
|
|
|
|
|
struct object *l;
|
|
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_for_each(l, &c->context.objects, link) {
|
|
|
|
|
if (l->type != INTERFACE_Link || l->removed)
|
|
|
|
|
continue;
|
2018-02-16 12:08:16 +01:00
|
|
|
if (l->port_link.src == src &&
|
|
|
|
|
l->port_link.dst == dst) {
|
|
|
|
|
return l;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-26 12:10:15 +02:00
|
|
|
static struct buffer *dequeue_buffer(struct client *c, struct mix *mix)
|
2018-02-13 18:05:55 +01:00
|
|
|
{
|
2020-03-16 12:52:28 +01:00
|
|
|
struct buffer *b;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(spa_list_is_empty(&mix->queue)))
|
|
|
|
|
return NULL;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
b = spa_list_first(&mix->queue, struct buffer, link);
|
|
|
|
|
spa_list_remove(&b->link);
|
2018-02-13 18:05:55 +01:00
|
|
|
SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
|
2021-10-26 12:10:15 +02:00
|
|
|
pw_log_trace_fp("%p: port %p: dequeue buffer %d", c, mix->port, b->id);
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
return b;
|
2018-02-13 18:05:55 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-28 17:10:37 +01:00
|
|
|
#if defined (__SSE__)
|
|
|
|
|
#include <xmmintrin.h>
|
2022-07-10 21:59:05 +02:00
|
|
|
static void mix_sse(float *dst, float *src[], uint32_t n_src, bool aligned, uint32_t n_samples)
|
2019-01-28 17:10:37 +01:00
|
|
|
{
|
2022-07-10 21:59:05 +02:00
|
|
|
uint32_t i, n, unrolled;
|
|
|
|
|
__m128 in[1];
|
2019-01-28 17:10:37 +01:00
|
|
|
|
2022-07-10 21:59:05 +02:00
|
|
|
if (SPA_IS_ALIGNED(dst, 16) && aligned)
|
|
|
|
|
unrolled = n_samples & ~3;
|
2019-01-28 17:10:37 +01:00
|
|
|
else
|
|
|
|
|
unrolled = 0;
|
|
|
|
|
|
2022-07-10 21:59:05 +02:00
|
|
|
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]));
|
2019-01-28 17:10:37 +01:00
|
|
|
_mm_store_ps(&dst[n], in[0]);
|
|
|
|
|
}
|
|
|
|
|
for (; n < n_samples; n++) {
|
2022-07-10 21:59:05 +02:00
|
|
|
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]));
|
2019-01-28 17:10:37 +01:00
|
|
|
_mm_store_ss(&dst[n], in[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-07-10 21:59:05 +02:00
|
|
|
static void mix_c(float *dst, float *src[], uint32_t n_src, bool aligned, uint32_t n_samples)
|
2019-01-28 17:10:37 +01:00
|
|
|
{
|
2022-07-10 21:59:05 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2019-01-28 17:10:37 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void jack_get_version(int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto_ptr)
|
|
|
|
|
{
|
2020-02-24 11:25:31 +01:00
|
|
|
if (major_ptr)
|
2020-09-11 11:28:04 +02:00
|
|
|
*major_ptr = 3;
|
2020-02-24 11:25:31 +01:00
|
|
|
if (minor_ptr)
|
|
|
|
|
*minor_ptr = 0;
|
|
|
|
|
if (micro_ptr)
|
|
|
|
|
*micro_ptr = 0;
|
|
|
|
|
if (proto_ptr)
|
|
|
|
|
*proto_ptr = 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
#define do_callback_expr(c,expr,callback,do_emit,...) \
|
2021-06-17 10:46:04 +02:00
|
|
|
({ \
|
2023-05-11 18:21:57 +02:00
|
|
|
if (c->callback && do_emit) { \
|
2021-06-17 10:46:04 +02:00
|
|
|
pw_thread_loop_unlock(c->context.loop); \
|
2021-09-08 13:02:04 +02:00
|
|
|
if (c->locked_process) \
|
|
|
|
|
pthread_mutex_lock(&c->rt_lock); \
|
2021-09-11 13:07:10 +02:00
|
|
|
(expr); \
|
2021-09-16 11:29:13 +02:00
|
|
|
pw_log_debug("emit " #callback); \
|
2021-06-17 10:46:04 +02:00
|
|
|
c->callback(__VA_ARGS__); \
|
2021-09-08 13:02:04 +02:00
|
|
|
if (c->locked_process) \
|
|
|
|
|
pthread_mutex_unlock(&c->rt_lock); \
|
2021-06-17 10:46:04 +02:00
|
|
|
pw_thread_loop_lock(c->context.loop); \
|
2021-09-11 13:07:10 +02:00
|
|
|
} else { \
|
2023-05-02 14:41:46 +02:00
|
|
|
(expr); \
|
2023-05-10 12:10:03 +02:00
|
|
|
pw_log_debug("skip " #callback \
|
2023-05-11 18:21:57 +02:00
|
|
|
" cb:%p do_emit:%d", c->callback, \
|
|
|
|
|
do_emit); \
|
2021-06-17 10:46:04 +02:00
|
|
|
} \
|
|
|
|
|
})
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
#define do_callback(c,callback,do_emit,...) do_callback_expr(c,(void)0,callback,do_emit,__VA_ARGS__)
|
2021-09-11 13:07:10 +02:00
|
|
|
|
2021-06-17 10:46:04 +02:00
|
|
|
#define do_rt_callback_res(c,callback,...) \
|
|
|
|
|
({ \
|
|
|
|
|
int res = 0; \
|
|
|
|
|
if (c->callback) { \
|
|
|
|
|
if (pthread_mutex_trylock(&c->rt_lock) == 0) { \
|
2022-02-10 17:13:48 +01:00
|
|
|
c->rt_locked = true; \
|
2021-06-17 10:46:04 +02:00
|
|
|
res = c->callback(__VA_ARGS__); \
|
2022-02-10 17:13:48 +01:00
|
|
|
c->rt_locked = false; \
|
2021-06-17 10:46:04 +02:00
|
|
|
pthread_mutex_unlock(&c->rt_lock); \
|
2022-08-08 19:29:09 +02:00
|
|
|
} else { \
|
|
|
|
|
pw_log_debug("skip " #callback \
|
|
|
|
|
" cb:%p", c->callback); \
|
2021-06-17 10:46:04 +02:00
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
res; \
|
|
|
|
|
})
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
const char *
|
|
|
|
|
jack_get_version_string(void)
|
|
|
|
|
{
|
2020-09-11 11:28:04 +02:00
|
|
|
static char name[1024];
|
2021-02-02 12:09:29 +01:00
|
|
|
snprintf(name, sizeof(name), "3.0.0.0 (using PipeWire %s)", pw_get_library_version());
|
2020-09-11 11:28:04 +02:00
|
|
|
return name;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-10 12:10:03 +02:00
|
|
|
#define freeze_callbacks(c) \
|
|
|
|
|
({ \
|
2023-05-10 16:46:42 +02:00
|
|
|
(c)->frozen_callbacks++; \
|
2023-05-10 12:10:03 +02:00
|
|
|
})
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
#define check_callbacks(c) \
|
|
|
|
|
({ \
|
|
|
|
|
if ((c)->frozen_callbacks == 0 && (c)->pending_callbacks) \
|
|
|
|
|
pw_loop_signal_event((c)->context.l, (c)->notify_source); \
|
|
|
|
|
})
|
2023-05-10 12:10:03 +02:00
|
|
|
#define thaw_callbacks(c) \
|
|
|
|
|
({ \
|
2023-05-10 16:46:42 +02:00
|
|
|
(c)->frozen_callbacks--; \
|
2023-05-11 18:21:57 +02:00
|
|
|
check_callbacks(c); \
|
2023-05-10 12:10:03 +02:00
|
|
|
})
|
|
|
|
|
|
2023-05-02 14:41:46 +02:00
|
|
|
static void emit_callbacks(struct client *c)
|
|
|
|
|
{
|
2023-05-11 18:21:57 +02:00
|
|
|
struct object *o;
|
|
|
|
|
int32_t avail;
|
|
|
|
|
uint32_t index;
|
|
|
|
|
struct notify *notify;
|
2023-08-23 17:28:24 +02:00
|
|
|
bool do_graph = false, do_recompute_capture = false, do_recompute_playback = false;
|
2023-05-02 14:41:46 +02:00
|
|
|
|
2023-05-10 16:46:42 +02:00
|
|
|
if (c->frozen_callbacks != 0 || !c->pending_callbacks)
|
2023-05-10 12:10:03 +02:00
|
|
|
return;
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
pw_log_debug("%p: enter active:%u", c, c->active);
|
2023-05-10 16:46:42 +02:00
|
|
|
|
2023-05-10 12:10:03 +02:00
|
|
|
c->pending_callbacks = false;
|
|
|
|
|
|
|
|
|
|
freeze_callbacks(c);
|
2023-05-11 18:21:57 +02:00
|
|
|
|
|
|
|
|
avail = spa_ringbuffer_get_read_index(&c->notify_ring, &index);
|
|
|
|
|
while (avail > 0) {
|
|
|
|
|
notify = SPA_PTROFF(c->notify_buffer, index & NOTIFY_BUFFER_MASK, struct notify);
|
|
|
|
|
|
|
|
|
|
o = notify->object;
|
2023-06-19 09:31:17 +02:00
|
|
|
pw_log_debug("%p: dequeue notify index:%08x %p type:%d %p arg1:%d", c,
|
2023-05-11 18:21:57 +02:00
|
|
|
index, notify, notify->type, o, notify->arg1);
|
|
|
|
|
|
|
|
|
|
switch (notify->type) {
|
|
|
|
|
case NOTIFY_TYPE_REGISTRATION:
|
2023-05-12 15:25:38 +02:00
|
|
|
if (o->registered == notify->arg1)
|
|
|
|
|
break;
|
2023-05-11 18:21:57 +02:00
|
|
|
pw_log_debug("%p: node %u %s %u", c, o->serial,
|
|
|
|
|
o->node.name, notify->arg1);
|
|
|
|
|
do_callback(c, registration_callback, true,
|
|
|
|
|
o->node.name,
|
|
|
|
|
notify->arg1,
|
|
|
|
|
c->registration_arg);
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_PORTREGISTRATION:
|
2023-05-12 15:25:38 +02:00
|
|
|
if (o->registered == notify->arg1)
|
|
|
|
|
break;
|
2023-05-11 18:21:57 +02:00
|
|
|
pw_log_debug("%p: port %u %s %u", c, o->serial,
|
|
|
|
|
o->port.name, notify->arg1);
|
|
|
|
|
do_callback(c, portregistration_callback, c->active,
|
|
|
|
|
o->serial,
|
|
|
|
|
notify->arg1,
|
|
|
|
|
c->portregistration_arg);
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_CONNECT:
|
2023-05-12 15:25:38 +02:00
|
|
|
if (o->registered == notify->arg1)
|
|
|
|
|
break;
|
2023-05-11 18:21:57 +02:00
|
|
|
pw_log_debug("%p: link %u %u -> %u %u", c, o->serial,
|
|
|
|
|
o->port_link.src_serial,
|
|
|
|
|
o->port_link.dst, notify->arg1);
|
|
|
|
|
do_callback(c, connect_callback, c->active,
|
|
|
|
|
o->port_link.src_serial,
|
|
|
|
|
o->port_link.dst_serial,
|
|
|
|
|
notify->arg1,
|
|
|
|
|
c->connect_arg);
|
2023-08-03 11:43:42 +02:00
|
|
|
|
|
|
|
|
do_graph = true;
|
2023-08-23 17:28:24 +02:00
|
|
|
do_recompute_capture = do_recompute_playback = true;
|
2023-05-11 18:21:57 +02:00
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_BUFFER_FRAMES:
|
|
|
|
|
pw_log_debug("%p: buffer frames %d", c, notify->arg1);
|
|
|
|
|
if (c->buffer_frames != (uint32_t)notify->arg1) {
|
|
|
|
|
do_callback_expr(c, c->buffer_frames = notify->arg1,
|
|
|
|
|
bufsize_callback, c->active,
|
|
|
|
|
notify->arg1, c->bufsize_arg);
|
2023-08-23 17:28:24 +02:00
|
|
|
do_recompute_capture = do_recompute_playback = true;
|
2023-05-11 18:21:57 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_SAMPLE_RATE:
|
|
|
|
|
pw_log_debug("%p: sample rate %d", c, notify->arg1);
|
|
|
|
|
if (c->sample_rate != (uint32_t)notify->arg1) {
|
|
|
|
|
do_callback_expr(c, c->sample_rate = notify->arg1,
|
|
|
|
|
srate_callback, c->active,
|
|
|
|
|
notify->arg1, c->srate_arg);
|
2023-05-02 14:41:46 +02:00
|
|
|
}
|
2023-05-11 18:21:57 +02:00
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_FREEWHEEL:
|
|
|
|
|
pw_log_debug("%p: freewheel %d", c, notify->arg1);
|
|
|
|
|
do_callback(c, freewheel_callback, c->active,
|
|
|
|
|
notify->arg1, c->freewheel_arg);
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_SHUTDOWN:
|
|
|
|
|
pw_log_debug("%p: shutdown %d %s", c, notify->arg1, notify->msg);
|
|
|
|
|
if (c->info_shutdown_callback)
|
|
|
|
|
do_callback(c, info_shutdown_callback, c->active,
|
|
|
|
|
notify->arg1, notify->msg,
|
|
|
|
|
c->info_shutdown_arg);
|
|
|
|
|
else
|
|
|
|
|
do_callback(c, shutdown_callback, c->active, c->shutdown_arg);
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_LATENCY:
|
|
|
|
|
pw_log_debug("%p: latency %d", c, notify->arg1);
|
2023-08-23 17:28:24 +02:00
|
|
|
if (notify->arg1 == JackCaptureLatency)
|
|
|
|
|
do_recompute_capture = true;
|
|
|
|
|
else if (notify->arg1 == JackPlaybackLatency)
|
|
|
|
|
do_recompute_playback = true;
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_TOTAL_LATENCY:
|
|
|
|
|
pw_log_debug("%p: total latency", c);
|
|
|
|
|
do_recompute_capture = do_recompute_playback = true;
|
2023-05-11 18:21:57 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2023-05-02 14:41:46 +02:00
|
|
|
}
|
2023-05-12 15:25:38 +02:00
|
|
|
if (o != NULL) {
|
|
|
|
|
o->registered = notify->arg1;
|
|
|
|
|
if (notify->arg1 == 0 && o->removing) {
|
|
|
|
|
o->removing = false;
|
|
|
|
|
free_object(c, o);
|
|
|
|
|
}
|
2023-05-02 14:41:46 +02:00
|
|
|
}
|
2023-05-11 18:21:57 +02:00
|
|
|
avail -= sizeof(struct notify);
|
|
|
|
|
index += sizeof(struct notify);
|
|
|
|
|
spa_ringbuffer_read_update(&c->notify_ring, index);
|
2023-05-02 14:41:46 +02:00
|
|
|
}
|
2023-08-23 17:28:24 +02:00
|
|
|
if (do_recompute_capture)
|
|
|
|
|
do_callback(c, latency_callback, c->active, JackCaptureLatency, c->latency_arg);
|
|
|
|
|
if (do_recompute_playback)
|
|
|
|
|
do_callback(c, latency_callback, c->active, JackPlaybackLatency, c->latency_arg);
|
|
|
|
|
if (do_graph)
|
2023-08-03 11:43:42 +02:00
|
|
|
do_callback(c, graph_callback, c->active, c->graph_arg);
|
2023-08-23 17:28:24 +02:00
|
|
|
|
2023-05-10 12:10:03 +02:00
|
|
|
thaw_callbacks(c);
|
2023-05-10 16:46:42 +02:00
|
|
|
pw_log_debug("%p: leave", c);
|
2023-05-02 14:41:46 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
static int queue_notify(struct client *c, int type, struct object *o, int arg1, const char *msg)
|
|
|
|
|
{
|
|
|
|
|
int32_t filled;
|
|
|
|
|
uint32_t index;
|
|
|
|
|
struct notify *notify;
|
2023-06-19 09:31:17 +02:00
|
|
|
bool emit = false;
|
2023-08-23 17:28:24 +02:00
|
|
|
int res = 0;
|
2023-05-11 18:21:57 +02:00
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case NOTIFY_TYPE_REGISTRATION:
|
|
|
|
|
emit = c->registration_callback != NULL && o != NULL;
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_PORTREGISTRATION:
|
|
|
|
|
emit = c->portregistration_callback != NULL && o != NULL;
|
2023-08-07 19:57:01 +02:00
|
|
|
o->visible = arg1;
|
2023-05-11 18:21:57 +02:00
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_CONNECT:
|
|
|
|
|
emit = c->connect_callback != NULL && o != NULL;
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_BUFFER_FRAMES:
|
|
|
|
|
emit = c->bufsize_callback != NULL;
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_SAMPLE_RATE:
|
|
|
|
|
emit = c->srate_callback != NULL;
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_FREEWHEEL:
|
|
|
|
|
emit = c->freewheel_callback != NULL;
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_SHUTDOWN:
|
|
|
|
|
emit = c->info_shutdown_callback != NULL || c->shutdown_callback != NULL;
|
|
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_LATENCY:
|
2023-08-23 17:28:24 +02:00
|
|
|
case NOTIFY_TYPE_TOTAL_LATENCY:
|
2023-05-11 18:21:57 +02:00
|
|
|
emit = c->latency_callback != NULL;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-06-19 09:31:17 +02:00
|
|
|
if (!emit || ((type & NOTIFY_ACTIVE_FLAG) && !c->active)) {
|
2023-05-18 09:59:26 +02:00
|
|
|
switch (type) {
|
|
|
|
|
case NOTIFY_TYPE_BUFFER_FRAMES:
|
2023-08-23 17:28:24 +02:00
|
|
|
if (!emit) {
|
2023-06-19 09:31:17 +02:00
|
|
|
c->buffer_frames = arg1;
|
2023-08-23 17:28:24 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_TOTAL_LATENCY, NULL, 0, NULL);
|
|
|
|
|
}
|
2023-05-18 09:59:26 +02:00
|
|
|
break;
|
|
|
|
|
case NOTIFY_TYPE_SAMPLE_RATE:
|
2023-06-19 09:31:17 +02:00
|
|
|
if (!emit)
|
|
|
|
|
c->sample_rate = arg1;
|
2023-05-18 09:59:26 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2023-06-19 09:31:17 +02:00
|
|
|
pw_log_debug("%p: skip notify %08x active:%d emit:%d", c, type,
|
|
|
|
|
c->active, emit);
|
2023-08-30 18:23:07 +02:00
|
|
|
if (o != NULL) {
|
|
|
|
|
o->registered = arg1;
|
|
|
|
|
if (arg1 == 0 && o->removing) {
|
|
|
|
|
o->removing = false;
|
|
|
|
|
free_object(c, o);
|
|
|
|
|
}
|
2023-05-11 18:21:57 +02:00
|
|
|
}
|
2023-08-23 17:28:24 +02:00
|
|
|
return res;
|
2023-05-11 18:21:57 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-23 17:28:24 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2023-05-11 18:21:57 +02:00
|
|
|
filled = spa_ringbuffer_get_write_index(&c->notify_ring, &index);
|
|
|
|
|
if (filled < 0 || filled + sizeof(struct notify) > NOTIFY_BUFFER_SIZE) {
|
|
|
|
|
pw_log_warn("%p: notify queue full %d", c, type);
|
2023-08-23 17:28:24 +02:00
|
|
|
res = -ENOSPC;
|
|
|
|
|
goto done;
|
2023-05-11 18:21:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
notify = SPA_PTROFF(c->notify_buffer, index & NOTIFY_BUFFER_MASK, struct notify);
|
|
|
|
|
notify->type = type;
|
|
|
|
|
notify->object = o;
|
|
|
|
|
notify->arg1 = arg1;
|
|
|
|
|
notify->msg = msg;
|
2023-06-19 09:31:17 +02:00
|
|
|
pw_log_debug("%p: queue notify index:%08x %p type:%d %p arg1:%d msg:%s", c,
|
2023-05-11 18:21:57 +02:00
|
|
|
index, notify, notify->type, o, notify->arg1, notify->msg);
|
|
|
|
|
index += sizeof(struct notify);
|
|
|
|
|
spa_ringbuffer_write_update(&c->notify_ring, index);
|
|
|
|
|
c->pending_callbacks = true;
|
|
|
|
|
check_callbacks(c);
|
2023-08-23 17:28:24 +02:00
|
|
|
done:
|
|
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
|
|
|
|
return res;
|
2023-05-11 18:21:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void on_notify_event(void *data, uint64_t count)
|
2023-05-02 14:41:46 +02:00
|
|
|
{
|
|
|
|
|
struct client *c = data;
|
|
|
|
|
emit_callbacks(c);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-06 11:48:40 +01:00
|
|
|
static void on_sync_reply(void *data, uint32_t id, int seq)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
|
|
|
|
struct client *client = data;
|
2020-10-27 16:46:12 +01:00
|
|
|
if (id != PW_ID_CORE)
|
2019-12-06 11:48:40 +01:00
|
|
|
return;
|
|
|
|
|
client->last_sync = seq;
|
2021-09-07 17:13:15 +02:00
|
|
|
if (client->pending_sync == seq)
|
|
|
|
|
pw_thread_loop_signal(client->context.loop, false);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-06 11:48:40 +01:00
|
|
|
static void on_error(void *data, uint32_t id, int seq, int res, const char *message)
|
2019-09-02 12:02:34 +02:00
|
|
|
{
|
|
|
|
|
struct client *client = data;
|
2019-12-06 11:48:40 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_warn("%p: error id:%u seq:%d res:%d (%s): %s", client,
|
2019-12-06 11:48:40 +01:00
|
|
|
id, seq, res, spa_strerror(res), message);
|
|
|
|
|
|
2020-02-24 11:25:31 +01:00
|
|
|
if (id == PW_ID_CORE) {
|
|
|
|
|
client->last_res = res;
|
2023-05-11 18:21:57 +02:00
|
|
|
if (res == -EPIPE && !client->destroyed) {
|
|
|
|
|
queue_notify(client, NOTIFY_TYPE_SHUTDOWN,
|
|
|
|
|
NULL, JackFailure | JackServerError,
|
|
|
|
|
"JACK server has been closed");
|
|
|
|
|
}
|
2019-12-06 11:48:40 +01:00
|
|
|
}
|
2019-09-02 12:02:34 +02:00
|
|
|
pw_thread_loop_signal(client->context.loop, false);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-11 07:46:59 +01:00
|
|
|
static const struct pw_core_events core_events = {
|
|
|
|
|
PW_VERSION_CORE_EVENTS,
|
2019-01-10 09:31:00 +01:00
|
|
|
.done = on_sync_reply,
|
2019-12-06 11:48:40 +01:00
|
|
|
.error = on_error,
|
2019-01-10 09:31:00 +01:00
|
|
|
};
|
|
|
|
|
|
2018-02-09 18:23:36 +01:00
|
|
|
static int do_sync(struct client *client)
|
|
|
|
|
{
|
2022-02-10 17:13:48 +01:00
|
|
|
bool in_data_thread = pw_data_loop_in_thread(client->loop);
|
|
|
|
|
|
2021-06-17 10:46:04 +02:00
|
|
|
if (pw_thread_loop_in_thread(client->context.loop)) {
|
|
|
|
|
pw_log_warn("sync requested from callback");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2022-08-11 11:04:50 +02:00
|
|
|
if (client->last_res == -EPIPE)
|
|
|
|
|
return -EPIPE;
|
2021-06-17 10:46:04 +02:00
|
|
|
|
2022-03-26 21:35:28 +01:00
|
|
|
client->last_res = 0;
|
2021-09-07 17:13:15 +02:00
|
|
|
client->pending_sync = pw_proxy_sync((struct pw_proxy*)client->core, client->pending_sync);
|
2023-05-11 18:21:57 +02:00
|
|
|
if (client->pending_sync < 0)
|
|
|
|
|
return client->pending_sync;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
|
|
|
|
while (true) {
|
2022-04-26 20:15:58 +02:00
|
|
|
if (in_data_thread) {
|
|
|
|
|
if (client->rt_locked)
|
|
|
|
|
pthread_mutex_unlock(&client->rt_lock);
|
|
|
|
|
client->data_locked = true;
|
|
|
|
|
}
|
2018-02-13 18:05:55 +01:00
|
|
|
pw_thread_loop_wait(client->context.loop);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2022-04-26 20:15:58 +02:00
|
|
|
if (in_data_thread) {
|
|
|
|
|
client->data_locked = false;
|
|
|
|
|
if (client->rt_locked)
|
|
|
|
|
pthread_mutex_lock(&client->rt_lock);
|
|
|
|
|
}
|
2022-02-10 17:13:48 +01:00
|
|
|
|
2022-03-26 21:35:28 +01:00
|
|
|
if (client->last_res < 0)
|
2020-02-24 11:25:31 +01:00
|
|
|
return client->last_res;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2021-09-07 17:13:15 +02:00
|
|
|
if (client->pending_sync == client->last_sync)
|
2018-02-09 18:23:36 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-04 12:59:20 +02:00
|
|
|
static void on_node_removed(void *data)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
|
|
|
|
struct client *client = data;
|
2020-06-04 12:59:20 +02:00
|
|
|
pw_proxy_destroy((struct pw_proxy*)client->node);
|
|
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2020-06-04 12:59:20 +02:00
|
|
|
static void on_node_destroy(void *data)
|
|
|
|
|
{
|
|
|
|
|
struct client *client = data;
|
2019-12-11 16:13:46 +01:00
|
|
|
client->node = NULL;
|
2018-02-09 18:23:36 +01:00
|
|
|
spa_hook_remove(&client->proxy_listener);
|
2022-02-09 15:52:14 +01:00
|
|
|
spa_hook_remove(&client->node_listener);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-21 17:22:27 +01:00
|
|
|
static void on_node_bound_props(void *data, uint32_t global_id, const struct spa_dict *props)
|
2019-11-28 13:24:09 +01:00
|
|
|
{
|
|
|
|
|
struct client *client = data;
|
|
|
|
|
client->node_id = global_id;
|
2023-03-21 17:22:27 +01:00
|
|
|
if (props)
|
|
|
|
|
pw_properties_update(client->props, props);
|
2019-11-28 13:24:09 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-04 12:59:20 +02:00
|
|
|
static const struct pw_proxy_events node_proxy_events = {
|
2018-02-09 18:23:36 +01:00
|
|
|
PW_VERSION_PROXY_EVENTS,
|
2020-06-04 12:59:20 +02:00
|
|
|
.removed = on_node_removed,
|
2019-12-11 16:13:46 +01:00
|
|
|
.destroy = on_node_destroy,
|
2023-03-21 17:22:27 +01:00
|
|
|
.bound_props = on_node_bound_props,
|
2018-02-09 18:23:36 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-07 16:45:58 +01:00
|
|
|
static struct link *find_activation(struct spa_list *links, uint32_t node_id)
|
2019-02-12 17:40:46 +01:00
|
|
|
{
|
|
|
|
|
struct link *l;
|
|
|
|
|
|
2020-02-07 16:45:58 +01:00
|
|
|
spa_list_for_each(l, links, link) {
|
2019-02-12 17:40:46 +01:00
|
|
|
if (l->node_id == node_id)
|
|
|
|
|
return l;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-26 20:15:58 +02:00
|
|
|
static void client_remove_source(struct client *c)
|
2018-02-13 18:05:55 +01:00
|
|
|
{
|
|
|
|
|
if (c->socket_source) {
|
2023-07-11 19:31:27 +02:00
|
|
|
pw_loop_destroy_source(c->l, c->socket_source);
|
2018-02-13 18:05:55 +01:00
|
|
|
c->socket_source = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-30 11:59:41 +02:00
|
|
|
static inline void reuse_buffer(struct client *c, struct mix *mix, uint32_t id)
|
2018-02-13 18:05:55 +01:00
|
|
|
{
|
2018-05-21 13:12:26 +02:00
|
|
|
struct buffer *b;
|
|
|
|
|
|
|
|
|
|
b = &mix->buffers[id];
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2019-10-02 17:54:49 +02:00
|
|
|
if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_trace_fp("%p: port %p: recycle buffer %d", c, mix->port, id);
|
2018-05-21 13:12:26 +02:00
|
|
|
spa_list_append(&mix->queue, &b->link);
|
2019-10-02 17:54:49 +02:00
|
|
|
SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
|
2018-02-13 18:05:55 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-03 20:13:57 +02:00
|
|
|
|
2021-09-20 12:22:58 +02:00
|
|
|
static size_t convert_from_midi(void *midi, void *buffer, size_t size)
|
2018-10-03 20:13:57 +02:00
|
|
|
{
|
|
|
|
|
struct spa_pod_builder b = { 0, };
|
|
|
|
|
uint32_t i, count;
|
2019-01-22 17:37:00 +01:00
|
|
|
struct spa_pod_frame f;
|
2018-10-03 20:13:57 +02:00
|
|
|
|
|
|
|
|
count = jack_midi_get_event_count(midi);
|
|
|
|
|
|
|
|
|
|
spa_pod_builder_init(&b, buffer, size);
|
2019-01-22 17:37:00 +01:00
|
|
|
spa_pod_builder_push_sequence(&b, &f, 0);
|
2018-10-03 20:13:57 +02:00
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
|
jack_midi_event_t ev;
|
|
|
|
|
jack_midi_event_get(&ev, midi, i);
|
2019-01-17 17:06:47 +01:00
|
|
|
spa_pod_builder_control(&b, ev.time, SPA_CONTROL_Midi);
|
2018-10-03 20:13:57 +02:00
|
|
|
spa_pod_builder_bytes(&b, ev.buffer, ev.size);
|
|
|
|
|
}
|
2021-09-20 12:22:58 +02:00
|
|
|
spa_pod_builder_pop(&b, &f);
|
|
|
|
|
return b.state.offset;
|
2018-10-03 20:13:57 +02:00
|
|
|
}
|
|
|
|
|
|
2022-06-20 15:51:33 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-13 11:11:30 +01:00
|
|
|
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;
|
2022-12-13 16:41:20 +01:00
|
|
|
return priotab[(db[0]>>4) & 7] - priotab[(da[0]>>4) & 7];
|
2022-12-13 11:11:30 +01:00
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-20 15:51:33 +02:00
|
|
|
static void convert_to_midi(struct spa_pod_sequence **seq, uint32_t n_seq, void *midi, bool fix)
|
2018-10-29 14:29:59 +00:00
|
|
|
{
|
2019-09-26 11:56:25 +02:00
|
|
|
struct spa_pod_control *c[n_seq];
|
|
|
|
|
uint32_t i;
|
2022-06-20 15:51:33 +02:00
|
|
|
int res;
|
2018-10-29 14:29:59 +00:00
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
for (i = 0; i < n_seq; i++)
|
2019-09-26 11:56:25 +02:00
|
|
|
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;
|
|
|
|
|
|
2022-12-13 11:11:30 +01:00
|
|
|
if (next == NULL || event_sort(c[i], next) <= 0) {
|
2019-09-26 11:56:25 +02:00
|
|
|
next = c[i];
|
|
|
|
|
next_index = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(next == NULL))
|
2019-09-26 11:56:25 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
switch(next->type) {
|
2018-10-29 14:29:59 +00:00
|
|
|
case SPA_CONTROL_Midi:
|
2022-12-13 11:11:30 +01:00
|
|
|
{
|
|
|
|
|
uint8_t *data = SPA_POD_BODY(&next->value);
|
|
|
|
|
size_t size = SPA_POD_BODY_SIZE(&next->value);
|
|
|
|
|
|
2022-06-20 15:51:33 +02:00
|
|
|
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));
|
2018-10-29 14:29:59 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2022-12-13 11:11:30 +01:00
|
|
|
}
|
2019-09-26 11:56:25 +02:00
|
|
|
c[next_index] = spa_pod_control_next(c[next_index]);
|
2018-10-29 14:29:59 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-26 11:56:25 +02:00
|
|
|
|
2021-09-20 12:22:58 +02:00
|
|
|
static inline void *get_buffer_output(struct port *p, uint32_t frames, uint32_t stride, struct buffer **buf)
|
2019-09-16 21:08:35 +02:00
|
|
|
{
|
|
|
|
|
struct mix *mix;
|
2020-09-30 11:59:41 +02:00
|
|
|
struct client *c = p->client;
|
2019-09-16 21:08:35 +02:00
|
|
|
void *ptr = NULL;
|
2021-10-26 12:28:31 +02:00
|
|
|
struct buffer *b;
|
2021-11-25 15:17:42 +01:00
|
|
|
struct spa_data *d;
|
2019-09-16 21:08:35 +02:00
|
|
|
|
2022-10-20 21:34:01 +02:00
|
|
|
if (frames == 0 || !p->valid)
|
2020-08-11 12:09:50 +02:00
|
|
|
return NULL;
|
|
|
|
|
|
2021-10-26 12:28:31 +02:00
|
|
|
if (SPA_UNLIKELY((mix = p->global_mix) == NULL))
|
|
|
|
|
return NULL;
|
2019-09-16 21:08:35 +02:00
|
|
|
|
2021-12-07 15:27:18 +01:00
|
|
|
pw_log_trace_fp("%p: port %s %d get buffer %d n_buffers:%d",
|
2022-01-22 23:29:45 +01:00
|
|
|
c, p->object->port.name, p->port_id, frames, mix->n_buffers);
|
2019-10-22 09:17:38 +02:00
|
|
|
|
2021-10-26 12:28:31 +02:00
|
|
|
if (SPA_UNLIKELY(mix->n_buffers == 0))
|
|
|
|
|
return NULL;
|
2019-09-16 21:08:35 +02:00
|
|
|
|
2021-10-26 12:28:31 +02:00
|
|
|
if (p->io.status == SPA_STATUS_HAVE_DATA &&
|
|
|
|
|
p->io.buffer_id < mix->n_buffers) {
|
|
|
|
|
b = &mix->buffers[p->io.buffer_id];
|
2021-11-25 15:17:42 +01:00
|
|
|
d = &b->datas[0];
|
2021-10-26 12:28:31 +02:00
|
|
|
} 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)) {
|
2019-09-16 21:08:35 +02:00
|
|
|
pw_log_warn("port %p: out of buffers", p);
|
2021-10-26 12:28:31 +02:00
|
|
|
return NULL;
|
2019-09-16 21:08:35 +02:00
|
|
|
}
|
2021-11-25 15:17:42 +01:00
|
|
|
d = &b->datas[0];
|
|
|
|
|
d->chunk->offset = 0;
|
|
|
|
|
d->chunk->size = frames * sizeof(float);
|
|
|
|
|
d->chunk->stride = stride;
|
2019-09-16 21:08:35 +02:00
|
|
|
|
|
|
|
|
p->io.status = SPA_STATUS_HAVE_DATA;
|
|
|
|
|
p->io.buffer_id = b->id;
|
|
|
|
|
}
|
2021-11-25 15:17:42 +01:00
|
|
|
ptr = d->data;
|
2021-10-26 12:28:31 +02:00
|
|
|
if (buf)
|
|
|
|
|
*buf = b;
|
2019-09-16 21:08:35 +02:00
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-26 12:28:31 +02:00
|
|
|
static inline void process_empty(struct port *p, uint32_t frames)
|
2018-10-03 20:13:57 +02:00
|
|
|
{
|
2023-08-25 14:28:38 +02:00
|
|
|
void *ptr, *src = p->emptyptr;
|
|
|
|
|
struct port *tied = p->tied;
|
|
|
|
|
|
|
|
|
|
if (SPA_UNLIKELY(tied != NULL)) {
|
|
|
|
|
if ((src = tied->get_buffer(tied, frames)) == NULL)
|
|
|
|
|
src = p->emptyptr;
|
|
|
|
|
}
|
2018-10-03 20:13:57 +02:00
|
|
|
|
2021-10-26 12:28:31 +02:00
|
|
|
switch (p->object->port.type_id) {
|
|
|
|
|
case TYPE_ID_AUDIO:
|
|
|
|
|
ptr = get_buffer_output(p, frames, sizeof(float), NULL);
|
|
|
|
|
if (SPA_LIKELY(ptr != NULL))
|
2023-08-25 14:28:38 +02:00
|
|
|
memcpy(ptr, src, frames * sizeof(float));
|
2021-10-26 12:28:31 +02:00
|
|
|
break;
|
|
|
|
|
case TYPE_ID_MIDI:
|
|
|
|
|
{
|
|
|
|
|
struct buffer *b;
|
|
|
|
|
ptr = get_buffer_output(p, MAX_BUFFER_FRAMES, 1, &b);
|
2023-08-25 14:28:38 +02:00
|
|
|
if (SPA_LIKELY(ptr != NULL))
|
|
|
|
|
b->datas[0].chunk->size = convert_from_midi(src,
|
2021-10-26 12:28:31 +02:00
|
|
|
ptr, MAX_BUFFER_FRAMES * sizeof(float));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
pw_log_warn("port %p: unhandled format %d", p, p->object->port.type_id);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-29 10:29:26 +01:00
|
|
|
|
2021-12-07 15:27:18 +01:00
|
|
|
static void prepare_output(struct port *p, uint32_t frames)
|
|
|
|
|
{
|
|
|
|
|
struct mix *mix;
|
|
|
|
|
|
2023-08-25 14:28:38 +02:00
|
|
|
if (SPA_UNLIKELY(p->empty_out || p->tied))
|
2021-12-07 15:27:18 +01:00
|
|
|
process_empty(p, frames);
|
|
|
|
|
|
|
|
|
|
spa_list_for_each(mix, &p->mix, port_link) {
|
|
|
|
|
if (SPA_LIKELY(mix->io != NULL))
|
|
|
|
|
*mix->io = p->io;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-26 12:28:31 +02:00
|
|
|
static void complete_process(struct client *c, uint32_t frames)
|
|
|
|
|
{
|
|
|
|
|
struct port *p;
|
|
|
|
|
struct mix *mix;
|
2022-01-22 20:55:40 +01:00
|
|
|
union pw_map_item *item;
|
2020-01-29 10:29:26 +01:00
|
|
|
|
2023-08-25 14:28:38 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2022-01-22 20:55:40 +01:00
|
|
|
pw_array_for_each(item, &c->ports[SPA_DIRECTION_INPUT].items) {
|
|
|
|
|
if (pw_map_item_is_free(item))
|
|
|
|
|
continue;
|
|
|
|
|
p = item->data;
|
2022-12-02 09:46:05 +01:00
|
|
|
if (!p->valid)
|
|
|
|
|
continue;
|
2021-10-26 12:28:31 +02:00
|
|
|
spa_list_for_each(mix, &p->mix, port_link) {
|
|
|
|
|
if (SPA_LIKELY(mix->io != NULL))
|
|
|
|
|
mix->io->status = SPA_STATUS_NEED_DATA;
|
2021-09-20 12:22:58 +02:00
|
|
|
}
|
2022-01-22 20:55:40 +01:00
|
|
|
}
|
2018-10-03 20:13:57 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-27 14:41:15 +02:00
|
|
|
static inline void debug_position(struct client *c, jack_position_t *p)
|
|
|
|
|
{
|
2020-05-08 20:40:02 +02:00
|
|
|
pw_log_trace("usecs: %"PRIu64, p->usecs);
|
2019-08-27 14:41:15 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-29 18:20:40 +02:00
|
|
|
static inline void jack_to_position(jack_position_t *s, struct pw_node_activation *a)
|
2019-08-27 14:41:15 +02:00
|
|
|
{
|
2019-09-02 12:02:34 +02:00
|
|
|
struct spa_io_segment *d = &a->segment;
|
2019-08-27 21:35:28 +02:00
|
|
|
|
2019-08-27 14:41:15 +02:00
|
|
|
if (s->valid & JackPositionBBT) {
|
2019-08-29 18:20:40 +02:00
|
|
|
d->bar.flags = SPA_IO_SEGMENT_BAR_FLAG_VALID;
|
2019-08-27 14:41:15 +02:00
|
|
|
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;
|
2019-08-29 16:13:02 +02:00
|
|
|
d->bar.beat = (s->bar - 1) * s->beats_per_bar + (s->beat - 1) +
|
|
|
|
|
(s->tick / s->ticks_per_beat);
|
2019-08-27 14:41:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-02 12:02:34 +02:00
|
|
|
static inline jack_transport_state_t position_to_jack(struct pw_node_activation *a, jack_position_t *d)
|
2019-08-27 14:41:15 +02:00
|
|
|
{
|
2019-09-02 12:02:34 +02:00
|
|
|
struct spa_io_position *s = &a->position;
|
2019-08-27 21:35:28 +02:00
|
|
|
jack_transport_state_t state;
|
|
|
|
|
struct spa_io_segment *seg = &s->segments[0];
|
2019-08-28 10:27:26 +02:00
|
|
|
uint64_t running;
|
2019-08-27 21:35:28 +02:00
|
|
|
|
2019-08-27 14:41:15 +02:00
|
|
|
switch (s->state) {
|
|
|
|
|
default:
|
|
|
|
|
case SPA_IO_POSITION_STATE_STOPPED:
|
2019-08-27 21:35:28 +02:00
|
|
|
state = JackTransportStopped;
|
2019-08-27 14:41:15 +02:00
|
|
|
break;
|
|
|
|
|
case SPA_IO_POSITION_STATE_STARTING:
|
2019-08-27 21:35:28 +02:00
|
|
|
state = JackTransportStarting;
|
2019-08-27 14:41:15 +02:00
|
|
|
break;
|
|
|
|
|
case SPA_IO_POSITION_STATE_RUNNING:
|
2019-08-27 21:35:28 +02:00
|
|
|
if (seg->flags & SPA_IO_SEGMENT_FLAG_LOOPING)
|
|
|
|
|
state = JackTransportLooping;
|
|
|
|
|
else
|
|
|
|
|
state = JackTransportRolling;
|
2019-08-27 14:41:15 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(d == NULL))
|
2019-08-27 21:35:28 +02:00
|
|
|
return state;
|
|
|
|
|
|
2019-08-27 14:41:15 +02:00
|
|
|
d->unique_1++;
|
|
|
|
|
d->usecs = s->clock.nsec / SPA_NSEC_PER_USEC;
|
|
|
|
|
d->frame_rate = s->clock.rate.denom;
|
|
|
|
|
|
2021-05-25 21:28:35 +00:00
|
|
|
if ((int64_t)s->clock.position < s->offset) {
|
2019-08-27 21:35:28 +02:00
|
|
|
d->frame = seg->position;
|
2021-05-25 21:28:35 +00:00
|
|
|
} 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;
|
|
|
|
|
}
|
2019-08-27 14:41:15 +02:00
|
|
|
d->valid = 0;
|
2019-10-02 17:54:49 +02:00
|
|
|
if (a->segment_owner[0] && SPA_FLAG_IS_SET(seg->bar.flags, SPA_IO_SEGMENT_BAR_FLAG_VALID)) {
|
2019-08-29 16:13:02 +02:00
|
|
|
double abs_beat;
|
|
|
|
|
long beats;
|
2019-08-27 14:41:15 +02:00
|
|
|
|
|
|
|
|
d->valid |= JackPositionBBT;
|
|
|
|
|
|
2019-08-27 21:35:28 +02:00
|
|
|
d->bbt_offset = seg->bar.offset;
|
|
|
|
|
if (seg->bar.offset)
|
2019-08-27 14:41:15 +02:00
|
|
|
d->valid |= JackBBTFrameOffset;
|
|
|
|
|
|
2019-08-27 21:35:28 +02:00
|
|
|
d->beats_per_bar = seg->bar.signature_num;
|
|
|
|
|
d->beat_type = seg->bar.signature_denom;
|
2019-08-27 14:41:15 +02:00
|
|
|
d->ticks_per_beat = 1920.0f;
|
2019-08-27 21:35:28 +02:00
|
|
|
d->beats_per_minute = seg->bar.bpm;
|
2019-08-27 14:41:15 +02:00
|
|
|
|
2019-08-27 21:35:28 +02:00
|
|
|
abs_beat = seg->bar.beat;
|
2019-08-27 14:41:15 +02:00
|
|
|
|
|
|
|
|
d->bar = abs_beat / d->beats_per_bar;
|
2019-08-29 16:13:02 +02:00
|
|
|
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;
|
2019-08-27 14:41:15 +02:00
|
|
|
d->bar++;
|
2019-08-29 16:13:02 +02:00
|
|
|
d->beat++;
|
2019-08-27 14:41:15 +02:00
|
|
|
}
|
|
|
|
|
d->unique_2 = d->unique_1;
|
2019-08-27 21:35:28 +02:00
|
|
|
return state;
|
2019-08-27 14:41:15 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-16 11:29:13 +02:00
|
|
|
static inline int check_buffer_frames(struct client *c, struct spa_io_position *pos)
|
2020-02-24 15:39:19 +01:00
|
|
|
{
|
2020-04-30 16:15:22 +02:00
|
|
|
uint32_t buffer_frames = pos->clock.duration;
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(buffer_frames != c->buffer_frames)) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: bufferframes old:%d new:%d cb:%p", c,
|
2021-09-16 11:29:13 +02:00
|
|
|
c->buffer_frames, buffer_frames, c->bufsize_callback);
|
2022-04-23 21:37:45 +02:00
|
|
|
if (c->buffer_frames != (uint32_t)-1)
|
2023-05-11 18:21:57 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_BUFFER_FRAMES, NULL, buffer_frames, NULL);
|
2022-04-23 21:37:45 +02:00
|
|
|
else
|
|
|
|
|
c->buffer_frames = buffer_frames;
|
2020-02-24 15:39:19 +01:00
|
|
|
}
|
2021-09-11 13:07:10 +02:00
|
|
|
return c->buffer_frames == buffer_frames;
|
2020-02-24 15:39:19 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-11 13:07:10 +02:00
|
|
|
static inline int check_sample_rate(struct client *c, struct spa_io_position *pos)
|
2020-02-24 15:39:19 +01:00
|
|
|
{
|
2020-04-30 16:15:22 +02:00
|
|
|
uint32_t sample_rate = pos->clock.rate.denom;
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(sample_rate != c->sample_rate)) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: sample_rate old:%d new:%d cb:%p", c,
|
2021-09-16 11:29:13 +02:00
|
|
|
c->sample_rate, sample_rate, c->srate_callback);
|
2023-05-18 09:46:40 +02:00
|
|
|
if (c->sample_rate != (uint32_t)-1)
|
2023-05-11 18:21:57 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_SAMPLE_RATE, NULL, sample_rate, NULL);
|
2023-05-18 09:46:40 +02:00
|
|
|
else
|
2021-09-16 11:29:13 +02:00
|
|
|
c->sample_rate = sample_rate;
|
2020-02-24 15:39:19 +01:00
|
|
|
}
|
2021-09-11 13:07:10 +02:00
|
|
|
return c->sample_rate == sample_rate;
|
2020-02-24 15:39:19 +01:00
|
|
|
}
|
|
|
|
|
|
2023-04-10 14:45:27 +02:00
|
|
|
static inline uint64_t get_time_ns(void)
|
|
|
|
|
{
|
|
|
|
|
struct timespec ts;
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
|
|
|
return SPA_TIMESPEC_TO_NSEC(&ts);
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-09 17:16:18 +02:00
|
|
|
static inline uint32_t cycle_run(struct client *c)
|
2018-02-13 18:05:55 +01:00
|
|
|
{
|
2020-01-31 13:35:39 +01:00
|
|
|
uint64_t cmd;
|
2019-09-02 12:02:34 +02:00
|
|
|
int fd = c->socket_source->fd;
|
2020-04-30 16:15:22 +02:00
|
|
|
struct spa_io_position *pos = c->rt.position;
|
2019-09-02 12:02:34 +02:00
|
|
|
struct pw_node_activation *activation = c->activation;
|
2020-04-30 12:57:56 +02:00
|
|
|
struct pw_node_activation *driver = c->rt.driver_activation;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2020-11-02 14:51:07 +01:00
|
|
|
while (true) {
|
|
|
|
|
if (SPA_UNLIKELY(read(fd, &cmd, sizeof(cmd)) != sizeof(cmd))) {
|
2020-11-02 16:43:56 +01:00
|
|
|
if (errno == EINTR)
|
2020-11-02 14:51:07 +01:00
|
|
|
continue;
|
2020-11-02 16:43:56 +01:00
|
|
|
if (errno == EWOULDBLOCK || errno == EAGAIN)
|
2020-11-02 14:51:07 +01:00
|
|
|
return 0;
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_warn("%p: read failed %m", c);
|
2020-11-02 14:51:07 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (SPA_UNLIKELY(cmd > 1))
|
2021-12-07 10:19:10 +01:00
|
|
|
pw_log_info("%p: missed %"PRIu64" wakeups", c, cmd - 1);
|
2019-09-02 12:02:34 +02:00
|
|
|
|
|
|
|
|
activation->status = PW_NODE_ACTIVATION_AWAKE;
|
2023-04-10 14:45:27 +02:00
|
|
|
activation->awake_time = get_time_ns();
|
2020-01-31 13:35:39 +01:00
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(c->first)) {
|
2019-09-03 13:45:13 +02:00
|
|
|
if (c->thread_init_callback)
|
|
|
|
|
c->thread_init_callback(c->thread_init_arg);
|
|
|
|
|
c->first = false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-30 16:15:22 +02:00
|
|
|
if (SPA_UNLIKELY(pos == NULL)) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: missing position", c);
|
2020-04-30 16:15:22 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-16 11:29:13 +02:00
|
|
|
if (check_buffer_frames(c, pos) == 0)
|
2021-09-11 13:07:10 +02:00
|
|
|
return 0;
|
|
|
|
|
if (check_sample_rate(c, pos) == 0)
|
|
|
|
|
return 0;
|
2019-09-02 12:02:34 +02:00
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_LIKELY(driver)) {
|
2020-04-21 16:15:45 +02:00
|
|
|
c->jack_state = position_to_jack(driver, &c->jack_position);
|
|
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(activation->pending_sync)) {
|
2019-09-02 12:02:34 +02:00
|
|
|
if (c->sync_callback == NULL ||
|
|
|
|
|
c->sync_callback(c->jack_state, &c->jack_position, c->sync_arg))
|
|
|
|
|
activation->pending_sync = false;
|
2019-08-22 13:56:39 +02:00
|
|
|
}
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(c->xrun_count != driver->xrun_count &&
|
|
|
|
|
c->xrun_count != 0 && c->xrun_callback))
|
2019-09-02 12:02:34 +02:00
|
|
|
c->xrun_callback(c->xrun_arg);
|
|
|
|
|
c->xrun_count = driver->xrun_count;
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_trace_fp("%p: wait %"PRIu64" frames:%d rate:%d pos:%d delay:%"PRIi64" corr:%f", c,
|
2019-10-24 10:23:01 +02:00
|
|
|
activation->awake_time, c->buffer_frames, c->sample_rate,
|
2019-09-02 12:02:34 +02:00
|
|
|
c->jack_position.frame, pos->clock.delay, pos->clock.rate_diff);
|
|
|
|
|
|
2020-02-24 15:39:19 +01:00
|
|
|
return c->buffer_frames;
|
2019-09-02 12:02:34 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-09 17:16:18 +02:00
|
|
|
static inline uint32_t cycle_wait(struct client *c)
|
|
|
|
|
{
|
|
|
|
|
int res;
|
2021-07-03 16:58:20 +09:00
|
|
|
uint32_t nframes;
|
2019-09-09 17:16:18 +02:00
|
|
|
|
2021-07-03 16:58:20 +09:00
|
|
|
do {
|
|
|
|
|
res = pw_data_loop_wait(c->loop, -1);
|
|
|
|
|
if (SPA_UNLIKELY(res <= 0)) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_warn("%p: wait error %m", c);
|
2021-07-03 16:58:20 +09:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
nframes = cycle_run(c);
|
|
|
|
|
} while (!nframes);
|
|
|
|
|
|
|
|
|
|
return nframes;
|
2019-09-09 17:16:18 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-02 12:02:34 +02:00
|
|
|
static inline void signal_sync(struct client *c)
|
|
|
|
|
{
|
|
|
|
|
uint64_t cmd, nsec;
|
|
|
|
|
struct link *l;
|
|
|
|
|
struct pw_node_activation *activation = c->activation;
|
|
|
|
|
|
2021-10-26 12:28:31 +02:00
|
|
|
complete_process(c, c->buffer_frames);
|
2019-09-02 12:02:34 +02:00
|
|
|
|
2023-04-10 14:45:27 +02:00
|
|
|
nsec = get_time_ns();
|
2019-09-02 12:02:34 +02:00
|
|
|
activation->status = PW_NODE_ACTIVATION_FINISHED;
|
|
|
|
|
activation->finish_time = nsec;
|
|
|
|
|
|
|
|
|
|
cmd = 1;
|
2020-04-30 12:57:56 +02:00
|
|
|
spa_list_for_each(l, &c->rt.target_links, target_link) {
|
2019-09-02 12:02:34 +02:00
|
|
|
struct pw_node_activation_state *state;
|
|
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(l->activation == NULL))
|
2019-09-02 12:02:34 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
state = &l->activation->state[0];
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_trace_fp("%p: link %p %p %d/%d", c, l, state,
|
2019-09-02 12:02:34 +02:00
|
|
|
state->pending, state->required);
|
2019-08-22 13:56:39 +02:00
|
|
|
|
2023-07-13 12:18:11 +02:00
|
|
|
if (pw_node_activation_state_dec(state)) {
|
2019-09-02 12:02:34 +02:00
|
|
|
l->activation->status = PW_NODE_ACTIVATION_TRIGGERED;
|
|
|
|
|
l->activation->signal_time = nsec;
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_trace_fp("%p: signal %p %p", c, l, state);
|
2019-09-02 12:02:34 +02:00
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(write(l->signalfd, &cmd, sizeof(cmd)) != sizeof(cmd)))
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_warn("%p: write failed %m", c);
|
2019-09-02 12:02:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-31 21:42:28 +02:00
|
|
|
|
2019-09-02 12:02:34 +02:00
|
|
|
static inline void cycle_signal(struct client *c, int status)
|
|
|
|
|
{
|
2020-04-30 12:57:56 +02:00
|
|
|
struct pw_node_activation *driver = c->rt.driver_activation;
|
2019-09-02 12:02:34 +02:00
|
|
|
struct pw_node_activation *activation = c->activation;
|
2018-05-21 13:12:26 +02:00
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_LIKELY(status == 0)) {
|
2019-09-02 12:02:34 +02:00
|
|
|
if (c->timebase_callback && driver && driver->segment_owner[0] == c->node_id) {
|
2019-08-29 14:01:20 +02:00
|
|
|
if (activation->pending_new_pos ||
|
2019-09-02 12:02:34 +02:00
|
|
|
c->jack_state == JackTransportRolling ||
|
|
|
|
|
c->jack_state == JackTransportLooping) {
|
|
|
|
|
c->timebase_callback(c->jack_state,
|
2019-10-24 10:23:01 +02:00
|
|
|
c->buffer_frames,
|
2019-09-02 12:02:34 +02:00
|
|
|
&c->jack_position,
|
2019-08-29 14:01:20 +02:00
|
|
|
activation->pending_new_pos,
|
|
|
|
|
c->timebase_arg);
|
2019-08-27 14:41:15 +02:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
activation->pending_new_pos = false;
|
2019-08-27 14:41:15 +02:00
|
|
|
|
2019-09-02 12:02:34 +02:00
|
|
|
debug_position(c, &c->jack_position);
|
|
|
|
|
jack_to_position(&c->jack_position, activation);
|
2019-08-29 14:01:20 +02:00
|
|
|
}
|
2018-05-24 16:13:23 +02:00
|
|
|
}
|
2019-09-02 12:02:34 +02:00
|
|
|
}
|
|
|
|
|
signal_sync(c);
|
|
|
|
|
}
|
2019-02-12 17:40:46 +01:00
|
|
|
|
2019-09-02 12:02:34 +02:00
|
|
|
static void
|
|
|
|
|
on_rtsocket_condition(void *data, int fd, uint32_t mask)
|
|
|
|
|
{
|
|
|
|
|
struct client *c = data;
|
2019-02-12 17:40:46 +01:00
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(mask & (SPA_IO_ERR | SPA_IO_HUP))) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_warn("%p: got error", c);
|
2022-04-26 20:15:58 +02:00
|
|
|
client_remove_source(c);
|
2019-09-02 12:02:34 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(c->thread_callback)) {
|
2019-09-09 17:16:18 +02:00
|
|
|
if (!c->thread_entered) {
|
|
|
|
|
c->thread_entered = true;
|
|
|
|
|
c->thread_callback(c->thread_arg);
|
|
|
|
|
}
|
2020-03-16 12:52:28 +01:00
|
|
|
} else if (SPA_LIKELY(mask & SPA_IO_IN)) {
|
2019-10-24 10:23:01 +02:00
|
|
|
uint32_t buffer_frames;
|
2020-08-11 11:11:00 +02:00
|
|
|
int status = 0;
|
2019-08-08 11:02:40 +02:00
|
|
|
|
2019-10-24 10:23:01 +02:00
|
|
|
buffer_frames = cycle_run(c);
|
2019-02-12 17:40:46 +01:00
|
|
|
|
2021-09-11 13:07:10 +02:00
|
|
|
if (buffer_frames > 0)
|
|
|
|
|
status = do_rt_callback_res(c, process_callback, buffer_frames, c->process_arg);
|
2019-08-08 11:02:40 +02:00
|
|
|
|
2019-09-02 12:02:34 +02:00
|
|
|
cycle_signal(c, status);
|
2018-02-13 18:05:55 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
static void free_link(struct link *link)
|
2019-07-23 17:46:06 +02:00
|
|
|
{
|
2023-05-11 18:21:57 +02:00
|
|
|
pw_log_debug("free link %p", link);
|
2019-07-23 17:46:06 +02:00
|
|
|
pw_memmap_free(link->mem);
|
|
|
|
|
close(link->signalfd);
|
2020-02-07 16:45:58 +01:00
|
|
|
free(link);
|
2019-07-23 17:46:06 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
static int
|
|
|
|
|
do_clean_transport(struct spa_loop *loop,
|
|
|
|
|
bool async, uint32_t seq, const void *data, size_t size, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct client *c = user_data;
|
|
|
|
|
struct link *l;
|
|
|
|
|
pw_log_debug("%p: clean transport", c);
|
|
|
|
|
client_remove_source(c);
|
|
|
|
|
spa_list_consume(l, &c->rt.target_links, target_link)
|
|
|
|
|
spa_list_remove(&l->target_link);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
static void clean_transport(struct client *c)
|
|
|
|
|
{
|
2019-02-12 17:40:46 +01:00
|
|
|
struct link *l;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2019-11-28 12:57:06 +01:00
|
|
|
if (!c->has_transport)
|
2018-02-13 18:05:55 +01:00
|
|
|
return;
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
/* We assume the data-loop is unlocked now and can process our
|
|
|
|
|
* clean function. This is reasonable, the cleanup function is run when
|
|
|
|
|
* closing the client, which should join the data-thread. */
|
|
|
|
|
pw_data_loop_invoke(c->loop, do_clean_transport, 1, NULL, 0, true, c);
|
2018-05-21 13:12:26 +02:00
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
spa_list_consume(l, &c->links, link) {
|
|
|
|
|
spa_list_remove(&l->link);
|
|
|
|
|
free_link(l);
|
|
|
|
|
}
|
2019-11-28 12:57:06 +01:00
|
|
|
c->has_transport = false;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_transport(void *data,
|
2019-07-25 12:05:54 +02:00
|
|
|
int readfd, int writefd,
|
|
|
|
|
uint32_t mem_id, uint32_t offset, uint32_t size)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
|
|
|
|
clean_transport(c);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2019-12-06 11:48:40 +01:00
|
|
|
c->mem = pw_mempool_map_id(c->pool, mem_id,
|
2019-07-25 12:05:54 +02:00
|
|
|
PW_MEMMAP_FLAG_READWRITE, offset, size, NULL);
|
|
|
|
|
if (c->mem == NULL) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: can't map activation: %m", c);
|
2019-07-25 12:05:54 +02:00
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
c->activation = c->mem->ptr;
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: create client transport with fds %d %d for node %u",
|
2019-11-28 12:57:06 +01:00
|
|
|
c, readfd, writefd, c->node_id);
|
2018-02-26 17:31:22 +01:00
|
|
|
|
2019-02-12 17:40:46 +01:00
|
|
|
close(writefd);
|
2023-07-11 19:31:27 +02:00
|
|
|
c->socket_source = pw_loop_add_io(c->l,
|
2018-02-13 18:05:55 +01:00
|
|
|
readfd,
|
|
|
|
|
SPA_IO_ERR | SPA_IO_HUP,
|
|
|
|
|
true, on_rtsocket_condition, c);
|
2019-11-28 12:57:06 +01:00
|
|
|
|
|
|
|
|
c->has_transport = true;
|
2020-12-09 20:48:59 +01:00
|
|
|
c->position = &c->activation->position;
|
2020-02-07 13:30:24 +01:00
|
|
|
pw_thread_loop_signal(c->context.loop, false);
|
2019-11-28 12:57:06 +01:00
|
|
|
|
2019-02-18 12:28:00 +01:00
|
|
|
return 0;
|
2018-02-13 18:05:55 +01:00
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_set_param(void *data,
|
2019-02-18 12:28:00 +01:00
|
|
|
uint32_t id, uint32_t flags,
|
|
|
|
|
const struct spa_pod *param)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_proxy_error((struct pw_proxy*)c->node, -ENOTSUP, "not supported");
|
2019-02-18 12:28:00 +01:00
|
|
|
return -ENOTSUP;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2020-08-06 13:49:33 +02:00
|
|
|
static int install_timeowner(struct client *c)
|
2020-02-24 15:38:21 +01:00
|
|
|
{
|
|
|
|
|
struct pw_node_activation *a;
|
|
|
|
|
uint32_t owner;
|
|
|
|
|
|
2021-09-13 17:35:57 +02:00
|
|
|
if (!c->timebase_callback)
|
2020-02-24 15:38:21 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if ((a = c->driver_activation) == NULL)
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: activation %p", c, a);
|
2020-02-24 15:38:21 +01:00
|
|
|
|
|
|
|
|
/* was ok */
|
2023-07-13 12:25:22 +02:00
|
|
|
owner = SPA_ATOMIC_LOAD(a->segment_owner[0]);
|
2020-02-24 15:38:21 +01:00
|
|
|
if (owner == c->node_id)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2020-08-06 13:49:33 +02:00
|
|
|
/* try to become owner */
|
|
|
|
|
if (c->timeowner_conditional) {
|
2023-07-13 12:25:22 +02:00
|
|
|
if (!SPA_ATOMIC_CAS(a->segment_owner[0], 0, c->node_id)) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: owner:%u id:%u", c, owner, c->node_id);
|
2020-02-24 15:38:21 +01:00
|
|
|
return -EBUSY;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2023-07-13 12:25:22 +02:00
|
|
|
SPA_ATOMIC_STORE(a->segment_owner[0], c->node_id);
|
2020-02-24 15:38:21 +01:00
|
|
|
}
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: timebase installed for id:%u", c, c->node_id);
|
2020-02-24 15:38:21 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
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;
|
2020-04-30 16:15:22 +02:00
|
|
|
c->rt.position = c->position;
|
2020-04-30 12:57:56 +02:00
|
|
|
c->rt.driver_activation = c->driver_activation;
|
2021-09-16 11:51:01 +02:00
|
|
|
if (c->position) {
|
2021-12-10 12:19:14 +01:00
|
|
|
pw_log_info("%p: driver:%d clock:%s", c,
|
|
|
|
|
c->driver_id, c->position->clock.name);
|
2021-09-16 11:51:01 +02:00
|
|
|
check_sample_rate(c, c->position);
|
|
|
|
|
check_buffer_frames(c, c->position);
|
|
|
|
|
}
|
2020-04-30 12:57:56 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-22 10:59:16 +02:00
|
|
|
static int update_driver_activation(struct client *c)
|
|
|
|
|
{
|
2021-07-02 11:41:37 +02:00
|
|
|
jack_client_t *client = (jack_client_t*)c;
|
2019-08-22 10:59:16 +02:00
|
|
|
struct link *link;
|
2021-05-06 18:14:07 +02:00
|
|
|
bool freewheeling;
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: driver %d", c, c->driver_id);
|
2019-08-22 10:59:16 +02:00
|
|
|
|
2021-05-06 18:14:07 +02:00
|
|
|
freewheeling = SPA_FLAG_IS_SET(c->position->clock.flags, SPA_IO_CLOCK_FLAG_FREEWHEEL);
|
|
|
|
|
if (c->freewheeling != freewheeling) {
|
2021-07-02 11:41:37 +02:00
|
|
|
jack_native_thread_t thr = jack_client_thread_id(client);
|
|
|
|
|
|
2021-05-06 18:14:07 +02:00
|
|
|
c->freewheeling = freewheeling;
|
2021-07-02 11:41:37 +02:00
|
|
|
if (freewheeling && thr) {
|
|
|
|
|
jack_drop_real_time_scheduling(thr);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_FREEWHEEL, NULL, freewheeling, NULL);
|
2021-07-02 11:41:37 +02:00
|
|
|
|
|
|
|
|
if (!freewheeling && thr) {
|
|
|
|
|
jack_acquire_real_time_scheduling(thr,
|
|
|
|
|
jack_client_real_time_priority(client));
|
|
|
|
|
}
|
2021-05-06 18:14:07 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-22 10:59:16 +02:00
|
|
|
link = find_activation(&c->links, c->driver_id);
|
|
|
|
|
c->driver_activation = link ? link->activation : NULL;
|
2020-04-30 12:57:56 +02:00
|
|
|
pw_data_loop_invoke(c->loop,
|
2022-04-26 20:32:25 +02:00
|
|
|
do_update_driver_activation, SPA_ID_INVALID, NULL, 0, false, c);
|
2020-08-06 13:49:33 +02:00
|
|
|
install_timeowner(c);
|
2020-02-24 15:38:21 +01:00
|
|
|
|
2019-08-22 10:59:16 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_set_io(void *data,
|
2019-02-18 12:28:00 +01:00
|
|
|
uint32_t id,
|
|
|
|
|
uint32_t mem_id,
|
|
|
|
|
uint32_t offset,
|
|
|
|
|
uint32_t size)
|
2018-07-12 15:32:13 +02:00
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2020-04-30 16:13:50 +02:00
|
|
|
struct pw_memmap *old, *mm;
|
|
|
|
|
void *ptr;
|
2019-07-25 12:05:54 +02:00
|
|
|
uint32_t tag[5] = { c->node_id, id, };
|
2018-07-12 15:32:13 +02:00
|
|
|
|
2020-04-30 16:13:50 +02:00
|
|
|
old = pw_mempool_find_tag(c->pool, tag, sizeof(tag));
|
2019-08-21 20:36:29 +02:00
|
|
|
|
2020-04-30 16:13:50 +02:00
|
|
|
if (mem_id == SPA_ID_INVALID) {
|
2019-07-23 17:46:06 +02:00
|
|
|
mm = ptr = NULL;
|
2020-04-30 16:13:50 +02:00
|
|
|
} else {
|
2019-12-06 11:48:40 +01:00
|
|
|
mm = pw_mempool_map_id(c->pool, mem_id,
|
2019-07-25 12:05:54 +02:00
|
|
|
PW_MEMMAP_FLAG_READWRITE, offset, size, tag);
|
2020-04-30 16:13:50 +02:00
|
|
|
if (mm == NULL) {
|
2023-04-27 09:52:35 +02:00
|
|
|
pw_log_warn("%p: can't map memory id %u: %m", c, mem_id);
|
2019-02-18 12:28:00 +01:00
|
|
|
return -errno;
|
2020-04-30 16:13:50 +02:00
|
|
|
}
|
2019-07-23 17:46:06 +02:00
|
|
|
ptr = mm->ptr;
|
2020-04-30 16:13:50 +02:00
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: set io %s %p", c,
|
2018-09-01 14:12:24 +02:00
|
|
|
spa_debug_type_find_name(spa_type_io, id), ptr);
|
2018-07-12 15:32:13 +02:00
|
|
|
|
2019-07-23 17:46:06 +02:00
|
|
|
switch (id) {
|
|
|
|
|
case SPA_IO_Position:
|
2018-07-12 15:32:13 +02:00
|
|
|
c->position = ptr;
|
2019-08-22 10:59:16 +02:00
|
|
|
c->driver_id = ptr ? c->position->clock.id : SPA_ID_INVALID;
|
|
|
|
|
update_driver_activation(c);
|
2019-07-23 17:46:06 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2018-07-12 15:32:13 +02:00
|
|
|
}
|
2021-06-02 15:24:08 +10:00
|
|
|
pw_memmap_free(old);
|
2019-07-23 17:46:06 +02:00
|
|
|
|
2019-02-18 12:28:00 +01:00
|
|
|
return 0;
|
2018-07-12 15:32:13 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_event(void *data, const struct spa_event *event)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2019-02-18 12:28:00 +01:00
|
|
|
return -ENOTSUP;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_command(void *data, const struct spa_command *command)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: got command %d", c, SPA_COMMAND_TYPE(command));
|
2019-08-08 11:02:40 +02:00
|
|
|
|
2018-08-24 11:01:04 +02:00
|
|
|
switch (SPA_NODE_COMMAND_ID(command)) {
|
2019-10-21 17:23:26 +02:00
|
|
|
case SPA_NODE_COMMAND_Suspend:
|
2018-08-24 11:01:04 +02:00
|
|
|
case SPA_NODE_COMMAND_Pause:
|
2018-02-26 17:31:22 +01:00
|
|
|
if (c->started) {
|
2023-07-11 19:31:27 +02:00
|
|
|
pw_loop_update_io(c->l,
|
2018-02-26 17:31:22 +01:00
|
|
|
c->socket_source, SPA_IO_ERR | SPA_IO_HUP);
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2018-02-26 17:31:22 +01:00
|
|
|
c->started = false;
|
|
|
|
|
}
|
2018-08-24 11:01:04 +02:00
|
|
|
break;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2018-08-24 11:01:04 +02:00
|
|
|
case SPA_NODE_COMMAND_Start:
|
2018-02-26 17:31:22 +01:00
|
|
|
if (!c->started) {
|
2023-07-11 19:31:27 +02:00
|
|
|
pw_loop_update_io(c->l,
|
2018-02-26 17:31:22 +01:00
|
|
|
c->socket_source,
|
|
|
|
|
SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP);
|
|
|
|
|
c->started = true;
|
2019-09-03 13:45:13 +02:00
|
|
|
c->first = true;
|
2019-09-09 17:16:18 +02:00
|
|
|
c->thread_entered = false;
|
2018-02-26 17:31:22 +01:00
|
|
|
}
|
2018-08-24 11:01:04 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_warn("%p: unhandled node command %d", c, SPA_COMMAND_TYPE(command));
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_proxy_errorf((struct pw_proxy*)c->node, -ENOTSUP,
|
2019-02-18 12:28:00 +01:00
|
|
|
"unhandled command %d", SPA_COMMAND_TYPE(command));
|
2018-02-13 18:05:55 +01:00
|
|
|
}
|
2019-02-18 12:28:00 +01:00
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_add_port(void *data,
|
2018-02-09 18:23:36 +01:00
|
|
|
enum spa_direction direction,
|
2019-02-20 17:50:05 +01:00
|
|
|
uint32_t port_id, const struct spa_dict *props)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_proxy_error((struct pw_proxy*)c->node, -ENOTSUP, "add port not supported");
|
2019-02-18 12:28:00 +01:00
|
|
|
return -ENOTSUP;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_remove_port(void *data,
|
2018-02-09 18:23:36 +01:00
|
|
|
enum spa_direction direction,
|
|
|
|
|
uint32_t port_id)
|
|
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_proxy_error((struct pw_proxy*)c->node, -ENOTSUP, "remove port not supported");
|
2019-02-18 12:28:00 +01:00
|
|
|
return -ENOTSUP;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-20 15:50:55 +01:00
|
|
|
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) {
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_AUDIO:
|
2019-01-16 11:03:27 +01:00
|
|
|
*param = spa_pod_builder_add_object(b,
|
2018-08-29 09:54:21 +02:00
|
|
|
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
2019-01-16 11:03:27 +01:00
|
|
|
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio),
|
2020-01-09 13:40:48 +01:00
|
|
|
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp),
|
|
|
|
|
SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_DSP_F32));
|
2018-02-20 15:50:55 +01:00
|
|
|
break;
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_MIDI:
|
2019-01-16 11:03:27 +01:00
|
|
|
*param = spa_pod_builder_add_object(b,
|
2018-08-29 09:54:21 +02:00
|
|
|
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
2019-09-16 21:08:35 +02:00
|
|
|
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
|
|
|
|
|
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
|
2018-02-20 15:50:55 +01:00
|
|
|
break;
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_VIDEO:
|
2019-10-28 15:03:31 +01:00
|
|
|
*param = spa_pod_builder_add_object(b,
|
2019-10-28 18:03:46 +01:00
|
|
|
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
2019-10-28 15:03:31 +01:00
|
|
|
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
|
2020-01-09 13:40:48 +01:00
|
|
|
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp),
|
|
|
|
|
SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_DSP_F32));
|
2019-10-28 15:03:31 +01:00
|
|
|
break;
|
2018-02-20 15:50:55 +01:00
|
|
|
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) {
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_AUDIO:
|
2020-01-09 13:40:48 +01:00
|
|
|
*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));
|
2018-02-20 15:50:55 +01:00
|
|
|
break;
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_MIDI:
|
2019-01-16 11:03:27 +01:00
|
|
|
*param = spa_pod_builder_add_object(b,
|
2020-01-09 13:40:48 +01:00
|
|
|
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));
|
2018-02-20 15:50:55 +01:00
|
|
|
break;
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_VIDEO:
|
2019-10-28 15:03:31 +01:00
|
|
|
*param = spa_pod_builder_add_object(b,
|
2020-01-09 13:40:48 +01:00
|
|
|
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));
|
2019-10-28 15:03:31 +01:00
|
|
|
break;
|
2018-02-20 15:50:55 +01:00
|
|
|
default:
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int param_buffers(struct client *c, struct port *p,
|
|
|
|
|
struct spa_pod **param, struct spa_pod_builder *b)
|
|
|
|
|
{
|
2019-10-28 15:03:31 +01:00
|
|
|
switch (p->object->port.type_id) {
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_AUDIO:
|
|
|
|
|
case TYPE_ID_MIDI:
|
2019-10-28 15:03:31 +01:00
|
|
|
*param = spa_pod_builder_add_object(b,
|
|
|
|
|
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
|
2020-02-19 10:17:00 +01:00
|
|
|
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
|
2019-10-28 15:03:31 +01:00
|
|
|
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
|
|
|
|
|
SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_STEP_Int(
|
|
|
|
|
MAX_BUFFER_FRAMES * sizeof(float),
|
|
|
|
|
sizeof(float),
|
2022-01-12 17:41:13 +01:00
|
|
|
INT32_MAX,
|
2019-10-28 15:03:31 +01:00
|
|
|
sizeof(float)),
|
2020-03-02 16:50:37 +01:00
|
|
|
SPA_PARAM_BUFFERS_stride, SPA_POD_Int(p->object->port.type_id == TYPE_ID_AUDIO ?
|
2022-01-03 12:32:26 +01:00
|
|
|
sizeof(float) : 1));
|
2019-10-28 15:03:31 +01:00
|
|
|
break;
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_VIDEO:
|
2019-10-28 15:03:31 +01:00
|
|
|
*param = spa_pod_builder_add_object(b,
|
|
|
|
|
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
|
2020-02-19 10:17:00 +01:00
|
|
|
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
|
2019-10-28 15:03:31 +01:00
|
|
|
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
|
|
|
|
|
SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(
|
|
|
|
|
320 * 240 * 4 * 4,
|
|
|
|
|
0,
|
|
|
|
|
INT32_MAX),
|
2022-01-03 12:32:26 +01:00
|
|
|
SPA_PARAM_BUFFERS_stride, SPA_POD_CHOICE_RANGE_Int(4, 4, INT32_MAX));
|
2019-10-28 15:03:31 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2018-02-20 15:50:55 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-03 20:13:57 +02:00
|
|
|
static int param_io(struct client *c, struct port *p,
|
|
|
|
|
struct spa_pod **param, struct spa_pod_builder *b)
|
|
|
|
|
{
|
2019-09-16 21:08:35 +02:00
|
|
|
*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)));
|
2018-10-03 20:13:57 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-25 17:55:13 +02:00
|
|
|
static int param_latency(struct client *c, struct port *p,
|
|
|
|
|
struct spa_pod **param, struct spa_pod_builder *b)
|
|
|
|
|
{
|
2021-05-27 15:13:48 +02:00
|
|
|
*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)]);
|
2021-05-25 17:55:13 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 10:38:53 +02:00
|
|
|
/* called from thread-loop */
|
2018-07-31 21:42:28 +02:00
|
|
|
static int port_set_format(struct client *c, struct port *p,
|
|
|
|
|
uint32_t flags, const struct spa_pod *param)
|
|
|
|
|
{
|
2021-09-07 12:20:36 +02:00
|
|
|
struct spa_pod *params[6];
|
|
|
|
|
uint8_t buffer[4096];
|
|
|
|
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
|
|
|
|
|
2018-07-31 21:42:28 +02:00
|
|
|
if (param == NULL) {
|
|
|
|
|
struct mix *mix;
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: port %p clear format", c, p);
|
2018-07-31 21:42:28 +02:00
|
|
|
|
|
|
|
|
spa_list_for_each(mix, &p->mix, port_link)
|
|
|
|
|
clear_buffers(c, mix);
|
2021-09-07 12:20:36 +02:00
|
|
|
|
|
|
|
|
p->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
|
2018-07-31 21:42:28 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
struct spa_audio_info info = { 0 };
|
2021-03-27 18:33:22 +01:00
|
|
|
if (spa_format_parse(param, &info.media_type, &info.media_subtype) < 0)
|
|
|
|
|
return -EINVAL;
|
2018-07-31 21:42:28 +02:00
|
|
|
|
2018-08-24 11:01:04 +02:00
|
|
|
switch (info.media_type) {
|
|
|
|
|
case SPA_MEDIA_TYPE_audio:
|
2019-10-28 15:03:31 +01:00
|
|
|
{
|
2020-01-09 13:40:48 +01:00
|
|
|
if (info.media_subtype != SPA_MEDIA_SUBTYPE_dsp)
|
2018-08-24 11:01:04 +02:00
|
|
|
return -EINVAL;
|
2018-07-31 21:42:28 +02:00
|
|
|
|
2020-01-09 13:40:48 +01:00
|
|
|
if (spa_format_audio_dsp_parse(param, &info.info.dsp) < 0)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
if (info.info.dsp.format != SPA_AUDIO_FORMAT_DSP_F32)
|
2018-07-31 21:42:28 +02:00
|
|
|
return -EINVAL;
|
2018-08-24 11:01:04 +02:00
|
|
|
break;
|
2019-10-28 15:03:31 +01:00
|
|
|
}
|
2019-09-16 21:08:35 +02:00
|
|
|
case SPA_MEDIA_TYPE_application:
|
|
|
|
|
if (info.media_subtype != SPA_MEDIA_SUBTYPE_control)
|
|
|
|
|
return -EINVAL;
|
2018-08-24 11:01:04 +02:00
|
|
|
break;
|
2019-10-28 15:03:31 +01:00
|
|
|
case SPA_MEDIA_TYPE_video:
|
|
|
|
|
{
|
2019-10-28 18:03:46 +01:00
|
|
|
struct spa_video_info vinfo = { 0 };
|
2019-10-28 15:03:31 +01:00
|
|
|
|
2020-01-09 13:40:48 +01:00
|
|
|
if (info.media_subtype != SPA_MEDIA_SUBTYPE_dsp)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
if (spa_format_video_dsp_parse(param, &vinfo.info.dsp) < 0)
|
2019-10-28 15:03:31 +01:00
|
|
|
return -EINVAL;
|
2020-01-09 13:40:48 +01:00
|
|
|
if (vinfo.info.dsp.format != SPA_VIDEO_FORMAT_DSP_F32)
|
2019-10-28 15:03:31 +01:00
|
|
|
return -EINVAL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-08-24 11:01:04 +02:00
|
|
|
default:
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2021-09-07 12:20:36 +02:00
|
|
|
p->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE);
|
2018-07-31 21:42:28 +02:00
|
|
|
}
|
2021-09-07 12:20:36 +02:00
|
|
|
|
|
|
|
|
pw_log_info("port %s: update", p->object->port.name);
|
|
|
|
|
|
|
|
|
|
p->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
|
|
|
|
|
|
|
|
|
|
param_enum_format(c, p, ¶ms[0], &b);
|
|
|
|
|
param_format(c, p, ¶ms[1], &b);
|
|
|
|
|
param_buffers(c, p, ¶ms[2], &b);
|
|
|
|
|
param_io(c, p, ¶ms[3], &b);
|
|
|
|
|
param_latency(c, p, ¶ms[4], &b);
|
|
|
|
|
param_latency_other(c, p, ¶ms[5], &b);
|
|
|
|
|
|
|
|
|
|
pw_client_node_port_update(c->node,
|
|
|
|
|
p->direction,
|
2022-01-22 20:55:40 +01:00
|
|
|
p->port_id,
|
2021-09-07 12:20:36 +02:00
|
|
|
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;
|
2018-07-31 21:42:28 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 10:38:53 +02:00
|
|
|
/* called from thread-loop */
|
2021-05-26 15:37:23 +02:00
|
|
|
static void port_update_latency(struct port *p)
|
|
|
|
|
{
|
|
|
|
|
struct client *c = p->client;
|
2021-05-27 15:13:48 +02:00
|
|
|
struct spa_pod *params[6];
|
2021-05-26 15:37:23 +02:00
|
|
|
uint8_t buffer[4096];
|
|
|
|
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
|
|
|
|
|
|
|
|
|
param_enum_format(c, p, ¶ms[0], &b);
|
|
|
|
|
param_format(c, p, ¶ms[1], &b);
|
|
|
|
|
param_buffers(c, p, ¶ms[2], &b);
|
|
|
|
|
param_io(c, p, ¶ms[3], &b);
|
|
|
|
|
param_latency(c, p, ¶ms[4], &b);
|
2021-05-27 15:13:48 +02:00
|
|
|
param_latency_other(c, p, ¶ms[5], &b);
|
|
|
|
|
|
2021-07-05 16:06:22 +02:00
|
|
|
pw_log_info("port %s: update", p->object->port.name);
|
2021-05-26 15:37:23 +02:00
|
|
|
|
|
|
|
|
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,
|
2022-01-22 20:55:40 +01:00
|
|
|
p->port_id,
|
2021-05-26 15:37:23 +02:00
|
|
|
PW_CLIENT_NODE_PORT_UPDATE_PARAMS |
|
|
|
|
|
PW_CLIENT_NODE_PORT_UPDATE_INFO,
|
2021-05-27 15:13:48 +02:00
|
|
|
SPA_N_ELEMENTS(params),
|
2021-05-26 15:37:23 +02:00
|
|
|
(const struct spa_pod **) params,
|
|
|
|
|
&p->info);
|
2021-09-07 12:20:36 +02:00
|
|
|
p->info.change_mask = 0;
|
2021-05-27 15:13:48 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
static void port_check_latency(struct port *p, const struct spa_latency_info *latency)
|
|
|
|
|
{
|
|
|
|
|
struct spa_latency_info *current;
|
|
|
|
|
struct client *c = p->client;
|
|
|
|
|
struct object *o = p->object;
|
|
|
|
|
|
|
|
|
|
current = &o->port.latency[latency->direction];
|
|
|
|
|
if (spa_latency_info_compare(current, latency) == 0)
|
|
|
|
|
return;
|
|
|
|
|
*current = *latency;
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
port_update_latency(p);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 10:38:53 +02:00
|
|
|
/* called from thread-loop */
|
2021-05-27 15:13:48 +02:00
|
|
|
static void default_latency(struct client *c, enum spa_direction direction,
|
|
|
|
|
struct spa_latency_info *latency)
|
|
|
|
|
{
|
|
|
|
|
enum spa_direction other;
|
2022-01-22 20:55:40 +01:00
|
|
|
union pw_map_item *item;
|
2021-05-27 15:13:48 +02:00
|
|
|
struct port *p;
|
|
|
|
|
|
|
|
|
|
other = SPA_DIRECTION_REVERSE(direction);
|
|
|
|
|
|
2021-12-02 14:49:48 +01:00
|
|
|
spa_latency_info_combine_start(latency, direction);
|
|
|
|
|
|
2022-01-22 20:55:40 +01:00
|
|
|
pw_array_for_each(item, &c->ports[other].items) {
|
|
|
|
|
if (pw_map_item_is_free(item))
|
|
|
|
|
continue;
|
|
|
|
|
p = item->data;
|
2021-05-27 15:13:48 +02:00
|
|
|
spa_latency_info_combine(latency, &p->object->port.latency[direction]);
|
2022-01-22 20:55:40 +01:00
|
|
|
}
|
2021-12-02 14:49:48 +01:00
|
|
|
|
|
|
|
|
spa_latency_info_combine_finish(latency);
|
2021-05-26 15:37:23 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-06 10:38:53 +02:00
|
|
|
/* called from thread-loop */
|
2021-05-26 15:37:23 +02:00
|
|
|
static void default_latency_callback(jack_latency_callback_mode_t mode, struct client *c)
|
|
|
|
|
{
|
2023-05-11 18:21:57 +02:00
|
|
|
struct spa_latency_info latency;
|
2022-01-22 20:55:40 +01:00
|
|
|
union pw_map_item *item;
|
2023-05-11 18:21:57 +02:00
|
|
|
enum spa_direction direction;
|
2021-05-26 15:37:23 +02:00
|
|
|
struct port *p;
|
|
|
|
|
|
|
|
|
|
if (mode == JackPlaybackLatency)
|
|
|
|
|
direction = SPA_DIRECTION_INPUT;
|
|
|
|
|
else
|
|
|
|
|
direction = SPA_DIRECTION_OUTPUT;
|
|
|
|
|
|
2021-05-27 15:13:48 +02:00
|
|
|
default_latency(c, direction, &latency);
|
2021-05-26 15:37:23 +02:00
|
|
|
|
2022-01-22 20:55:40 +01:00
|
|
|
pw_array_for_each(item, &c->ports[direction].items) {
|
|
|
|
|
if (pw_map_item_is_free(item))
|
|
|
|
|
continue;
|
|
|
|
|
p = item->data;
|
2023-05-11 18:21:57 +02:00
|
|
|
port_check_latency(p, &latency);
|
2021-05-26 15:37:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 10:38:53 +02:00
|
|
|
/* called from thread-loop */
|
2021-05-25 17:55:13 +02:00
|
|
|
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;
|
2021-05-27 15:13:48 +02:00
|
|
|
struct spa_latency_info *current;
|
2021-05-25 17:55:13 +02:00
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
if (param == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if ((res = spa_latency_parse(param, &info)) < 0)
|
|
|
|
|
return res;
|
|
|
|
|
|
2021-05-27 15:13:48 +02:00
|
|
|
current = &p->object->port.latency[info.direction];
|
|
|
|
|
if (spa_latency_info_compare(current, &info) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
*current = info;
|
2021-05-25 17:55:13 +02:00
|
|
|
|
2021-07-05 16:06:22 +02:00
|
|
|
pw_log_info("port %s: set %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, p->object->port.name,
|
2021-05-25 17:55:13 +02:00
|
|
|
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);
|
|
|
|
|
|
2021-05-27 15:13:48 +02:00
|
|
|
if (info.direction == p->direction)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2021-05-25 17:55:13 +02:00
|
|
|
if (info.direction == SPA_DIRECTION_INPUT)
|
|
|
|
|
mode = JackPlaybackLatency;
|
|
|
|
|
else
|
|
|
|
|
mode = JackCaptureLatency;
|
|
|
|
|
|
|
|
|
|
if (c->latency_callback)
|
2023-05-11 18:21:57 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_LATENCY, NULL, mode, NULL);
|
2021-05-26 15:37:23 +02:00
|
|
|
else
|
|
|
|
|
default_latency_callback(mode, c);
|
2021-05-25 17:55:13 +02:00
|
|
|
|
2021-05-27 15:13:48 +02:00
|
|
|
port_update_latency(p);
|
|
|
|
|
|
2021-05-25 17:55:13 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 10:38:53 +02:00
|
|
|
/* called from thread-loop */
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_port_set_param(void *data,
|
2018-02-09 18:23:36 +01:00
|
|
|
enum spa_direction direction,
|
|
|
|
|
uint32_t port_id,
|
|
|
|
|
uint32_t id, uint32_t flags,
|
|
|
|
|
const struct spa_pod *param)
|
|
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2018-02-16 17:57:26 +01:00
|
|
|
struct port *p = GET_PORT(c, direction, port_id);
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2021-06-17 09:18:29 +02:00
|
|
|
if (p == NULL || !p->valid)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
2021-07-05 16:06:22 +02:00
|
|
|
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);
|
2019-10-24 10:23:01 +02:00
|
|
|
|
2021-05-25 17:55:13 +02:00
|
|
|
switch (id) {
|
|
|
|
|
case SPA_PARAM_Format:
|
2021-09-07 12:20:36 +02:00
|
|
|
return port_set_format(c, p, flags, param);
|
2021-05-25 17:55:13 +02:00
|
|
|
break;
|
|
|
|
|
case SPA_PARAM_Latency:
|
2021-06-17 09:18:29 +02:00
|
|
|
return port_set_latency(c, p, flags, param);
|
2021-05-25 17:55:13 +02:00
|
|
|
default:
|
|
|
|
|
break;
|
2018-02-20 13:06:07 +01:00
|
|
|
}
|
2021-09-07 12:20:36 +02:00
|
|
|
return 0;
|
2018-02-13 18:05:55 +01:00
|
|
|
}
|
|
|
|
|
|
2020-09-30 11:59:41 +02:00
|
|
|
static inline void *init_buffer(struct port *p)
|
2018-05-24 16:12:59 +02:00
|
|
|
{
|
2020-04-28 14:15:49 +02:00
|
|
|
void *data = p->emptyptr;
|
|
|
|
|
if (p->zeroed)
|
|
|
|
|
return data;
|
|
|
|
|
|
2020-03-02 16:50:37 +01:00
|
|
|
if (p->object->port.type_id == TYPE_ID_MIDI) {
|
2018-08-15 21:26:05 +02:00
|
|
|
struct midi_buffer *mb = data;
|
2018-05-24 16:12:59 +02:00
|
|
|
mb->magic = MIDI_BUFFER_MAGIC;
|
2019-10-24 10:23:01 +02:00
|
|
|
mb->buffer_size = MAX_BUFFER_FRAMES * sizeof(float);
|
2020-04-28 14:15:49 +02:00
|
|
|
mb->nframes = MAX_BUFFER_FRAMES;
|
2018-05-24 16:12:59 +02:00
|
|
|
mb->write_pos = 0;
|
|
|
|
|
mb->event_count = 0;
|
|
|
|
|
mb->lost_events = 0;
|
2020-04-28 14:15:49 +02:00
|
|
|
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;
|
2018-05-24 16:12:59 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_port_use_buffers(void *data,
|
2018-02-09 18:23:36 +01:00
|
|
|
enum spa_direction direction,
|
|
|
|
|
uint32_t port_id,
|
2018-05-21 13:12:26 +02:00
|
|
|
uint32_t mix_id,
|
2019-07-25 13:16:52 +02:00
|
|
|
uint32_t flags,
|
2018-02-09 18:23:36 +01:00
|
|
|
uint32_t n_buffers,
|
|
|
|
|
struct pw_client_node_buffer *buffers)
|
|
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2018-02-13 18:05:55 +01:00
|
|
|
struct port *p = GET_PORT(c, direction, port_id);
|
|
|
|
|
struct buffer *b;
|
2020-03-17 11:37:56 +01:00
|
|
|
uint32_t i, j, fl;
|
|
|
|
|
int res;
|
2018-05-21 13:12:26 +02:00
|
|
|
struct mix *mix;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2021-06-17 09:18:29 +02:00
|
|
|
if (p == NULL || !p->valid) {
|
2018-02-13 18:05:55 +01:00
|
|
|
res = -EINVAL;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
2023-07-06 12:53:28 +02:00
|
|
|
if ((mix = find_mix(c, p, mix_id)) == NULL) {
|
2018-05-21 13:12:26 +02:00
|
|
|
res = -ENOMEM;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: port %p %d %d.%d use_buffers %d", c, p, direction,
|
2018-05-21 13:12:26 +02:00
|
|
|
port_id, mix_id, n_buffers);
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2020-02-18 18:03:30 +01:00
|
|
|
if (n_buffers > MAX_BUFFERS) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: too many buffers %u > %u", c, n_buffers, MAX_BUFFERS);
|
2023-01-10 12:30:25 +01:00
|
|
|
return -ENOSPC;
|
2020-02-18 18:03:30 +01:00
|
|
|
}
|
2020-02-18 17:37:02 +01:00
|
|
|
|
2020-03-02 16:50:37 +01:00
|
|
|
if (p->object->port.type_id == TYPE_ID_VIDEO && direction == SPA_DIRECTION_INPUT) {
|
2019-10-28 18:03:46 +01:00
|
|
|
fl = PW_MEMMAP_FLAG_READ;
|
|
|
|
|
} else {
|
|
|
|
|
/* some apps write to the input buffer so we want everything readwrite */
|
|
|
|
|
fl = PW_MEMMAP_FLAG_READWRITE;
|
|
|
|
|
}
|
2018-02-13 18:05:55 +01:00
|
|
|
|
|
|
|
|
/* clear previous buffers */
|
2018-05-21 13:12:26 +02:00
|
|
|
clear_buffers(c, mix);
|
2018-02-13 18:05:55 +01:00
|
|
|
|
|
|
|
|
for (i = 0; i < n_buffers; i++) {
|
|
|
|
|
off_t offset;
|
|
|
|
|
struct spa_buffer *buf;
|
2019-07-23 17:46:06 +02:00
|
|
|
struct pw_memmap *mm;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2019-12-06 11:48:40 +01:00
|
|
|
mm = pw_mempool_map_id(c->pool, buffers[i].mem_id,
|
2019-07-25 13:16:52 +02:00
|
|
|
fl, buffers[i].offset, buffers[i].size, NULL);
|
2019-07-23 17:46:06 +02:00
|
|
|
if (mm == NULL) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_warn("%p: can't map memory id %u: %m", c, buffers[i].mem_id);
|
2018-02-13 18:05:55 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf = buffers[i].buffer;
|
|
|
|
|
|
2019-01-07 17:53:13 +01:00
|
|
|
b = &mix->buffers[i];
|
|
|
|
|
b->id = i;
|
2018-02-13 18:05:55 +01:00
|
|
|
b->flags = 0;
|
|
|
|
|
b->n_mem = 0;
|
2019-07-23 17:46:06 +02:00
|
|
|
b->mem[b->n_mem++] = mm;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: add buffer id:%u offset:%u size:%u map:%p ptr:%p",
|
2019-08-08 11:02:40 +02:00
|
|
|
c, buffers[i].mem_id, buffers[i].offset,
|
|
|
|
|
buffers[i].size, mm, mm->ptr);
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2019-07-23 17:46:06 +02:00
|
|
|
offset = 0;
|
2018-02-13 18:05:55 +01:00
|
|
|
for (j = 0; j < buf->n_metas; j++) {
|
|
|
|
|
struct spa_meta *m = &buf->metas[j];
|
2019-01-24 18:27:40 +01:00
|
|
|
offset += SPA_ROUND_UP_N(m->size, 8);
|
2018-02-13 18:05:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 =
|
2021-05-06 13:41:44 +10:00
|
|
|
SPA_PTROFF(mm->ptr, offset + sizeof(struct spa_chunk) * j,
|
2018-02-13 18:05:55 +01:00
|
|
|
struct spa_chunk);
|
|
|
|
|
|
2019-07-23 17:46:06 +02:00
|
|
|
if (d->type == SPA_DATA_MemId) {
|
|
|
|
|
uint32_t mem_id = SPA_PTR_TO_UINT32(d->data);
|
|
|
|
|
struct pw_memblock *bm;
|
|
|
|
|
struct pw_memmap *bmm;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2019-12-06 11:48:40 +01:00
|
|
|
bm = pw_mempool_find_id(c->pool, mem_id);
|
2019-07-23 17:46:06 +02:00
|
|
|
if (bm == NULL) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: unknown buffer mem %u", c, mem_id);
|
2019-07-23 17:46:06 +02:00
|
|
|
res = -ENODEV;
|
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d->fd = bm->fd;
|
|
|
|
|
d->type = bm->type;
|
|
|
|
|
d->data = NULL;
|
|
|
|
|
|
2019-07-25 13:16:52 +02:00
|
|
|
bmm = pw_memblock_map(bm, fl, d->mapoffset, d->maxsize, NULL);
|
2019-07-23 17:46:06 +02:00
|
|
|
if (bmm == NULL) {
|
|
|
|
|
res = -errno;
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: failed to map buffer mem %m", c);
|
2018-02-13 18:05:55 +01:00
|
|
|
d->data = NULL;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
2019-07-23 17:46:06 +02:00
|
|
|
b->mem[b->n_mem++] = bmm;
|
|
|
|
|
d->data = bmm->ptr;
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: data %d %u -> fd %d %d",
|
2018-05-24 16:12:59 +02:00
|
|
|
c, j, bm->id, bm->fd, d->maxsize);
|
2018-08-24 11:01:04 +02:00
|
|
|
} else if (d->type == SPA_DATA_MemPtr) {
|
2019-07-23 17:46:06 +02:00
|
|
|
int offs = SPA_PTR_TO_INT(d->data);
|
2021-05-06 13:41:44 +10:00
|
|
|
d->data = SPA_PTROFF(mm->ptr, offs, void);
|
2018-02-13 18:05:55 +01:00
|
|
|
d->fd = -1;
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: data %d %u -> mem %p %d",
|
2018-05-24 16:12:59 +02:00
|
|
|
c, j, b->id, d->data, d->maxsize);
|
2018-02-13 18:05:55 +01:00
|
|
|
} else {
|
|
|
|
|
pw_log_warn("unknown buffer data type %d", d->type);
|
|
|
|
|
}
|
2021-01-19 14:47:44 +01:00
|
|
|
if (c->allow_mlock && mlock(d->data, d->maxsize) < 0) {
|
|
|
|
|
if (errno != ENOMEM || !mlock_warned) {
|
2021-02-08 10:59:02 +01:00
|
|
|
pw_log(c->warn_mlock ? SPA_LOG_LEVEL_WARN : SPA_LOG_LEVEL_DEBUG,
|
2021-10-03 08:52:27 +02:00
|
|
|
"%p: Failed to mlock memory %p %u: %s", c,
|
2021-01-19 14:47:44 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-13 18:05:55 +01:00
|
|
|
}
|
|
|
|
|
SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);
|
2019-09-16 21:08:35 +02:00
|
|
|
if (direction == SPA_DIRECTION_OUTPUT)
|
2018-05-21 13:12:26 +02:00
|
|
|
reuse_buffer(c, mix, b->id);
|
2018-05-24 16:12:59 +02:00
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: have %d buffers", c, n_buffers);
|
2018-05-21 13:12:26 +02:00
|
|
|
mix->n_buffers = n_buffers;
|
2018-02-13 18:05:55 +01:00
|
|
|
res = 0;
|
|
|
|
|
|
|
|
|
|
done:
|
2019-02-18 12:28:00 +01:00
|
|
|
if (res < 0)
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_proxy_error((struct pw_proxy*)c->node, res, spa_strerror(res));
|
2019-02-18 12:28:00 +01:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_port_set_io(void *data,
|
2018-02-09 18:23:36 +01:00
|
|
|
enum spa_direction direction,
|
|
|
|
|
uint32_t port_id,
|
2018-05-21 13:12:26 +02:00
|
|
|
uint32_t mix_id,
|
2018-02-09 18:23:36 +01:00
|
|
|
uint32_t id,
|
|
|
|
|
uint32_t mem_id,
|
|
|
|
|
uint32_t offset,
|
|
|
|
|
uint32_t size)
|
|
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2018-02-26 17:31:22 +01:00
|
|
|
struct port *p = GET_PORT(c, direction, port_id);
|
2020-06-05 13:02:56 +02:00
|
|
|
struct pw_memmap *mm, *old;
|
2018-05-21 13:12:26 +02:00
|
|
|
struct mix *mix;
|
2019-07-25 12:05:54 +02:00
|
|
|
uint32_t tag[5] = { c->node_id, direction, port_id, mix_id, id };
|
2018-02-26 17:31:22 +01:00
|
|
|
void *ptr;
|
2018-05-21 13:12:26 +02:00
|
|
|
int res = 0;
|
2018-02-26 17:31:22 +01:00
|
|
|
|
2021-06-17 09:18:29 +02:00
|
|
|
if (p == NULL || !p->valid) {
|
|
|
|
|
res = -EINVAL;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-06 12:53:28 +02:00
|
|
|
if ((mix = find_mix(c, p, mix_id)) == NULL) {
|
2018-05-25 12:48:23 +02:00
|
|
|
res = -ENOMEM;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-05 13:02:56 +02:00
|
|
|
old = pw_mempool_find_tag(c->pool, tag, sizeof(tag));
|
2019-08-21 20:36:29 +02:00
|
|
|
|
2018-02-26 17:31:22 +01:00
|
|
|
if (mem_id == SPA_ID_INVALID) {
|
2019-07-23 17:46:06 +02:00
|
|
|
mm = ptr = NULL;
|
2018-02-26 17:31:22 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2019-12-06 11:48:40 +01:00
|
|
|
mm = pw_mempool_map_id(c->pool, mem_id,
|
2019-07-25 12:05:54 +02:00
|
|
|
PW_MEMMAP_FLAG_READWRITE, offset, size, tag);
|
2019-07-23 17:46:06 +02:00
|
|
|
if (mm == NULL) {
|
2023-04-27 09:52:35 +02:00
|
|
|
pw_log_warn("%p: can't map memory id %u: %m", c, mem_id);
|
2018-02-26 17:31:22 +01:00
|
|
|
res = -EINVAL;
|
2020-06-05 13:02:56 +02:00
|
|
|
goto exit_free;
|
2018-02-26 17:31:22 +01:00
|
|
|
}
|
2019-07-23 17:46:06 +02:00
|
|
|
ptr = mm->ptr;
|
2018-02-26 17:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: port %p mix:%d set io:%s id:%u ptr:%p", c, p, mix_id,
|
2019-07-23 17:46:06 +02:00
|
|
|
spa_debug_type_find_name(spa_type_io, id), id, ptr);
|
|
|
|
|
|
2018-10-03 20:13:57 +02:00
|
|
|
switch (id) {
|
|
|
|
|
case SPA_IO_Buffers:
|
2018-05-21 13:12:26 +02:00
|
|
|
mix->io = ptr;
|
2018-10-03 20:13:57 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-06-05 13:02:56 +02:00
|
|
|
exit_free:
|
2021-06-02 15:24:08 +10:00
|
|
|
pw_memmap_free(old);
|
2020-06-05 13:02:56 +02:00
|
|
|
exit:
|
2019-02-18 12:28:00 +01:00
|
|
|
if (res < 0)
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_proxy_error((struct pw_proxy*)c->node, res, spa_strerror(res));
|
2019-02-18 12:28:00 +01:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-07 16:45:58 +01:00
|
|
|
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);
|
2020-04-30 12:57:56 +02:00
|
|
|
spa_list_append(&c->rt.target_links, &link->target_link);
|
2020-02-07 16:45:58 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
static int
|
|
|
|
|
do_deactivate_link(struct spa_loop *loop,
|
|
|
|
|
bool async, uint32_t seq, const void *data, size_t size, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct link *link = user_data;
|
|
|
|
|
pw_log_trace("link %p activate", link);
|
|
|
|
|
spa_list_remove(&link->target_link);
|
|
|
|
|
free_link(link);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_set_activation(void *data,
|
2019-02-12 17:40:46 +01:00
|
|
|
uint32_t node_id,
|
|
|
|
|
int signalfd,
|
|
|
|
|
uint32_t mem_id,
|
|
|
|
|
uint32_t offset,
|
|
|
|
|
uint32_t size)
|
|
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2019-07-23 17:46:06 +02:00
|
|
|
struct pw_memmap *mm;
|
2019-02-12 17:40:46 +01:00
|
|
|
struct link *link;
|
|
|
|
|
void *ptr;
|
2019-02-18 12:28:00 +01:00
|
|
|
int res = 0;
|
2019-02-12 17:40:46 +01:00
|
|
|
|
|
|
|
|
if (mem_id == SPA_ID_INVALID) {
|
2019-07-25 12:05:54 +02:00
|
|
|
mm = ptr = NULL;
|
2019-02-12 17:40:46 +01:00
|
|
|
size = 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-12-06 11:48:40 +01:00
|
|
|
mm = pw_mempool_map_id(c->pool, mem_id,
|
2019-07-25 12:05:54 +02:00
|
|
|
PW_MEMMAP_FLAG_READWRITE, offset, size, NULL);
|
2019-07-23 17:46:06 +02:00
|
|
|
if (mm == NULL) {
|
2023-04-27 09:52:35 +02:00
|
|
|
pw_log_warn("%p: can't map memory id %u: %m", c, mem_id);
|
2019-02-18 12:28:00 +01:00
|
|
|
res = -EINVAL;
|
|
|
|
|
goto exit;
|
2019-02-12 17:40:46 +01:00
|
|
|
}
|
2019-07-23 17:46:06 +02:00
|
|
|
ptr = mm->ptr;
|
2019-02-12 17:40:46 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-22 12:15:01 +02:00
|
|
|
if (c->node_id == node_id) {
|
|
|
|
|
pw_log_debug("%p: our activation %u: %u %u %u %p", c, node_id,
|
|
|
|
|
mem_id, offset, size, ptr);
|
|
|
|
|
} else {
|
|
|
|
|
pw_log_debug("%p: set activation %u: %u %u %u %p", c, node_id,
|
|
|
|
|
mem_id, offset, size, ptr);
|
|
|
|
|
}
|
2019-02-12 17:40:46 +01:00
|
|
|
|
|
|
|
|
if (ptr) {
|
2020-02-07 16:45:58 +01:00
|
|
|
link = calloc(1, sizeof(struct link));
|
2019-07-23 17:46:06 +02:00
|
|
|
if (link == NULL) {
|
|
|
|
|
res = -errno;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
2020-02-07 16:45:58 +01:00
|
|
|
link->client = c;
|
2019-02-12 17:40:46 +01:00
|
|
|
link->node_id = node_id;
|
2019-07-23 17:46:06 +02:00
|
|
|
link->mem = mm;
|
2019-02-12 17:40:46 +01:00
|
|
|
link->activation = ptr;
|
|
|
|
|
link->signalfd = signalfd;
|
2020-02-07 16:45:58 +01:00
|
|
|
spa_list_append(&c->links, &link->link);
|
|
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pw_data_loop_invoke(c->loop,
|
2020-02-07 16:45:58 +01:00
|
|
|
do_activate_link, SPA_ID_INVALID, NULL, 0, false, link);
|
2019-02-12 17:40:46 +01:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
link = find_activation(&c->links, node_id);
|
2019-02-18 12:28:00 +01:00
|
|
|
if (link == NULL) {
|
|
|
|
|
res = -EINVAL;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
2023-05-11 18:21:57 +02:00
|
|
|
spa_list_remove(&link->link);
|
|
|
|
|
|
|
|
|
|
pw_data_loop_invoke(c->loop,
|
|
|
|
|
do_deactivate_link, SPA_ID_INVALID, NULL, 0, false, link);
|
2019-02-12 17:40:46 +01:00
|
|
|
}
|
2019-02-18 12:28:00 +01:00
|
|
|
|
2019-08-22 10:59:16 +02:00
|
|
|
if (c->driver_id == node_id)
|
|
|
|
|
update_driver_activation(c);
|
|
|
|
|
|
2019-02-18 12:28:00 +01:00
|
|
|
exit:
|
|
|
|
|
if (res < 0)
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_proxy_error((struct pw_proxy*)c->node, res, spa_strerror(res));
|
2019-02-18 12:28:00 +01:00
|
|
|
return res;
|
2019-02-12 17:40:46 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int client_node_port_set_mix_info(void *data,
|
2021-08-03 17:39:14 +02:00
|
|
|
enum spa_direction direction,
|
|
|
|
|
uint32_t port_id,
|
|
|
|
|
uint32_t mix_id,
|
|
|
|
|
uint32_t peer_id,
|
|
|
|
|
const struct spa_dict *props)
|
|
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2021-08-03 17:39:14 +02:00
|
|
|
struct port *p = GET_PORT(c, direction, port_id);
|
|
|
|
|
struct mix *mix;
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
|
|
if (p == NULL || !p->valid) {
|
|
|
|
|
res = -EINVAL;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-20 20:09:29 +02:00
|
|
|
mix = find_mix(c, p, mix_id);
|
2021-08-03 17:39:14 +02:00
|
|
|
|
2023-06-20 20:09:29 +02:00
|
|
|
if (peer_id == SPA_ID_INVALID) {
|
|
|
|
|
if (mix == NULL) {
|
|
|
|
|
res = -ENOENT;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
free_mix(c, mix);
|
2021-10-01 09:30:39 +02:00
|
|
|
} else {
|
2023-06-20 20:09:29 +02:00
|
|
|
if (mix != NULL) {
|
|
|
|
|
res = -EEXIST;
|
|
|
|
|
goto exit;
|
2021-12-07 15:27:18 +01:00
|
|
|
}
|
2023-06-20 20:09:29 +02:00
|
|
|
mix = create_mix(c, p, mix_id, peer_id);
|
2021-10-01 09:30:39 +02:00
|
|
|
}
|
2021-08-03 17:39:14 +02:00
|
|
|
exit:
|
|
|
|
|
if (res < 0)
|
|
|
|
|
pw_proxy_error((struct pw_proxy*)c->node, res, spa_strerror(res));
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-11 16:13:46 +01:00
|
|
|
static const struct pw_client_node_events client_node_events = {
|
|
|
|
|
PW_VERSION_CLIENT_NODE_EVENTS,
|
2018-02-09 18:23:36 +01:00
|
|
|
.transport = client_node_transport,
|
|
|
|
|
.set_param = client_node_set_param,
|
2018-07-12 15:32:13 +02:00
|
|
|
.set_io = client_node_set_io,
|
2018-02-09 18:23:36 +01:00
|
|
|
.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,
|
2019-02-12 17:40:46 +01:00
|
|
|
.set_activation = client_node_set_activation,
|
2021-08-03 17:39:14 +02:00
|
|
|
.port_set_mix_info = client_node_port_set_mix_info,
|
2018-02-09 18:23:36 +01:00
|
|
|
};
|
|
|
|
|
|
2022-03-29 11:37:47 +02:00
|
|
|
#define CHECK(expression,label) \
|
|
|
|
|
do { \
|
|
|
|
|
if ((errno = expression) != 0) { \
|
|
|
|
|
res = -errno; \
|
|
|
|
|
pw_log_error(#expression ": %s", strerror(errno)); \
|
|
|
|
|
goto label; \
|
|
|
|
|
} \
|
|
|
|
|
} while(false);
|
|
|
|
|
|
2022-04-16 22:38:38 +02:00
|
|
|
static struct spa_thread *impl_create(void *object,
|
2022-03-29 11:37:47 +02:00
|
|
|
const struct spa_dict *props,
|
|
|
|
|
void *(*start)(void*), void *arg)
|
|
|
|
|
{
|
2022-04-16 22:38:38 +02:00
|
|
|
struct client *c = (struct client *) object;
|
2022-03-29 11:37:47 +02:00
|
|
|
struct spa_thread *thr;
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
|
|
pw_log_info("create thread");
|
|
|
|
|
if (globals.creator != NULL) {
|
|
|
|
|
pthread_t pt;
|
2022-04-18 13:02:27 +02:00
|
|
|
pthread_attr_t *attr = NULL, attributes;
|
2022-03-29 11:37:47 +02:00
|
|
|
|
2022-04-18 13:02:27 +02:00
|
|
|
attr = pw_thread_fill_attr(props, &attributes);
|
2022-03-29 11:37:47 +02:00
|
|
|
|
2022-04-18 13:02:27 +02:00
|
|
|
res = -globals.creator(&pt, attr, start, arg);
|
|
|
|
|
if (attr)
|
|
|
|
|
pthread_attr_destroy(attr);
|
2022-03-29 11:37:47 +02:00
|
|
|
if (res != 0)
|
|
|
|
|
goto error;
|
|
|
|
|
thr = (struct spa_thread*)pt;
|
|
|
|
|
} else {
|
2022-04-18 11:52:22 +02:00
|
|
|
thr = spa_thread_utils_create(c->context.old_thread_utils, props, start, arg);
|
2022-03-29 11:37:47 +02:00
|
|
|
}
|
|
|
|
|
return thr;
|
|
|
|
|
error:
|
|
|
|
|
pw_log_warn("create RT thread failed: %s", strerror(res));
|
|
|
|
|
errno = -res;
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-16 22:38:38 +02:00
|
|
|
static int impl_join(void *object,
|
2022-03-29 11:37:47 +02:00
|
|
|
struct spa_thread *thread, void **retval)
|
|
|
|
|
{
|
2022-04-16 22:38:38 +02:00
|
|
|
struct client *c = (struct client *) object;
|
2022-03-29 11:37:47 +02:00
|
|
|
pw_log_info("join thread");
|
2022-03-30 20:31:42 +02:00
|
|
|
return spa_thread_utils_join(c->context.old_thread_utils, thread, retval);
|
2022-03-29 11:37:47 +02:00
|
|
|
}
|
|
|
|
|
|
2022-04-16 22:38:38 +02:00
|
|
|
static int impl_acquire_rt(void *object, struct spa_thread *thread, int priority)
|
2022-03-29 11:37:47 +02:00
|
|
|
{
|
2022-04-16 22:38:38 +02:00
|
|
|
struct client *c = (struct client *) object;
|
2022-03-30 20:31:42 +02:00
|
|
|
return spa_thread_utils_acquire_rt(c->context.old_thread_utils, thread, priority);
|
2022-03-29 11:37:47 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-13 11:56:28 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-30 20:31:42 +02:00
|
|
|
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,
|
2022-10-13 11:56:28 +02:00
|
|
|
.drop_rt = impl_drop_rt,
|
2022-03-29 11:37:47 +02:00
|
|
|
};
|
|
|
|
|
|
2018-02-20 15:50:55 +01:00
|
|
|
static jack_port_type_id_t string_to_type(const char *port_type)
|
|
|
|
|
{
|
2021-05-18 11:40:50 +10:00
|
|
|
if (spa_streq(JACK_DEFAULT_AUDIO_TYPE, port_type))
|
2020-03-02 16:50:37 +01:00
|
|
|
return TYPE_ID_AUDIO;
|
2021-05-18 11:40:50 +10:00
|
|
|
else if (spa_streq(JACK_DEFAULT_MIDI_TYPE, port_type))
|
2020-03-02 16:50:37 +01:00
|
|
|
return TYPE_ID_MIDI;
|
2021-05-18 11:40:50 +10:00
|
|
|
else if (spa_streq(JACK_DEFAULT_VIDEO_TYPE, port_type))
|
2020-03-02 16:50:37 +01:00
|
|
|
return TYPE_ID_VIDEO;
|
2021-05-18 11:40:50 +10:00
|
|
|
else if (spa_streq("other", port_type))
|
2020-03-02 16:50:37 +01:00
|
|
|
return TYPE_ID_OTHER;
|
2018-02-20 15:50:55 +01:00
|
|
|
else
|
|
|
|
|
return SPA_ID_INVALID;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-21 13:12:26 +02:00
|
|
|
static const char* type_to_string(jack_port_type_id_t type_id)
|
|
|
|
|
{
|
|
|
|
|
switch(type_id) {
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_AUDIO:
|
2018-05-21 13:12:26 +02:00
|
|
|
return JACK_DEFAULT_AUDIO_TYPE;
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_MIDI:
|
2018-05-21 13:12:26 +02:00
|
|
|
return JACK_DEFAULT_MIDI_TYPE;
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_VIDEO:
|
2019-10-28 13:19:46 +01:00
|
|
|
return JACK_DEFAULT_VIDEO_TYPE;
|
2020-03-02 16:50:37 +01:00
|
|
|
case TYPE_ID_OTHER:
|
2018-05-21 13:12:26 +02:00
|
|
|
return "other";
|
|
|
|
|
default:
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 12:25:59 +01:00
|
|
|
static jack_uuid_t client_make_uuid(uint32_t id, bool monitor)
|
2020-07-30 12:10:46 +02:00
|
|
|
{
|
|
|
|
|
jack_uuid_t uuid = 0x2; /* JackUUIDClient */
|
|
|
|
|
uuid = (uuid << 32) | (id + 1);
|
2021-12-23 12:25:59 +01:00
|
|
|
if (monitor)
|
|
|
|
|
uuid |= (1 << 30);
|
2020-07-30 12:10:46 +02:00
|
|
|
pw_log_debug("uuid %d -> %"PRIu64, id, uuid);
|
|
|
|
|
return uuid;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-05 17:37:12 +01:00
|
|
|
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;
|
|
|
|
|
|
2022-01-04 10:42:32 +01:00
|
|
|
while (spa_json_get_string(&it[1], k, sizeof(k)) > 0) {
|
2021-05-18 11:36:13 +10:00
|
|
|
if (spa_streq(k, key)) {
|
2021-03-05 17:37:12 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static int metadata_property(void *data, uint32_t id,
|
2019-11-05 10:01:43 +01:00
|
|
|
const char *key, const char *type, const char *value)
|
|
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2020-07-30 12:10:46 +02:00
|
|
|
struct object *o;
|
2019-11-05 10:01:43 +01:00
|
|
|
jack_uuid_t uuid;
|
|
|
|
|
|
2021-03-07 10:45:45 +01:00
|
|
|
pw_log_debug("set id:%u key:'%s' value:'%s' type:'%s'", id, key, value, type);
|
2019-11-05 10:01:43 +01:00
|
|
|
|
2020-08-18 17:18:37 +02:00
|
|
|
if (id == PW_ID_CORE) {
|
2021-05-18 11:36:13 +10:00
|
|
|
if (key == NULL || spa_streq(key, "default.audio.sink")) {
|
2021-03-05 17:37:12 +01:00
|
|
|
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';
|
|
|
|
|
}
|
2021-05-18 11:36:13 +10:00
|
|
|
if (key == NULL || spa_streq(key, "default.audio.source")) {
|
2021-03-05 17:37:12 +01:00
|
|
|
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';
|
|
|
|
|
}
|
2020-08-18 17:18:37 +02:00
|
|
|
} else {
|
2021-09-03 15:46:21 +02:00
|
|
|
if ((o = find_id(c, id, true)) == NULL)
|
2020-08-18 17:18:37 +02:00
|
|
|
return -EINVAL;
|
2020-07-30 12:10:46 +02:00
|
|
|
|
2020-08-18 17:18:37 +02:00
|
|
|
switch (o->type) {
|
|
|
|
|
case INTERFACE_Node:
|
2022-01-26 17:44:49 +01:00
|
|
|
uuid = client_make_uuid(o->serial, false);
|
2020-08-18 17:18:37 +02:00
|
|
|
break;
|
|
|
|
|
case INTERFACE_Port:
|
2022-01-26 17:44:49 +01:00
|
|
|
uuid = jack_port_uuid_generate(o->serial);
|
2020-08-18 17:18:37 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
update_property(c, uuid, key, type, value);
|
2020-07-30 12:10:46 +02:00
|
|
|
}
|
2019-11-05 10:01:43 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct pw_metadata_events metadata_events = {
|
|
|
|
|
PW_VERSION_METADATA_EVENTS,
|
|
|
|
|
.property = metadata_property
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-31 15:04:08 +01:00
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
|
2022-10-20 16:34:47 +02:00
|
|
|
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,
|
|
|
|
|
};
|
2021-05-21 10:02:34 +02:00
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
|
2023-08-02 17:30:35 +02:00
|
|
|
static void node_info(void *data, const struct pw_node_info *info)
|
|
|
|
|
{
|
|
|
|
|
struct object *n = data;
|
|
|
|
|
struct client *c = n->client;
|
2023-08-07 19:58:20 +02:00
|
|
|
const char *str;
|
2023-08-02 17:30:35 +02:00
|
|
|
|
2023-08-07 19:58:20 +02:00
|
|
|
if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS) {
|
|
|
|
|
str = spa_dict_lookup(info->props, PW_KEY_NODE_ALWAYS_PROCESS);
|
|
|
|
|
n->node.is_jack = str ? spa_atob(str) : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n->node.is_running = !n->node.is_jack || (info->state == PW_NODE_STATE_RUNNING);
|
2023-08-02 17:30:35 +02:00
|
|
|
|
2023-08-07 19:58:20 +02:00
|
|
|
pw_log_debug("DSP node %d %08"PRIx64" jack:%u state change %s running:%d", info->id,
|
|
|
|
|
info->change_mask, n->node.is_jack,
|
|
|
|
|
pw_node_state_as_string(info->state), n->node.is_running);
|
2023-08-02 17:30:35 +02:00
|
|
|
|
|
|
|
|
if (info->change_mask & PW_NODE_CHANGE_MASK_STATE) {
|
2023-09-12 15:48:42 +02:00
|
|
|
struct object *p, *l;
|
2023-08-02 17:30:35 +02:00
|
|
|
spa_list_for_each(p, &c->context.objects, link) {
|
|
|
|
|
if (p->type != INTERFACE_Port || p->removed ||
|
|
|
|
|
p->port.node_id != info->id)
|
|
|
|
|
continue;
|
|
|
|
|
if (n->node.is_running)
|
|
|
|
|
queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, p, 1, NULL);
|
2023-09-12 15:48:42 +02:00
|
|
|
else {
|
|
|
|
|
spa_list_for_each(l, &c->context.objects, link) {
|
|
|
|
|
if (l->type != INTERFACE_Link || l->removed ||
|
|
|
|
|
(l->port_link.src_serial != p->serial &&
|
|
|
|
|
l->port_link.dst_serial != p->serial))
|
|
|
|
|
continue;
|
|
|
|
|
queue_notify(c, NOTIFY_TYPE_CONNECT, l, 0, NULL);
|
|
|
|
|
}
|
2023-08-02 17:30:35 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, p, 0, NULL);
|
2023-09-12 15:48:42 +02:00
|
|
|
}
|
2023-08-02 17:30:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct pw_node_events node_events = {
|
|
|
|
|
PW_VERSION_NODE,
|
|
|
|
|
.info = node_info,
|
|
|
|
|
};
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static void port_param(void *data, int seq,
|
2021-05-21 10:02:34 +02:00
|
|
|
uint32_t id, uint32_t index, uint32_t next,
|
|
|
|
|
const struct spa_pod *param)
|
|
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct object *o = data;
|
2021-05-21 10:02:34 +02:00
|
|
|
|
|
|
|
|
switch (id) {
|
|
|
|
|
case SPA_PARAM_Latency:
|
|
|
|
|
{
|
2021-05-24 18:01:19 +02:00
|
|
|
struct spa_latency_info info;
|
|
|
|
|
if (spa_latency_parse(param, &info) < 0)
|
2021-05-21 10:02:34 +02:00
|
|
|
return;
|
2021-05-24 18:01:19 +02:00
|
|
|
o->port.latency[info.direction] = info;
|
2021-05-21 10:02:34 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct pw_port_events port_events = {
|
|
|
|
|
PW_VERSION_PORT,
|
|
|
|
|
.param = port_param,
|
|
|
|
|
};
|
|
|
|
|
|
2021-02-23 16:13:08 +01:00
|
|
|
#define FILTER_NAME " ()[].:*$"
|
|
|
|
|
#define FILTER_PORT " ()[].*$"
|
|
|
|
|
|
2022-11-10 17:40:25 +01:00
|
|
|
static void filter_name(char *str, const char *filter, char filter_char)
|
2021-02-23 16:13:08 +01:00
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
for (p = str; *p; p++) {
|
|
|
|
|
if (strchr(filter, *p) != NULL)
|
2022-11-10 17:40:25 +01:00
|
|
|
*p = filter_char;
|
2021-02-23 16:13:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-16 22:10:38 +02:00
|
|
|
static void registry_event_global(void *data, uint32_t id,
|
2019-12-19 13:15:10 +01:00
|
|
|
uint32_t permissions, const char *type, uint32_t version,
|
2018-02-09 18:23:36 +01:00
|
|
|
const struct spa_dict *props)
|
|
|
|
|
{
|
|
|
|
|
struct client *c = (struct client *) data;
|
2020-07-20 16:27:26 +02:00
|
|
|
struct object *o, *ot, *op;
|
2018-02-09 18:23:36 +01:00
|
|
|
const char *str;
|
2023-08-02 17:30:35 +02:00
|
|
|
bool do_emit = true;
|
2022-01-14 21:04:08 +01:00
|
|
|
uint32_t serial;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
|
|
|
|
if (props == NULL)
|
2019-03-01 14:02:59 +01:00
|
|
|
return;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2022-01-14 21:04:08 +01:00
|
|
|
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);
|
|
|
|
|
|
2021-05-18 11:36:13 +10:00
|
|
|
if (spa_streq(type, PW_TYPE_INTERFACE_Node)) {
|
2020-08-05 12:26:59 +02:00
|
|
|
const char *app, *node_name;
|
2021-02-23 16:13:08 +01:00
|
|
|
char tmp[JACK_CLIENT_NAME_SIZE+1];
|
2020-07-20 16:54:38 +02:00
|
|
|
|
2021-03-30 12:41:59 +02:00
|
|
|
o = alloc_object(c, INTERFACE_Node);
|
2023-04-21 15:37:52 +02:00
|
|
|
if (o == NULL)
|
|
|
|
|
goto exit;
|
2019-10-22 12:21:37 +02:00
|
|
|
|
2020-02-05 10:27:37 +01:00
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_CLIENT_ID)) != NULL)
|
|
|
|
|
o->node.client_id = atoi(str);
|
|
|
|
|
|
2020-08-05 12:26:59 +02:00
|
|
|
node_name = spa_dict_lookup(props, PW_KEY_NODE_NAME);
|
|
|
|
|
|
2021-03-05 17:37:12 +01:00
|
|
|
snprintf(o->node.node_name, sizeof(o->node.node_name),
|
|
|
|
|
"%s", node_name);
|
2020-08-05 12:26:59 +02:00
|
|
|
|
2020-07-20 16:54:38 +02:00
|
|
|
app = spa_dict_lookup(props, PW_KEY_APP_NAME);
|
|
|
|
|
|
2021-02-23 16:13:08 +01:00
|
|
|
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);
|
2019-08-12 12:28:54 +02:00
|
|
|
}
|
2021-02-23 16:13:08 +01:00
|
|
|
if (str == NULL)
|
|
|
|
|
str = node_name;
|
|
|
|
|
if (str == NULL)
|
|
|
|
|
str = "node";
|
|
|
|
|
|
2021-05-18 11:43:49 +10:00
|
|
|
if (app && !spa_streq(app, str))
|
2021-02-23 16:13:08 +01:00
|
|
|
snprintf(tmp, sizeof(tmp), "%s/%s", app, str);
|
2020-07-20 16:54:38 +02:00
|
|
|
else
|
2021-02-23 16:13:08 +01:00
|
|
|
snprintf(tmp, sizeof(tmp), "%s", str);
|
|
|
|
|
|
|
|
|
|
if (c->filter_name)
|
2022-11-10 17:40:25 +01:00
|
|
|
filter_name(tmp, FILTER_NAME, c->filter_char);
|
2020-07-20 16:54:38 +02:00
|
|
|
|
2021-02-23 16:13:08 +01:00
|
|
|
ot = find_node(c, tmp);
|
2021-03-30 12:41:59 +02:00
|
|
|
if (ot != NULL && o->node.client_id != ot->node.client_id) {
|
2021-02-23 16:13:08 +01:00
|
|
|
snprintf(o->node.name, sizeof(o->node.name), "%.*s-%d",
|
|
|
|
|
(int)(sizeof(tmp)-11), tmp, id);
|
2021-03-30 12:41:59 +02:00
|
|
|
} else {
|
2023-08-02 17:30:35 +02:00
|
|
|
do_emit = ot == NULL;
|
2021-02-23 16:13:08 +01:00
|
|
|
snprintf(o->node.name, sizeof(o->node.name), "%s", tmp);
|
2021-03-30 12:41:59 +02:00
|
|
|
}
|
2023-05-09 11:28:49 +02:00
|
|
|
if (id == c->node_id) {
|
|
|
|
|
pw_log_debug("%p: add our node %d", c, id);
|
|
|
|
|
snprintf(c->name, sizeof(c->name), "%s", o->node.name);
|
2023-05-11 18:21:57 +02:00
|
|
|
c->object = o;
|
2023-05-09 11:28:49 +02:00
|
|
|
c->serial = serial;
|
|
|
|
|
}
|
2020-07-20 16:27:26 +02:00
|
|
|
|
2021-04-08 17:48:13 +02:00
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_PRIORITY_SESSION)) != NULL)
|
2019-10-22 12:21:37 +02:00
|
|
|
o->node.priority = pw_properties_parse_int(str);
|
2023-08-02 17:30:35 +02:00
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_CLIENT_API)) != NULL)
|
|
|
|
|
o->node.is_jack = spa_streq(str, "jack");
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: add node %d", c, id);
|
2020-04-30 12:57:56 +02:00
|
|
|
|
2023-08-02 17:30:35 +02:00
|
|
|
if (o->node.is_jack) {
|
|
|
|
|
o->proxy = pw_registry_bind(c->registry,
|
|
|
|
|
id, type, PW_VERSION_NODE, 0);
|
|
|
|
|
if (o->proxy) {
|
|
|
|
|
pw_proxy_add_listener(o->proxy,
|
|
|
|
|
&o->proxy_listener, &proxy_events, o);
|
|
|
|
|
pw_proxy_add_object_listener(o->proxy,
|
|
|
|
|
&o->object_listener, &node_events, o);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_append(&c->context.objects, &o->link);
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
2019-12-19 13:15:10 +01:00
|
|
|
}
|
2021-05-18 11:36:13 +10:00
|
|
|
else if (spa_streq(type, PW_TYPE_INTERFACE_Port)) {
|
2018-02-09 18:23:36 +01:00
|
|
|
const struct spa_dict_item *item;
|
2018-02-15 17:57:43 +01:00
|
|
|
unsigned long flags = 0;
|
2018-02-20 15:50:55 +01:00
|
|
|
jack_port_type_id_t type_id;
|
2019-08-21 18:37:02 +02:00
|
|
|
uint32_t node_id;
|
2021-01-05 15:38:13 +01:00
|
|
|
bool is_monitor = false;
|
2021-02-23 16:13:08 +01:00
|
|
|
char tmp[REAL_JACK_PORT_NAME_SIZE+1];
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2019-10-28 13:19:46 +01:00
|
|
|
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;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2019-08-16 22:10:38 +02:00
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_NODE_ID)) == NULL)
|
|
|
|
|
goto exit;
|
|
|
|
|
|
2019-08-21 18:37:02 +02:00
|
|
|
node_id = atoi(str);
|
2019-08-16 22:10:38 +02:00
|
|
|
|
2021-02-09 16:09:53 +01:00
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_PORT_EXTRA)) != NULL &&
|
2021-08-02 14:05:45 +10:00
|
|
|
spa_strstartswith(str, "jack:flags:"))
|
2021-02-09 16:09:53 +01:00
|
|
|
flags = atoi(str+11);
|
|
|
|
|
|
2019-05-24 15:47:28 +02:00
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_PORT_NAME)) == NULL)
|
2018-02-15 17:57:43 +01:00
|
|
|
goto exit;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
|
|
|
|
spa_dict_for_each(item, props) {
|
2023-02-25 20:48:45 +01:00
|
|
|
if (spa_streq(item->key, PW_KEY_PORT_DIRECTION)) {
|
2021-05-18 11:36:13 +10:00
|
|
|
if (spa_streq(item->value, "in"))
|
2018-02-15 17:57:43 +01:00
|
|
|
flags |= JackPortIsInput;
|
2021-05-18 11:36:13 +10:00
|
|
|
else if (spa_streq(item->value, "out"))
|
2018-02-15 17:57:43 +01:00
|
|
|
flags |= JackPortIsOutput;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
2021-05-18 11:40:50 +10:00
|
|
|
else if (spa_streq(item->key, PW_KEY_PORT_PHYSICAL)) {
|
2018-02-09 18:23:36 +01:00
|
|
|
if (pw_properties_parse_bool(item->value))
|
2018-02-15 17:57:43 +01:00
|
|
|
flags |= JackPortIsPhysical;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
2021-05-18 11:40:50 +10:00
|
|
|
else if (spa_streq(item->key, PW_KEY_PORT_TERMINAL)) {
|
2018-02-09 18:23:36 +01:00
|
|
|
if (pw_properties_parse_bool(item->value))
|
2018-02-15 17:57:43 +01:00
|
|
|
flags |= JackPortIsTerminal;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
2021-05-18 11:40:50 +10:00
|
|
|
else if (spa_streq(item->key, PW_KEY_PORT_CONTROL)) {
|
2018-10-03 20:13:57 +02:00
|
|
|
if (pw_properties_parse_bool(item->value))
|
2020-03-02 16:50:37 +01:00
|
|
|
type_id = TYPE_ID_MIDI;
|
2018-10-03 20:13:57 +02:00
|
|
|
}
|
2021-05-18 11:40:50 +10:00
|
|
|
else if (spa_streq(item->key, PW_KEY_PORT_MONITOR)) {
|
2021-01-05 15:38:13 +01:00
|
|
|
is_monitor = pw_properties_parse_bool(item->value);
|
|
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
2021-10-29 09:27:46 +02:00
|
|
|
if (is_monitor && !c->show_monitor)
|
|
|
|
|
goto exit;
|
2023-04-21 15:04:45 +02:00
|
|
|
if (type_id == TYPE_ID_MIDI && !c->show_midi)
|
|
|
|
|
goto exit;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2018-05-21 13:12:26 +02:00
|
|
|
o = NULL;
|
2019-08-21 18:37:02 +02:00
|
|
|
if (node_id == c->node_id) {
|
2021-02-23 21:11:05 +01:00
|
|
|
snprintf(tmp, sizeof(tmp), "%s:%s", c->name, str);
|
2022-01-14 21:04:08 +01:00
|
|
|
o = find_port_by_name(c, tmp);
|
2018-05-21 13:12:26 +02:00
|
|
|
if (o != NULL)
|
2022-01-13 13:45:53 +01:00
|
|
|
pw_log_info("%p: %s found our port %p", c, tmp, o);
|
2018-02-15 17:57:43 +01:00
|
|
|
}
|
2018-05-21 13:12:26 +02:00
|
|
|
if (o == NULL) {
|
2022-01-13 13:45:53 +01:00
|
|
|
if ((ot = find_type(c, node_id, INTERFACE_Node, true)) == NULL)
|
|
|
|
|
goto exit;
|
|
|
|
|
|
2021-03-30 12:41:59 +02:00
|
|
|
o = alloc_object(c, INTERFACE_Port);
|
2018-02-15 17:57:43 +01:00
|
|
|
if (o == NULL)
|
|
|
|
|
goto exit;
|
|
|
|
|
|
2021-11-08 09:23:20 +01:00
|
|
|
o->port.system_id = 0;
|
2019-10-22 12:21:37 +02:00
|
|
|
o->port.priority = ot->node.priority;
|
2021-03-05 17:37:12 +01:00
|
|
|
o->port.node = ot;
|
2021-05-25 17:55:13 +02:00
|
|
|
o->port.latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
|
|
|
|
|
o->port.latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT);
|
2021-05-21 10:02:34 +02:00
|
|
|
|
2023-08-02 17:30:35 +02:00
|
|
|
do_emit = !ot->node.is_jack || ot->node.is_running;
|
|
|
|
|
|
2021-05-21 10:02:34 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2022-01-13 13:45:53 +01:00
|
|
|
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)
|
2022-11-10 17:40:25 +01:00
|
|
|
filter_name(tmp, FILTER_PORT, c->filter_char);
|
2022-01-13 13:45:53 +01:00
|
|
|
|
2022-01-14 21:04:08 +01:00
|
|
|
op = find_port_by_name(c, tmp);
|
2022-01-13 13:45:53 +01:00
|
|
|
if (op != NULL)
|
2022-01-14 21:04:08 +01:00
|
|
|
snprintf(o->port.name, sizeof(o->port.name), "%.*s-%u",
|
|
|
|
|
(int)(sizeof(tmp)-11), tmp, serial);
|
2022-01-13 13:45:53 +01:00
|
|
|
else
|
|
|
|
|
snprintf(o->port.name, sizeof(o->port.name), "%s", tmp);
|
2018-02-15 17:57:43 +01:00
|
|
|
}
|
2018-02-16 12:08:16 +01:00
|
|
|
|
2023-05-12 17:55:00 +02:00
|
|
|
if (c->fill_aliases) {
|
|
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_OBJECT_PATH)) != NULL)
|
|
|
|
|
snprintf(o->port.alias1, sizeof(o->port.alias1), "%s", str);
|
2018-02-16 12:08:16 +01:00
|
|
|
|
2023-05-12 17:55:00 +02:00
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_PORT_ALIAS)) != NULL)
|
|
|
|
|
snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", str);
|
|
|
|
|
}
|
2021-02-17 11:16:02 +01:00
|
|
|
|
2021-11-08 09:23:20 +01:00
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_PORT_ID)) != NULL) {
|
|
|
|
|
o->port.system_id = atoi(str);
|
2021-02-17 11:16:02 +01:00
|
|
|
snprintf(o->port.system, sizeof(o->port.system), "system:%s_%d",
|
2021-06-24 20:31:55 +02:00
|
|
|
flags & JackPortIsInput ? "playback" :
|
|
|
|
|
is_monitor ? "monitor" : "capture",
|
2021-11-08 09:23:20 +01:00
|
|
|
o->port.system_id+1);
|
|
|
|
|
}
|
2018-02-20 15:50:55 +01:00
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
o->port.flags = flags;
|
2018-02-20 15:50:55 +01:00
|
|
|
o->port.type_id = type_id;
|
2019-08-21 18:37:02 +02:00
|
|
|
o->port.node_id = node_id;
|
2021-01-05 15:38:13 +01:00
|
|
|
o->port.is_monitor = is_monitor;
|
2018-02-20 15:50:55 +01:00
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
pw_log_debug("%p: %p add port %d name:%s %d", c, o, id,
|
2020-07-23 11:59:13 +02:00
|
|
|
o->port.name, type_id);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
2021-05-18 11:36:13 +10:00
|
|
|
else if (spa_streq(type, PW_TYPE_INTERFACE_Link)) {
|
2021-10-01 09:30:39 +02:00
|
|
|
struct object *p;
|
|
|
|
|
|
2021-03-30 12:41:59 +02:00
|
|
|
o = alloc_object(c, INTERFACE_Link);
|
2023-04-21 15:37:52 +02:00
|
|
|
if (o == NULL)
|
|
|
|
|
goto exit;
|
2019-12-19 13:15:10 +01:00
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_append(&c->context.objects, &o->link);
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2019-05-24 15:47:28 +02:00
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_LINK_OUTPUT_PORT)) == NULL)
|
2018-02-15 17:57:43 +01:00
|
|
|
goto exit_free;
|
|
|
|
|
o->port_link.src = pw_properties_parse_int(str);
|
|
|
|
|
|
2021-10-01 09:30:39 +02:00
|
|
|
if ((p = find_type(c, o->port_link.src, INTERFACE_Port, true)) == NULL)
|
2021-06-22 11:16:45 +02:00
|
|
|
goto exit_free;
|
2022-01-14 21:04:08 +01:00
|
|
|
o->port_link.src_serial = p->serial;
|
2021-06-22 11:16:45 +02:00
|
|
|
|
2021-10-01 09:53:35 +02:00
|
|
|
o->port_link.src_ours = p->port.port != NULL &&
|
|
|
|
|
p->port.port->client == c;
|
2021-12-07 15:27:18 +01:00
|
|
|
if (o->port_link.src_ours)
|
|
|
|
|
o->port_link.our_output = p->port.port;
|
2021-10-01 09:30:39 +02:00
|
|
|
|
2019-05-24 15:47:28 +02:00
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_LINK_INPUT_PORT)) == NULL)
|
2018-02-15 17:57:43 +01:00
|
|
|
goto exit_free;
|
|
|
|
|
o->port_link.dst = pw_properties_parse_int(str);
|
|
|
|
|
|
2021-10-01 09:30:39 +02:00
|
|
|
if ((p = find_type(c, o->port_link.dst, INTERFACE_Port, true)) == NULL)
|
2021-06-22 11:16:45 +02:00
|
|
|
goto exit_free;
|
2022-01-14 21:04:08 +01:00
|
|
|
o->port_link.dst_serial = p->serial;
|
2021-06-22 11:16:45 +02:00
|
|
|
|
2021-10-01 09:53:35 +02:00
|
|
|
o->port_link.dst_ours = p->port.port != NULL &&
|
|
|
|
|
p->port.port->client == c;
|
2021-12-07 15:27:18 +01:00
|
|
|
if (o->port_link.dst_ours)
|
|
|
|
|
o->port_link.our_input = p->port.port;
|
2021-10-01 09:30:39 +02:00
|
|
|
|
2023-06-20 20:09:29 +02:00
|
|
|
if (o->port_link.our_input != NULL &&
|
|
|
|
|
o->port_link.our_output != NULL) {
|
|
|
|
|
struct mix *mix;
|
|
|
|
|
mix = find_port_peer(o->port_link.our_output, o->port_link.dst);
|
|
|
|
|
if (mix != NULL)
|
|
|
|
|
mix->peer_port = o->port_link.our_input;
|
|
|
|
|
mix = find_port_peer(o->port_link.our_input, o->port_link.src);
|
|
|
|
|
if (mix != NULL)
|
|
|
|
|
mix->peer_port = o->port_link.our_output;
|
|
|
|
|
}
|
2022-01-14 21:04:08 +01:00
|
|
|
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);
|
2019-12-19 13:15:10 +01:00
|
|
|
}
|
2021-05-18 11:36:13 +10:00
|
|
|
else if (spa_streq(type, PW_TYPE_INTERFACE_Metadata)) {
|
2019-11-05 10:01:43 +01:00
|
|
|
struct pw_proxy *proxy;
|
|
|
|
|
|
2021-06-16 11:45:14 +02:00
|
|
|
if (c->metadata != NULL)
|
|
|
|
|
goto exit;
|
2022-10-20 16:34:47 +02:00
|
|
|
if ((str = spa_dict_lookup(props, PW_KEY_METADATA_NAME)) == NULL)
|
2019-11-05 10:01:43 +01:00
|
|
|
goto exit;
|
|
|
|
|
|
2022-10-20 16:34:47 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2019-11-05 10:01:43 +01:00
|
|
|
goto exit;
|
|
|
|
|
}
|
2019-12-19 13:15:10 +01:00
|
|
|
else {
|
2018-02-15 17:57:43 +01:00
|
|
|
goto exit;
|
2019-05-24 15:47:28 +02:00
|
|
|
}
|
2018-02-15 17:57:43 +01:00
|
|
|
|
|
|
|
|
o->id = id;
|
2022-01-14 21:04:08 +01:00
|
|
|
o->serial = serial;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2019-12-19 13:15:10 +01:00
|
|
|
switch (o->type) {
|
|
|
|
|
case INTERFACE_Node:
|
2023-08-02 17:30:35 +02:00
|
|
|
pw_log_info("%p: client added \"%s\" emit:%d", c, o->node.name, do_emit);
|
|
|
|
|
if (do_emit)
|
2023-05-11 18:21:57 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_REGISTRATION, o, 1, NULL);
|
2019-05-24 15:47:28 +02:00
|
|
|
break;
|
|
|
|
|
|
2019-12-19 13:15:10 +01:00
|
|
|
case INTERFACE_Port:
|
2023-08-02 17:30:35 +02:00
|
|
|
pw_log_info("%p: port added %u/%u \"%s\" emit:%d", c, o->id,
|
|
|
|
|
o->serial, o->port.name, do_emit);
|
|
|
|
|
if (do_emit)
|
|
|
|
|
queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 1, NULL);
|
2019-05-24 15:47:28 +02:00
|
|
|
break;
|
|
|
|
|
|
2019-12-19 13:15:10 +01:00
|
|
|
case INTERFACE_Link:
|
2023-06-20 20:09:29 +02:00
|
|
|
pw_log_info("%p: link %u %u/%u -> %u/%u added", c,
|
2022-01-14 21:04:08 +01:00
|
|
|
o->id, o->port_link.src, o->port_link.src_serial,
|
2023-06-20 20:09:29 +02:00
|
|
|
o->port_link.dst, o->port_link.dst_serial);
|
2023-08-03 11:43:42 +02:00
|
|
|
if (do_emit)
|
|
|
|
|
queue_notify(c, NOTIFY_TYPE_CONNECT, o, 1, NULL);
|
2019-05-24 15:47:28 +02:00
|
|
|
break;
|
2018-02-15 17:57:43 +01:00
|
|
|
}
|
2023-05-10 12:10:03 +02:00
|
|
|
emit_callbacks(c);
|
2019-09-09 17:16:18 +02:00
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
exit:
|
2019-03-01 14:02:59 +01:00
|
|
|
return;
|
2018-02-15 17:57:43 +01:00
|
|
|
exit_free:
|
|
|
|
|
free_object(c, o);
|
2019-03-01 14:02:59 +01:00
|
|
|
return;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:09:32 +02:00
|
|
|
static void registry_event_global_remove(void *data, uint32_t id)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2021-09-27 15:09:32 +02:00
|
|
|
struct client *c = (struct client *) data;
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: removed: %u", c, id);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2021-09-03 15:46:21 +02:00
|
|
|
if ((o = find_id(c, id, true)) == NULL)
|
2019-03-01 14:02:59 +01:00
|
|
|
return;
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2021-05-21 10:02:34 +02:00
|
|
|
if (o->proxy) {
|
|
|
|
|
pw_proxy_destroy(o->proxy);
|
|
|
|
|
o->proxy = NULL;
|
|
|
|
|
}
|
2021-07-27 12:07:33 +02:00
|
|
|
o->removing = true;
|
2021-03-30 12:41:59 +02:00
|
|
|
|
2019-05-24 15:47:28 +02:00
|
|
|
switch (o->type) {
|
2019-12-19 13:15:10 +01:00
|
|
|
case INTERFACE_Node:
|
2021-03-05 17:37:12 +01:00
|
|
|
if (c->metadata) {
|
2021-05-18 11:36:13 +10:00
|
|
|
if (spa_streq(o->node.node_name, c->metadata->default_audio_sink))
|
2021-03-05 17:37:12 +01:00
|
|
|
c->metadata->default_audio_sink[0] = '\0';
|
2021-05-18 11:36:13 +10:00
|
|
|
if (spa_streq(o->node.node_name, c->metadata->default_audio_source))
|
2021-03-05 17:37:12 +01:00
|
|
|
c->metadata->default_audio_source[0] = '\0';
|
|
|
|
|
}
|
2021-06-21 11:25:17 +02:00
|
|
|
if (find_node(c, o->node.name) == NULL) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: client %u removed \"%s\"", c, o->id, o->node.name);
|
2023-05-11 18:21:57 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_REGISTRATION, o, 0, NULL);
|
|
|
|
|
} else {
|
|
|
|
|
free_object(c, o);
|
2021-06-21 11:25:17 +02:00
|
|
|
}
|
2019-05-24 15:47:28 +02:00
|
|
|
break;
|
2019-12-19 13:15:10 +01:00
|
|
|
case INTERFACE_Port:
|
2022-01-14 21:04:08 +01:00
|
|
|
pw_log_info("%p: port %u/%u removed \"%s\"", c, o->id, o->serial, o->port.name);
|
2023-05-11 18:21:57 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 0, NULL);
|
2019-05-24 15:47:28 +02:00
|
|
|
break;
|
2019-12-19 13:15:10 +01:00
|
|
|
case INTERFACE_Link:
|
2023-06-20 20:09:29 +02:00
|
|
|
if (find_type(c, o->port_link.src, INTERFACE_Port, true) != NULL &&
|
2021-09-03 15:46:21 +02:00
|
|
|
find_type(c, o->port_link.dst, INTERFACE_Port, true) != NULL) {
|
2022-01-14 21:04:08 +01:00
|
|
|
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);
|
2023-05-11 18:21:57 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_CONNECT, o, 0, NULL);
|
|
|
|
|
} else {
|
2021-06-22 11:16:45 +02:00
|
|
|
pw_log_warn("unlink between unknown ports %d and %d",
|
|
|
|
|
o->port_link.src, o->port_link.dst);
|
2023-05-11 18:21:57 +02:00
|
|
|
free_object(c, o);
|
|
|
|
|
}
|
2019-05-24 15:47:28 +02:00
|
|
|
break;
|
2018-02-15 17:57:43 +01:00
|
|
|
}
|
2023-05-10 12:10:03 +02:00
|
|
|
emit_callbacks(c);
|
2021-03-30 12:41:59 +02:00
|
|
|
|
2019-03-01 14:02:59 +01:00
|
|
|
return;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-11 09:44:48 +01:00
|
|
|
static const struct pw_registry_events registry_events = {
|
|
|
|
|
PW_VERSION_REGISTRY_EVENTS,
|
2018-02-09 18:23:36 +01:00
|
|
|
.global = registry_event_global,
|
|
|
|
|
.global_remove = registry_event_global_remove,
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-23 13:18:14 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-27 15:50:05 +02:00
|
|
|
|
2022-02-04 10:43:31 +01:00
|
|
|
static int execute_match(void *data, const char *location, const char *action,
|
|
|
|
|
const char *val, size_t len)
|
2021-07-27 15:50:05 +02:00
|
|
|
{
|
|
|
|
|
struct client *client = data;
|
|
|
|
|
if (spa_streq(action, "update-props"))
|
|
|
|
|
pw_properties_update_string(client->props, val, len);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_client_t * jack_client_open (const char *client_name,
|
|
|
|
|
jack_options_t options,
|
|
|
|
|
jack_status_t *status, ...)
|
|
|
|
|
{
|
|
|
|
|
struct client *client;
|
2019-01-28 17:10:37 +01:00
|
|
|
const struct spa_support *support;
|
|
|
|
|
uint32_t n_support;
|
2018-09-21 16:47:44 +02:00
|
|
|
const char *str;
|
2019-01-28 17:10:37 +01:00
|
|
|
struct spa_cpu *cpu_iface;
|
2023-07-13 12:08:36 +02:00
|
|
|
const struct pw_properties *props;
|
2020-07-23 13:18:14 +02:00
|
|
|
va_list ap;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2020-11-19 12:37:10 +01:00
|
|
|
if (getenv("PIPEWIRE_NOJACK") != NULL ||
|
|
|
|
|
getenv("PIPEWIRE_INTERNAL") != NULL ||
|
|
|
|
|
strstr(pw_get_library_version(), "0.2") != NULL)
|
2018-12-11 16:36:36 +01:00
|
|
|
goto disabled;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(client_name != NULL, NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2018-02-09 18:23:36 +01:00
|
|
|
client = calloc(1, sizeof(struct client));
|
|
|
|
|
if (client == NULL)
|
2020-11-23 03:56:57 -05:00
|
|
|
goto disabled;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: open '%s' options:%d", client, client_name, options);
|
2019-08-08 11:02:40 +02:00
|
|
|
|
2020-07-23 13:18:14 +02:00
|
|
|
va_start(ap, status);
|
|
|
|
|
varargs_parse(client, options, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
2022-06-03 01:05:43 +02:00
|
|
|
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);
|
|
|
|
|
|
2021-03-04 18:02:11 +01:00
|
|
|
if (client->server_name != NULL &&
|
2021-05-18 11:36:13 +10:00
|
|
|
spa_streq(client->server_name, "default"))
|
2021-03-04 18:02:11 +01:00
|
|
|
client->server_name = NULL;
|
|
|
|
|
|
2021-02-20 21:02:05 +01:00
|
|
|
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);
|
2021-02-10 11:22:43 +01:00
|
|
|
if (client->props == NULL)
|
|
|
|
|
goto no_props;
|
|
|
|
|
|
2021-06-28 12:19:15 +02:00
|
|
|
client->context.loop = pw_thread_loop_new(client->name, NULL);
|
2021-10-06 20:19:11 +02:00
|
|
|
client->context.l = pw_thread_loop_get_loop(client->context.loop);
|
2020-03-05 12:10:13 +01:00
|
|
|
client->context.context = pw_context_new(
|
2020-08-11 11:11:00 +02:00
|
|
|
client->context.l,
|
2021-02-10 11:22:43 +01:00
|
|
|
pw_properties_copy(client->props),
|
2020-03-05 12:10:13 +01:00
|
|
|
0);
|
2021-02-11 21:01:58 +01:00
|
|
|
if (client->context.context == NULL)
|
|
|
|
|
goto no_props;
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
client->notify_source = pw_loop_add_event(client->context.l,
|
|
|
|
|
on_notify_event, client);
|
|
|
|
|
client->notify_buffer = calloc(1, NOTIFY_BUFFER_SIZE + sizeof(struct notify));
|
|
|
|
|
spa_ringbuffer_init(&client->notify_ring);
|
2023-05-02 14:41:46 +02:00
|
|
|
|
2022-02-01 15:11:45 +01:00
|
|
|
pw_context_conf_update_props(client->context.context,
|
|
|
|
|
"jack.properties", client->props);
|
2021-02-16 13:23:47 +01:00
|
|
|
|
2023-07-13 12:08:36 +02:00
|
|
|
props = pw_context_get_properties(client->context.context);
|
|
|
|
|
|
|
|
|
|
client->allow_mlock = pw_properties_get_bool(props, "mem.allow-mlock", true);
|
|
|
|
|
client->warn_mlock = pw_properties_get_bool(props, "mem.warn-mlock", false);
|
|
|
|
|
|
2022-02-04 10:43:31 +01:00
|
|
|
pw_context_conf_section_match_rules(client->context.context, "jack.rules",
|
2023-07-13 12:08:36 +02:00
|
|
|
&props->dict, execute_match, client);
|
2021-07-27 15:50:05 +02:00
|
|
|
|
2019-12-10 18:19:56 +01:00
|
|
|
support = pw_context_get_support(client->context.context, &n_support);
|
2019-01-28 17:10:37 +01:00
|
|
|
|
2022-07-10 21:59:05 +02:00
|
|
|
mix_function = mix_c;
|
2019-01-28 17:10:37 +01:00
|
|
|
cpu_iface = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
|
|
|
|
|
if (cpu_iface) {
|
|
|
|
|
#if defined (__SSE__)
|
2019-06-07 17:15:10 +02:00
|
|
|
uint32_t flags = spa_cpu_get_flags(cpu_iface);
|
2019-01-28 17:10:37 +01:00
|
|
|
if (flags & SPA_CPU_FLAG_SSE)
|
2022-07-10 21:59:05 +02:00
|
|
|
mix_function = mix_sse;
|
2019-01-28 17:10:37 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
2022-03-30 20:31:42 +02:00
|
|
|
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();
|
|
|
|
|
|
2022-10-13 12:09:36 +02:00
|
|
|
globals.thread_utils = client->context.old_thread_utils;
|
|
|
|
|
|
2022-03-30 20:31:42 +02:00
|
|
|
client->context.thread_utils.iface = SPA_INTERFACE_INIT(
|
|
|
|
|
SPA_TYPE_INTERFACE_ThreadUtils,
|
|
|
|
|
SPA_VERSION_THREAD_UTILS,
|
|
|
|
|
&thread_utils_impl, client);
|
2019-09-09 17:16:18 +02:00
|
|
|
|
2023-04-28 11:12:02 +02:00
|
|
|
client->loop = pw_context_get_data_loop(client->context.context);
|
2023-07-11 19:31:27 +02:00
|
|
|
client->l = pw_data_loop_get_loop(client->loop);
|
2022-03-29 11:36:31 +02:00
|
|
|
pw_data_loop_stop(client->loop);
|
2022-03-30 20:31:42 +02:00
|
|
|
|
|
|
|
|
pw_context_set_object(client->context.context,
|
|
|
|
|
SPA_TYPE_INTERFACE_ThreadUtils,
|
|
|
|
|
&client->context.thread_utils);
|
2022-03-29 11:36:31 +02:00
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
pw_thread_loop_start(client->context.loop);
|
|
|
|
|
|
|
|
|
|
pw_thread_loop_lock(client->context.loop);
|
2019-12-06 11:48:40 +01:00
|
|
|
|
2019-12-11 07:46:59 +01:00
|
|
|
client->core = pw_context_connect(client->context.context,
|
2021-02-10 11:22:43 +01:00
|
|
|
pw_properties_copy(client->props), 0);
|
2019-12-11 07:46:59 +01:00
|
|
|
if (client->core == NULL)
|
2018-02-15 17:57:43 +01:00
|
|
|
goto server_failed;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2019-12-11 07:46:59 +01:00
|
|
|
client->pool = pw_core_get_mempool(client->core);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2019-12-11 07:46:59 +01:00
|
|
|
pw_core_add_listener(client->core,
|
2020-01-16 13:29:56 +01:00
|
|
|
&client->core_listener,
|
|
|
|
|
&core_events, client);
|
2019-12-11 09:44:48 +01:00
|
|
|
client->registry = pw_core_get_registry(client->core,
|
2020-01-16 13:29:56 +01:00
|
|
|
PW_VERSION_REGISTRY, 0);
|
2019-12-11 09:44:48 +01:00
|
|
|
pw_registry_add_listener(client->registry,
|
2020-01-16 13:29:56 +01:00
|
|
|
&client->registry_listener,
|
|
|
|
|
®istry_events, client);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2022-04-22 09:37:21 +02:00
|
|
|
if ((str = getenv("PIPEWIRE_PROPS")) != NULL)
|
|
|
|
|
pw_properties_update_string(client->props, str, strlen(str));
|
2022-01-23 11:21:18 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-22 09:37:21 +02:00
|
|
|
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);
|
2022-11-21 16:02:30 +01:00
|
|
|
if ((str = getenv("PIPEWIRE_LINK_PASSIVE")) != NULL)
|
2023-03-08 16:51:40 +01:00
|
|
|
pw_properties_set(client->props, "jack.passive-links", str);
|
2022-04-22 09:37:21 +02:00
|
|
|
|
2021-03-12 15:58:34 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-11 12:12:37 +02:00
|
|
|
if (pw_properties_get(client->props, PW_KEY_NODE_NAME) == NULL)
|
|
|
|
|
pw_properties_set(client->props, PW_KEY_NODE_NAME, client_name);
|
2022-08-31 16:01:11 +02:00
|
|
|
if (pw_properties_get(client->props, PW_KEY_NODE_GROUP) == NULL)
|
|
|
|
|
pw_properties_setf(client->props, PW_KEY_NODE_GROUP, "jack-%d", getpid());
|
2020-08-11 12:12:37 +02:00
|
|
|
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");
|
2022-02-08 18:35:12 +01:00
|
|
|
if (pw_properties_get(client->props, PW_KEY_NODE_LOCK_QUANTUM) == NULL)
|
|
|
|
|
pw_properties_set(client->props, PW_KEY_NODE_LOCK_QUANTUM, "true");
|
2022-01-18 20:34:03 +01:00
|
|
|
pw_properties_set(client->props, PW_KEY_NODE_TRANSPORT_SYNC, "true");
|
2020-07-21 16:50:45 +02:00
|
|
|
|
2019-12-11 16:13:46 +01:00
|
|
|
client->node = pw_core_create_object(client->core,
|
2018-02-09 18:23:36 +01:00
|
|
|
"client-node",
|
2018-08-29 09:54:21 +02:00
|
|
|
PW_TYPE_INTERFACE_ClientNode,
|
2018-02-09 18:23:36 +01:00
|
|
|
PW_VERSION_CLIENT_NODE,
|
2020-07-21 16:50:45 +02:00
|
|
|
&client->props->dict,
|
2018-02-09 18:23:36 +01:00
|
|
|
0);
|
2019-12-11 16:13:46 +01:00
|
|
|
if (client->node == NULL)
|
2018-02-09 18:23:36 +01:00
|
|
|
goto init_failed;
|
|
|
|
|
|
2022-06-03 01:17:50 +02:00
|
|
|
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;
|
|
|
|
|
|
2022-05-04 15:01:01 +02:00
|
|
|
client->show_monitor = pw_properties_get_bool(client->props, "jack.show-monitor", true);
|
2023-04-21 15:04:45 +02:00
|
|
|
client->show_midi = pw_properties_get_bool(client->props, "jack.show-midi", true);
|
2023-01-25 09:48:28 +01:00
|
|
|
client->merge_monitor = pw_properties_get_bool(client->props, "jack.merge-monitor", true);
|
2022-05-04 15:01:01 +02:00
|
|
|
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);
|
2023-03-08 16:51:40 +01:00
|
|
|
client->passive_links = pw_properties_get_bool(client->props, "jack.passive-links", false);
|
2022-11-10 17:40:25 +01:00
|
|
|
client->filter_char = ' ';
|
|
|
|
|
if ((str = pw_properties_get(client->props, "jack.filter-char")) != NULL && str[0] != '\0')
|
|
|
|
|
client->filter_char = str[0];
|
2022-05-04 15:01:01 +02:00
|
|
|
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);
|
2022-06-20 15:51:33 +02:00
|
|
|
client->fix_midi_events = pw_properties_get_bool(client->props, "jack.fix-midi-events", true);
|
2022-10-20 16:34:47 +02:00
|
|
|
client->global_buffer_size = pw_properties_get_bool(client->props, "jack.global-buffer-size", false);
|
2023-04-21 15:37:52 +02:00
|
|
|
client->max_ports = pw_properties_get_uint32(client->props, "jack.max-client-ports", MAX_CLIENT_PORTS);
|
2023-05-12 17:55:00 +02:00
|
|
|
client->fill_aliases = pw_properties_get_bool(client->props, "jack.fill-aliases", false);
|
2022-05-04 15:01:01 +02:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2020-08-05 12:26:59 +02:00
|
|
|
if (status)
|
|
|
|
|
*status = 0;
|
|
|
|
|
|
2020-02-07 13:30:24 +01:00
|
|
|
while (true) {
|
|
|
|
|
pw_thread_loop_wait(client->context.loop);
|
|
|
|
|
|
2022-03-26 21:35:28 +01:00
|
|
|
if (client->last_res < 0)
|
2020-02-07 13:30:24 +01:00
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
if (client->has_transport)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2021-05-18 11:43:49 +10:00
|
|
|
if (!spa_streq(client->name, client_name)) {
|
2020-08-05 12:26:59 +02:00
|
|
|
if (status)
|
|
|
|
|
*status |= JackNameNotUnique;
|
|
|
|
|
if (options & JackUseExactName)
|
2021-06-09 17:57:05 +02:00
|
|
|
goto exit_unlock;
|
2020-08-05 12:26:59 +02:00
|
|
|
}
|
2018-02-13 18:05:55 +01:00
|
|
|
pw_thread_loop_unlock(client->context.loop);
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: opened", client);
|
2018-02-09 18:23:36 +01:00
|
|
|
return (jack_client_t *)client;
|
|
|
|
|
|
2021-02-10 11:22:43 +01:00
|
|
|
no_props:
|
2018-02-15 17:57:43 +01:00
|
|
|
if (status)
|
|
|
|
|
*status = JackFailure | JackInitFailure;
|
2018-02-13 18:05:55 +01:00
|
|
|
goto exit;
|
2021-02-10 11:22:43 +01:00
|
|
|
init_failed:
|
|
|
|
|
if (status)
|
|
|
|
|
*status = JackFailure | JackInitFailure;
|
|
|
|
|
goto exit_unlock;
|
2020-02-05 10:27:37 +01:00
|
|
|
server_failed:
|
2018-02-15 17:57:43 +01:00
|
|
|
if (status)
|
|
|
|
|
*status = JackFailure | JackServerFailed;
|
2021-02-10 11:22:43 +01:00
|
|
|
goto exit_unlock;
|
|
|
|
|
exit_unlock:
|
2018-02-13 18:05:55 +01:00
|
|
|
pw_thread_loop_unlock(client->context.loop);
|
2021-02-10 11:22:43 +01:00
|
|
|
exit:
|
2022-06-03 01:26:58 +02:00
|
|
|
jack_client_close((jack_client_t *) client);
|
2018-02-09 18:23:36 +01:00
|
|
|
return NULL;
|
2020-02-05 10:27:37 +01:00
|
|
|
disabled:
|
2023-05-05 15:41:18 +02:00
|
|
|
pw_log_warn("JACK is disabled");
|
2018-12-11 16:36:36 +01:00
|
|
|
if (status)
|
2020-11-19 12:37:10 +01:00
|
|
|
*status = JackFailure | JackInitFailure;
|
2018-12-11 16:36:36 +01:00
|
|
|
return NULL;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
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)
|
2018-09-21 16:47:44 +02:00
|
|
|
options |= JackNoStartServer;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
|
|
|
|
return jack_client_open(client_name, options, &status, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_client_close (jack_client_t *client)
|
|
|
|
|
{
|
|
|
|
|
struct client *c = (struct client *) client;
|
2021-08-25 11:56:19 +02:00
|
|
|
struct object *o;
|
2020-01-16 13:29:56 +01:00
|
|
|
int res;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: close", client);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2020-01-16 13:29:56 +01:00
|
|
|
c->destroyed = true;
|
|
|
|
|
|
|
|
|
|
res = jack_deactivate(client);
|
2019-11-05 10:01:43 +01:00
|
|
|
|
2022-08-09 20:55:41 +02:00
|
|
|
clean_transport(c);
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
if (c->context.loop) {
|
|
|
|
|
queue_notify(c, NOTIFY_TYPE_REGISTRATION, c->object, 0, NULL);
|
|
|
|
|
pw_loop_invoke(c->context.l, NULL, 0, NULL, 0, false, c);
|
2022-06-03 01:08:35 +02:00
|
|
|
pw_thread_loop_stop(c->context.loop);
|
2023-05-11 18:21:57 +02:00
|
|
|
}
|
2020-06-04 13:08:39 +02:00
|
|
|
|
2022-02-09 15:52:14 +01:00
|
|
|
if (c->registry) {
|
|
|
|
|
spa_hook_remove(&c->registry_listener);
|
2020-06-04 12:59:20 +02:00
|
|
|
pw_proxy_destroy((struct pw_proxy*)c->registry);
|
2022-02-09 15:52:14 +01:00
|
|
|
}
|
2020-07-17 17:23:43 +02:00
|
|
|
if (c->metadata && c->metadata->proxy) {
|
2020-06-04 12:59:20 +02:00
|
|
|
pw_proxy_destroy((struct pw_proxy*)c->metadata->proxy);
|
2020-07-17 17:23:43 +02:00
|
|
|
}
|
2022-10-20 16:34:47 +02:00
|
|
|
if (c->settings && c->settings->proxy) {
|
|
|
|
|
pw_proxy_destroy((struct pw_proxy*)c->settings->proxy);
|
|
|
|
|
}
|
2020-04-30 12:57:56 +02:00
|
|
|
|
2022-06-03 01:08:35 +02:00
|
|
|
if (c->core) {
|
|
|
|
|
spa_hook_remove(&c->core_listener);
|
|
|
|
|
pw_core_disconnect(c->core);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-12 13:30:32 +02:00
|
|
|
globals.thread_utils = pw_thread_utils_get();
|
|
|
|
|
|
2022-06-03 01:08:35 +02:00
|
|
|
if (c->context.context)
|
|
|
|
|
pw_context_destroy(c->context.context);
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
if (c->notify_source)
|
|
|
|
|
pw_loop_destroy_source(c->context.l, c->notify_source);
|
|
|
|
|
free(c->notify_buffer);
|
2023-05-02 14:41:46 +02:00
|
|
|
|
2022-06-03 01:08:35 +02:00
|
|
|
if (c->context.loop)
|
|
|
|
|
pw_thread_loop_destroy(c->context.loop);
|
2019-07-23 17:46:06 +02:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: free", client);
|
2021-08-25 11:56:19 +02:00
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_consume(o, &c->context.objects, link)
|
2021-08-25 11:56:19 +02:00
|
|
|
free_object(c, o);
|
2022-01-13 13:45:53 +01:00
|
|
|
recycle_objects(c, 0);
|
2021-08-25 11:56:19 +02:00
|
|
|
|
2022-01-22 20:55:40 +01:00
|
|
|
pw_map_clear(&c->ports[SPA_DIRECTION_INPUT]);
|
|
|
|
|
pw_map_clear(&c->ports[SPA_DIRECTION_OUTPUT]);
|
|
|
|
|
|
2020-04-30 13:11:01 +02:00
|
|
|
pthread_mutex_destroy(&c->context.lock);
|
2021-06-17 10:46:04 +02:00
|
|
|
pthread_mutex_destroy(&c->rt_lock);
|
2020-07-21 16:50:45 +02:00
|
|
|
pw_properties_free(c->props);
|
2018-02-09 18:23:36 +01:00
|
|
|
free(c);
|
|
|
|
|
|
2020-01-16 13:29:56 +01:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2021-11-22 12:52:15 +01:00
|
|
|
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;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, 0);
|
2021-11-22 12:52:15 +01:00
|
|
|
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;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, 0);
|
2021-11-22 12:52:15 +01:00
|
|
|
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;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, 0);
|
2021-11-22 12:52:15 +01:00
|
|
|
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;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, NULL);
|
2021-11-22 12:52:15 +01:00
|
|
|
return strdup(c->name);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_client_name_size (void)
|
|
|
|
|
{
|
2022-08-20 09:41:02 +03:00
|
|
|
/* 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;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
char * jack_get_client_name (jack_client_t *client)
|
|
|
|
|
{
|
|
|
|
|
struct client *c = (struct client *) client;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, NULL);
|
2018-02-09 18:23:36 +01:00
|
|
|
return c->name;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
char *jack_get_uuid_for_client_name (jack_client_t *client,
|
|
|
|
|
const char *client_name)
|
|
|
|
|
{
|
2019-08-21 18:37:02 +02:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
struct object *o;
|
2020-04-30 12:57:56 +02:00
|
|
|
char *uuid = NULL;
|
2021-09-03 15:45:50 +02:00
|
|
|
bool monitor;
|
2019-08-21 18:37:02 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, NULL);
|
|
|
|
|
return_val_if_fail(client_name != NULL, NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-09-03 15:45:50 +02:00
|
|
|
monitor = spa_strendswith(client_name, MONITOR_EXT);
|
|
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
|
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_for_each(o, &c->context.objects, link) {
|
|
|
|
|
if (o->type != INTERFACE_Node)
|
|
|
|
|
continue;
|
2021-09-03 15:45:50 +02:00
|
|
|
if (spa_streq(o->node.name, client_name) ||
|
|
|
|
|
(monitor && spa_strneq(o->node.name, client_name,
|
|
|
|
|
strlen(client_name) - strlen(MONITOR_EXT)))) {
|
2022-01-26 17:44:49 +01:00
|
|
|
uuid = spa_aprintf( "%" PRIu64, client_make_uuid(o->serial, monitor));
|
2020-04-30 12:57:56 +02:00
|
|
|
break;
|
2019-08-21 18:37:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: name %s -> %s", client, client_name, uuid);
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
|
|
|
|
return uuid;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
char *jack_get_client_name_by_uuid (jack_client_t *client,
|
|
|
|
|
const char *client_uuid )
|
|
|
|
|
{
|
2019-08-21 18:37:02 +02:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
struct object *o;
|
|
|
|
|
jack_uuid_t uuid;
|
2020-04-30 12:57:56 +02:00
|
|
|
char *name = NULL;
|
2021-12-23 12:25:59 +01:00
|
|
|
bool monitor;
|
2019-08-21 18:37:02 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, NULL);
|
|
|
|
|
return_val_if_fail(client_uuid != NULL, NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-21 18:37:02 +02:00
|
|
|
if (jack_uuid_parse(client_uuid, &uuid) < 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2021-12-23 12:25:59 +01:00
|
|
|
monitor = uuid & (1 << 30);
|
|
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_for_each(o, &c->context.objects, link) {
|
|
|
|
|
if (o->type != INTERFACE_Node)
|
|
|
|
|
continue;
|
2022-01-26 17:44:49 +01:00
|
|
|
if (client_make_uuid(o->serial, monitor) == uuid) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: uuid %s (%"PRIu64")-> %s",
|
2019-08-21 18:37:02 +02:00
|
|
|
client, client_uuid, uuid, o->node.name);
|
2021-12-23 12:25:59 +01:00
|
|
|
name = spa_aprintf("%s%s", o->node.name, monitor ? MONITOR_EXT : "");
|
2020-04-30 12:57:56 +02:00
|
|
|
break;
|
2019-08-21 18:37:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
|
|
|
|
return name;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_internal_client_new (const char *client_name,
|
|
|
|
|
const char *load_name,
|
|
|
|
|
const char *load_init)
|
|
|
|
|
{
|
2019-08-08 11:02:40 +02:00
|
|
|
pw_log_warn("not implemented %s %s %s", client_name, load_name, load_init);
|
2018-05-21 13:12:26 +02:00
|
|
|
return -ENOTSUP;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void jack_internal_client_close (const char *client_name)
|
|
|
|
|
{
|
2019-08-08 11:02:40 +02:00
|
|
|
pw_log_warn("not implemented %s", client_name);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-08-29 15:32:30 +02:00
|
|
|
static int do_activate(struct client *c)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2019-08-29 14:01:20 +02:00
|
|
|
int res;
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_client_node_set_active(c->node, true);
|
2018-02-13 18:05:55 +01:00
|
|
|
res = do_sync(c);
|
2019-08-29 15:32:30 +02:00
|
|
|
return res;
|
|
|
|
|
}
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2019-08-29 15:32:30 +02:00
|
|
|
SPA_EXPORT
|
|
|
|
|
int jack_activate (jack_client_t *client)
|
|
|
|
|
{
|
|
|
|
|
struct client *c = (struct client *) client;
|
2023-04-24 12:55:56 +02:00
|
|
|
struct object *o;
|
2021-06-17 10:46:04 +02:00
|
|
|
int res = 0;
|
2019-08-29 15:32:30 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: active:%d", c, c->active);
|
2021-09-14 10:55:12 +02:00
|
|
|
|
2019-08-29 15:32:30 +02:00
|
|
|
if (c->active)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2021-06-17 10:46:04 +02:00
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2023-05-10 12:10:03 +02:00
|
|
|
freeze_callbacks(c);
|
2023-05-02 14:41:46 +02:00
|
|
|
|
2023-09-12 15:51:02 +02:00
|
|
|
/* reemit buffer_frames */
|
|
|
|
|
c->buffer_frames = 0;
|
|
|
|
|
|
2022-03-29 11:36:31 +02:00
|
|
|
pw_data_loop_start(c->loop);
|
2021-06-17 10:46:04 +02:00
|
|
|
|
2019-08-29 15:32:30 +02:00
|
|
|
if ((res = do_activate(c)) < 0)
|
2021-06-17 10:46:04 +02:00
|
|
|
goto done;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
c->activation->pending_new_pos = true;
|
|
|
|
|
c->activation->pending_sync = true;
|
2021-09-16 11:29:13 +02:00
|
|
|
c->active = true;
|
2021-06-17 10:46:04 +02:00
|
|
|
|
2023-04-24 12:55:56 +02:00
|
|
|
spa_list_for_each(o, &c->context.objects, link) {
|
|
|
|
|
if (o->type != INTERFACE_Port || o->port.port == NULL ||
|
|
|
|
|
o->port.port->client != c || !o->port.port->valid)
|
|
|
|
|
continue;
|
2023-09-12 15:51:45 +02:00
|
|
|
o->registered = 0;
|
2023-05-11 18:21:57 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 1, NULL);
|
2023-04-24 12:55:56 +02:00
|
|
|
}
|
2021-06-17 10:46:04 +02:00
|
|
|
done:
|
2022-03-29 11:36:31 +02:00
|
|
|
if (res < 0)
|
|
|
|
|
pw_data_loop_stop(c->loop);
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
pw_log_debug("%p: activate result:%d", c, res);
|
2023-05-10 12:10:03 +02:00
|
|
|
thaw_callbacks(c);
|
2021-06-17 10:46:04 +02:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
2020-02-25 18:02:49 +01:00
|
|
|
|
2022-03-29 11:36:31 +02:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_deactivate (jack_client_t *client)
|
|
|
|
|
{
|
2023-04-24 12:55:56 +02:00
|
|
|
struct object *o;
|
2018-02-13 18:05:55 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2019-08-29 14:01:20 +02:00
|
|
|
int res;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: active:%d", c, c->active);
|
2021-09-14 10:55:12 +02:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (!c->active)
|
|
|
|
|
return 0;
|
2018-02-13 18:05:55 +01:00
|
|
|
|
|
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2023-05-10 12:10:03 +02:00
|
|
|
freeze_callbacks(c);
|
2023-03-29 16:18:26 +02:00
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pw_data_loop_stop(c->loop);
|
|
|
|
|
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_client_node_set_active(c->node, false);
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2023-04-24 12:55:56 +02:00
|
|
|
spa_list_for_each(o, &c->context.objects, link) {
|
|
|
|
|
if (o->type != INTERFACE_Link || o->removed)
|
2022-01-13 13:45:53 +01:00
|
|
|
continue;
|
2023-04-24 12:55:56 +02:00
|
|
|
if (o->port_link.src_ours || o->port_link.dst_ours)
|
|
|
|
|
pw_registry_destroy(c->registry, o->id);
|
2021-12-15 16:24:28 +01:00
|
|
|
}
|
|
|
|
|
|
2023-04-24 12:55:56 +02:00
|
|
|
spa_list_for_each(o, &c->context.objects, link) {
|
|
|
|
|
if (o->type != INTERFACE_Port || o->port.port == NULL ||
|
|
|
|
|
o->port.port->client != c || !o->port.port->valid)
|
|
|
|
|
continue;
|
2023-05-11 18:21:57 +02:00
|
|
|
queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 0, NULL);
|
2023-04-24 12:55:56 +02:00
|
|
|
}
|
2023-05-11 18:21:57 +02:00
|
|
|
c->activation->pending_new_pos = false;
|
|
|
|
|
c->activation->pending_sync = false;
|
|
|
|
|
|
|
|
|
|
c->active = false;
|
|
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
res = do_sync(c);
|
|
|
|
|
|
2023-05-10 12:10:03 +02:00
|
|
|
thaw_callbacks(c);
|
2018-02-13 18:05:55 +01:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
|
|
|
|
|
2023-03-29 16:18:26 +02:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_get_client_pid (const char *name)
|
|
|
|
|
{
|
2019-09-09 17:15:52 +02:00
|
|
|
pw_log_error("not implemented on library side");
|
|
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_native_thread_t jack_client_thread_id (jack_client_t *client)
|
|
|
|
|
{
|
2021-07-02 11:39:44 +02:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, (pthread_t){0});
|
2021-07-02 11:39:44 +02:00
|
|
|
|
2022-10-13 12:08:27 +02:00
|
|
|
return (jack_native_thread_t)pw_data_loop_get_thread(c->loop);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_is_realtime (jack_client_t *client)
|
|
|
|
|
{
|
2023-04-21 11:40:43 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, 0);
|
2023-04-21 11:40:43 +02:00
|
|
|
return !c->freewheeling;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_nframes_t jack_thread_wait (jack_client_t *client, int status)
|
|
|
|
|
{
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: jack_thread_wait: deprecated, use jack_cycle_wait/jack_cycle_signal", client);
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_nframes_t jack_cycle_wait (jack_client_t* client)
|
|
|
|
|
{
|
2019-09-02 12:02:34 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-09-07 16:16:26 +02:00
|
|
|
jack_nframes_t res;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, 0);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-09-02 17:18:02 +02:00
|
|
|
res = cycle_wait(c);
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_trace("%p: result:%d", c, res);
|
2019-09-02 17:18:02 +02:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void jack_cycle_signal (jack_client_t* client, int status)
|
|
|
|
|
{
|
2019-09-02 12:02:34 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_if_fail(c != NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_trace("%p: status:%d", c, status);
|
2019-09-02 12:02:34 +02:00
|
|
|
cycle_signal(c, status);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_process_thread(jack_client_t* client, JackThreadCallback thread_callback, void *arg)
|
|
|
|
|
{
|
2018-02-13 18:05:55 +01:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2018-02-13 18:05:55 +01:00
|
|
|
return -EIO;
|
|
|
|
|
} else if (c->process_callback) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: process callback was already set", c);
|
2018-02-13 18:05:55 +01:00
|
|
|
return -EIO;
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, thread_callback, arg);
|
2018-02-13 18:05:55 +01:00
|
|
|
c->thread_callback = thread_callback;
|
|
|
|
|
c->thread_arg = arg;
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_thread_init_callback (jack_client_t *client,
|
|
|
|
|
JackThreadInitCallback thread_init_callback,
|
|
|
|
|
void *arg)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, thread_init_callback, arg);
|
2018-02-15 17:57:43 +01:00
|
|
|
c->thread_init_callback = thread_init_callback;
|
|
|
|
|
c->thread_init_arg = arg;
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void jack_on_shutdown (jack_client_t *client,
|
|
|
|
|
JackShutdownCallback shutdown_callback, void *arg)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2019-08-29 14:01:20 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_if_fail(c != NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2019-08-29 14:01:20 +02:00
|
|
|
} else {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, shutdown_callback, arg);
|
2019-08-29 14:01:20 +02:00
|
|
|
c->shutdown_callback = shutdown_callback;
|
|
|
|
|
c->shutdown_arg = arg;
|
|
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void jack_on_info_shutdown (jack_client_t *client,
|
|
|
|
|
JackInfoShutdownCallback shutdown_callback, void *arg)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2019-08-29 14:01:20 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_if_fail(c != NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2019-08-29 14:01:20 +02:00
|
|
|
} else {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, shutdown_callback, arg);
|
2019-08-29 14:01:20 +02:00
|
|
|
c->info_shutdown_callback = shutdown_callback;
|
|
|
|
|
c->info_shutdown_arg = arg;
|
|
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_process_callback (jack_client_t *client,
|
|
|
|
|
JackProcessCallback process_callback,
|
|
|
|
|
void *arg)
|
|
|
|
|
{
|
2018-02-13 18:05:55 +01:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2018-02-13 18:05:55 +01:00
|
|
|
return -EIO;
|
|
|
|
|
} else if (c->thread_callback) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: thread callback was already set", c);
|
2018-02-13 18:05:55 +01:00
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, process_callback, arg);
|
2018-02-13 18:05:55 +01:00
|
|
|
c->process_callback = process_callback;
|
|
|
|
|
c->process_arg = arg;
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_freewheel_callback (jack_client_t *client,
|
|
|
|
|
JackFreewheelCallback freewheel_callback,
|
|
|
|
|
void *arg)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2019-08-29 14:01:20 +02:00
|
|
|
return -EIO;
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, freewheel_callback, arg);
|
2018-02-15 17:57:43 +01:00
|
|
|
c->freewheel_callback = freewheel_callback;
|
|
|
|
|
c->freewheel_arg = arg;
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_buffer_size_callback (jack_client_t *client,
|
|
|
|
|
JackBufferSizeCallback bufsize_callback,
|
|
|
|
|
void *arg)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2019-08-29 14:01:20 +02:00
|
|
|
return -EIO;
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, bufsize_callback, arg);
|
2018-02-15 17:57:43 +01:00
|
|
|
c->bufsize_callback = bufsize_callback;
|
|
|
|
|
c->bufsize_arg = arg;
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_sample_rate_callback (jack_client_t *client,
|
|
|
|
|
JackSampleRateCallback srate_callback,
|
|
|
|
|
void *arg)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2019-08-29 14:01:20 +02:00
|
|
|
return -EIO;
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, srate_callback, arg);
|
2018-02-15 17:57:43 +01:00
|
|
|
c->srate_callback = srate_callback;
|
|
|
|
|
c->srate_arg = arg;
|
2020-02-24 15:39:19 +01:00
|
|
|
if (c->srate_callback && c->sample_rate != (uint32_t)-1)
|
|
|
|
|
c->srate_callback(c->sample_rate, c->srate_arg);
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_client_registration_callback (jack_client_t *client,
|
|
|
|
|
JackClientRegistrationCallback
|
|
|
|
|
registration_callback, void *arg)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2019-08-29 14:01:20 +02:00
|
|
|
return -EIO;
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, registration_callback, arg);
|
2018-02-15 17:57:43 +01:00
|
|
|
c->registration_callback = registration_callback;
|
|
|
|
|
c->registration_arg = arg;
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_port_registration_callback (jack_client_t *client,
|
|
|
|
|
JackPortRegistrationCallback
|
|
|
|
|
registration_callback, void *arg)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2019-08-29 14:01:20 +02:00
|
|
|
return -EIO;
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, registration_callback, arg);
|
2018-02-15 17:57:43 +01:00
|
|
|
c->portregistration_callback = registration_callback;
|
|
|
|
|
c->portregistration_arg = arg;
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_port_connect_callback (jack_client_t *client,
|
|
|
|
|
JackPortConnectCallback
|
|
|
|
|
connect_callback, void *arg)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2019-08-29 14:01:20 +02:00
|
|
|
return -EIO;
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, connect_callback, arg);
|
2018-02-15 17:57:43 +01:00
|
|
|
c->connect_callback = connect_callback;
|
|
|
|
|
c->connect_arg = arg;
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_port_rename_callback (jack_client_t *client,
|
2019-08-29 14:01:20 +02:00
|
|
|
JackPortRenameCallback rename_callback,
|
|
|
|
|
void *arg)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2019-08-29 14:01:20 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2019-08-29 14:01:20 +02:00
|
|
|
return -EIO;
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, rename_callback, arg);
|
2019-08-29 14:01:20 +02:00
|
|
|
c->rename_callback = rename_callback;
|
|
|
|
|
c->rename_arg = arg;
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_graph_order_callback (jack_client_t *client,
|
|
|
|
|
JackGraphOrderCallback graph_callback,
|
|
|
|
|
void *data)
|
|
|
|
|
{
|
2018-02-16 12:08:16 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2020-02-24 11:25:31 +01:00
|
|
|
return -EIO;
|
2019-08-29 14:01:20 +02:00
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, graph_callback, data);
|
2018-02-16 12:08:16 +01:00
|
|
|
c->graph_callback = graph_callback;
|
|
|
|
|
c->graph_arg = data;
|
2018-02-09 18:23:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_xrun_callback (jack_client_t *client,
|
|
|
|
|
JackXRunCallback xrun_callback, void *arg)
|
|
|
|
|
{
|
2019-08-22 13:56:39 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2020-02-24 11:25:31 +01:00
|
|
|
return -EIO;
|
2019-08-29 14:01:20 +02:00
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, xrun_callback, arg);
|
2019-08-22 13:56:39 +02:00
|
|
|
c->xrun_callback = xrun_callback;
|
|
|
|
|
c->xrun_arg = arg;
|
2018-10-23 16:50:59 +02:00
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_latency_callback (jack_client_t *client,
|
|
|
|
|
JackLatencyCallback latency_callback,
|
|
|
|
|
void *data)
|
|
|
|
|
{
|
2019-08-29 14:01:20 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2019-08-29 14:01:20 +02:00
|
|
|
return -EIO;
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %p %p", c, latency_callback, data);
|
2019-09-02 17:16:57 +02:00
|
|
|
c->latency_callback = latency_callback;
|
|
|
|
|
c->latency_arg = data;
|
|
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_freewheel(jack_client_t* client, int onoff)
|
|
|
|
|
{
|
2021-05-06 18:23:46 +02:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: freewheel %d", client, onoff);
|
2021-05-06 18:23:46 +02:00
|
|
|
|
2021-06-05 20:16:10 +02:00
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2021-05-25 17:55:13 +02:00
|
|
|
pw_properties_set(c->props, "node.group",
|
2021-06-07 12:22:47 +02:00
|
|
|
onoff ? "pipewire.freewheel" : "");
|
2021-05-25 17:55:13 +02:00
|
|
|
|
|
|
|
|
c->info.change_mask |= SPA_NODE_CHANGE_MASK_PROPS;
|
|
|
|
|
c->info.props = &c->props->dict;
|
2021-05-06 18:23:46 +02:00
|
|
|
|
|
|
|
|
pw_client_node_update(c->node,
|
|
|
|
|
PW_CLIENT_NODE_UPDATE_INFO,
|
2021-05-25 17:55:13 +02:00
|
|
|
0, NULL, &c->info);
|
|
|
|
|
c->info.change_mask = 0;
|
2021-06-05 20:16:10 +02:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
2021-05-06 18:23:46 +02:00
|
|
|
|
|
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_set_buffer_size (jack_client_t *client, jack_nframes_t nframes)
|
|
|
|
|
{
|
2019-08-30 16:59:10 +02:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2022-02-11 12:22:02 +01:00
|
|
|
pw_log_info("%p: buffer-size %u", client, nframes);
|
2019-08-30 16:59:10 +02:00
|
|
|
|
2021-06-05 20:16:10 +02:00
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2022-10-20 16:34:47 +02:00
|
|
|
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);
|
2021-05-25 17:55:13 +02:00
|
|
|
|
2022-10-20 16:34:47 +02:00
|
|
|
c->info.change_mask |= SPA_NODE_CHANGE_MASK_PROPS;
|
|
|
|
|
c->info.props = &c->props->dict;
|
2019-08-30 16:59:10 +02:00
|
|
|
|
2022-10-20 16:34:47 +02:00
|
|
|
pw_client_node_update(c->node,
|
|
|
|
|
PW_CLIENT_NODE_UPDATE_INFO,
|
|
|
|
|
0, NULL, &c->info);
|
|
|
|
|
c->info.change_mask = 0;
|
|
|
|
|
}
|
2021-06-05 20:16:10 +02:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
2019-08-30 16:59:10 +02:00
|
|
|
|
|
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_nframes_t jack_get_sample_rate (jack_client_t *client)
|
|
|
|
|
{
|
2018-02-13 18:05:55 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2021-03-12 15:58:34 +01:00
|
|
|
jack_nframes_t res = -1;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, 0);
|
2021-03-12 15:58:34 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2020-12-09 20:48:59 +01:00
|
|
|
}
|
2023-05-18 09:46:40 +02:00
|
|
|
c->sample_rate = res;
|
2021-07-05 16:06:22 +02:00
|
|
|
pw_log_debug("sample_rate: %u", res);
|
2021-03-12 15:58:34 +01:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_nframes_t jack_get_buffer_size (jack_client_t *client)
|
|
|
|
|
{
|
2018-02-13 18:05:55 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2021-03-12 15:58:34 +01:00
|
|
|
jack_nframes_t res = -1;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, 0);
|
2021-03-12 15:58:34 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2020-12-09 20:48:59 +01:00
|
|
|
}
|
2022-01-07 17:12:43 +01:00
|
|
|
c->buffer_frames = res;
|
2021-07-05 16:06:22 +02:00
|
|
|
pw_log_debug("buffer_frames: %u", res);
|
2021-03-12 15:58:34 +01:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_engine_takeover_timebase (jack_client_t *client)
|
|
|
|
|
{
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: deprecated", client);
|
2019-08-27 21:35:28 +02:00
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
float jack_cpu_load (jack_client_t *client)
|
|
|
|
|
{
|
2019-08-22 10:59:16 +02:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
float res = 0.0f;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, 0.0);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-22 10:59:16 +02:00
|
|
|
if (c->driver_activation)
|
2019-08-22 13:56:39 +02:00
|
|
|
res = c->driver_activation->cpu_load[0] * 100.0f;
|
2019-08-22 10:59:16 +02:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_trace("%p: cpu load %f", client, res);
|
2019-08-22 10:59:16 +02:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-08-22 13:25:01 +02:00
|
|
|
#include "statistics.c"
|
|
|
|
|
|
2020-09-30 11:59:41 +02:00
|
|
|
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);
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_port_t * jack_port_register (jack_client_t *client,
|
|
|
|
|
const char *port_name,
|
|
|
|
|
const char *port_type,
|
|
|
|
|
unsigned long flags,
|
2019-10-24 10:23:01 +02:00
|
|
|
unsigned long buffer_frames)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
|
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
enum spa_direction direction;
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o;
|
|
|
|
|
jack_port_type_id_t type_id;
|
2018-02-09 18:23:36 +01:00
|
|
|
uint8_t buffer[1024];
|
|
|
|
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
2021-05-27 15:13:48 +02:00
|
|
|
struct spa_pod *params[6];
|
2018-10-03 20:13:57 +02:00
|
|
|
uint32_t n_params = 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
struct port *p;
|
2023-04-24 12:53:56 +02:00
|
|
|
int res, len;
|
|
|
|
|
char name[REAL_JACK_PORT_NAME_SIZE+1];
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, NULL);
|
|
|
|
|
return_val_if_fail(port_name != NULL && strlen(port_name) != 0, NULL);
|
|
|
|
|
return_val_if_fail(port_type != NULL, NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
pw_log_info("%p: port register \"%s:%s\" \"%s\" %08lx %ld",
|
|
|
|
|
c, c->name, port_name, port_type, flags, buffer_frames);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
|
|
|
|
if (flags & JackPortIsInput)
|
|
|
|
|
direction = PW_DIRECTION_INPUT;
|
|
|
|
|
else if (flags & JackPortIsOutput)
|
|
|
|
|
direction = PW_DIRECTION_OUTPUT;
|
2022-01-23 09:34:43 +01:00
|
|
|
else {
|
|
|
|
|
pw_log_warn("invalid port flags %lu for %s", flags, port_name);
|
2018-02-09 18:23:36 +01:00
|
|
|
return NULL;
|
2022-01-23 09:34:43 +01:00
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2022-01-23 09:34:43 +01:00
|
|
|
if ((type_id = string_to_type(port_type)) == SPA_ID_INVALID) {
|
|
|
|
|
pw_log_warn("unknown port type %s", port_type);
|
2018-02-09 18:23:36 +01:00
|
|
|
return NULL;
|
2022-01-23 09:34:43 +01:00
|
|
|
}
|
2023-04-24 12:53:56 +02:00
|
|
|
len = snprintf(name, sizeof(name), "%s:%s", c->name, port_name);
|
|
|
|
|
if (len < 0 || (size_t)len >= sizeof(name)) {
|
|
|
|
|
pw_log_warn("%p: name \"%s:%s\" too long", c,
|
|
|
|
|
c->name, port_name);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
pthread_mutex_lock(&c->context.lock);
|
|
|
|
|
o = find_port_by_name(c, name);
|
|
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
|
|
|
|
if (o != NULL) {
|
|
|
|
|
pw_log_warn("%p: name \"%s\" already exists", c, name);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2022-01-23 09:34:43 +01:00
|
|
|
if ((p = alloc_port(c, direction)) == NULL) {
|
|
|
|
|
pw_log_warn("can't allocate port %s: %m", port_name);
|
2018-02-09 18:23:36 +01:00
|
|
|
return NULL;
|
2022-01-23 09:34:43 +01:00
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
o = p->object;
|
|
|
|
|
o->port.flags = flags;
|
2023-04-24 12:53:56 +02:00
|
|
|
strcpy(o->port.name, name);
|
2018-02-15 17:57:43 +01:00
|
|
|
o->port.type_id = type_id;
|
|
|
|
|
|
2020-04-28 14:15:49 +02:00
|
|
|
init_buffer(p);
|
|
|
|
|
|
2020-09-30 11:59:41 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: port %p", c, p);
|
2019-10-24 10:23:01 +02:00
|
|
|
|
2018-05-21 13:12:26 +02:00
|
|
|
spa_list_init(&p->mix);
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2021-05-25 17:55:13 +02:00
|
|
|
pw_properties_set(p->props, PW_KEY_FORMAT_DSP, port_type);
|
|
|
|
|
pw_properties_set(p->props, PW_KEY_PORT_NAME, port_name);
|
2021-02-09 16:09:53 +01:00
|
|
|
if (flags > 0x1f) {
|
2021-05-25 17:55:13 +02:00
|
|
|
pw_properties_setf(p->props, PW_KEY_PORT_EXTRA,
|
|
|
|
|
"jack:flags:%lu", flags & ~0x1f);
|
2021-02-09 16:09:53 +01:00
|
|
|
}
|
|
|
|
|
if (flags & JackPortIsPhysical)
|
2021-05-25 17:55:13 +02:00
|
|
|
pw_properties_set(p->props, PW_KEY_PORT_PHYSICAL, "true");
|
2021-02-09 16:09:53 +01:00
|
|
|
if (flags & JackPortIsTerminal)
|
2021-05-25 17:55:13 +02:00
|
|
|
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;
|
2018-10-03 20:13:57 +02:00
|
|
|
|
|
|
|
|
param_enum_format(c, p, ¶ms[n_params++], &b);
|
|
|
|
|
param_buffers(c, p, ¶ms[n_params++], &b);
|
|
|
|
|
param_io(c, p, ¶ms[n_params++], &b);
|
2021-05-25 17:55:13 +02:00
|
|
|
param_latency(c, p, ¶ms[n_params++], &b);
|
2021-05-27 15:13:48 +02:00
|
|
|
param_latency_other(c, p, ¶ms[n_params++], &b);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2023-07-06 12:53:28 +02:00
|
|
|
if (create_mix(c, p, SPA_ID_INVALID, SPA_ID_INVALID) == NULL) {
|
|
|
|
|
res = -errno;
|
|
|
|
|
pw_log_warn("can't create mix for port %s: %m", port_name);
|
|
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
|
|
|
|
goto error_free;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-10 12:10:03 +02:00
|
|
|
freeze_callbacks(c);
|
2018-02-20 15:50:55 +01:00
|
|
|
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_client_node_port_update(c->node,
|
2018-02-09 18:23:36 +01:00
|
|
|
direction,
|
2022-01-22 20:55:40 +01:00
|
|
|
p->port_id,
|
2018-02-09 18:23:36 +01:00
|
|
|
PW_CLIENT_NODE_PORT_UPDATE_PARAMS |
|
2019-02-27 16:37:10 +01:00
|
|
|
PW_CLIENT_NODE_PORT_UPDATE_INFO,
|
2018-10-03 20:13:57 +02:00
|
|
|
n_params,
|
2018-02-09 18:23:36 +01:00
|
|
|
(const struct spa_pod **) params,
|
2021-05-25 17:55:13 +02:00
|
|
|
&p->info);
|
|
|
|
|
|
|
|
|
|
p->info.change_mask = 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
res = do_sync(c);
|
|
|
|
|
|
2023-05-10 12:10:03 +02:00
|
|
|
thaw_callbacks(c);
|
2023-05-10 16:49:41 +02:00
|
|
|
pw_log_debug("%p: port %p done", c, p);
|
2018-02-13 18:05:55 +01:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
|
|
|
|
|
2022-01-23 09:34:43 +01:00
|
|
|
if (res < 0) {
|
|
|
|
|
pw_log_warn("can't create port %s: %s", port_name,
|
|
|
|
|
spa_strerror(res));
|
2023-04-24 12:53:56 +02:00
|
|
|
goto error_free;
|
2022-01-23 09:34:43 +01:00
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
return (jack_port_t *) o;
|
2023-04-24 12:53:56 +02:00
|
|
|
|
|
|
|
|
error_free:
|
2023-05-10 16:49:41 +02:00
|
|
|
free_port(c, p, true);
|
2023-04-24 12:53:56 +02:00
|
|
|
return NULL;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
static int
|
|
|
|
|
do_free_port(struct spa_loop *loop,
|
|
|
|
|
bool async, uint32_t seq, const void *data, size_t size, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct port *p = user_data;
|
|
|
|
|
struct client *c = p->client;
|
|
|
|
|
free_port(c, p, !c->active);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-20 21:34:01 +02:00
|
|
|
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;
|
2023-05-11 18:21:57 +02:00
|
|
|
struct client *c = p->client;
|
2022-10-20 21:34:01 +02:00
|
|
|
p->valid = false;
|
2023-05-11 18:21:57 +02:00
|
|
|
pw_loop_invoke(c->context.l, do_free_port, 0, NULL, 0, false, p);
|
2022-10-20 21:34:01 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_unregister (jack_client_t *client, jack_port_t *port)
|
|
|
|
|
{
|
2020-02-24 11:25:31 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o = (struct object *) port;
|
|
|
|
|
struct port *p;
|
2018-02-13 18:05:55 +01:00
|
|
|
int res;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(o != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2023-05-10 12:10:03 +02:00
|
|
|
freeze_callbacks(c);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2022-01-22 20:55:40 +01:00
|
|
|
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);
|
2021-06-17 09:18:29 +02:00
|
|
|
res = -EINVAL;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
2023-05-11 18:21:57 +02:00
|
|
|
pw_data_loop_invoke(c->loop, do_invalidate_port, 1, NULL, 0, false, p);
|
2022-10-20 21:34:01 +02:00
|
|
|
|
2022-01-22 20:55:40 +01:00
|
|
|
pw_log_info("%p: port %p unregister \"%s\"", client, port, o->port.name);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_client_node_port_update(c->node,
|
2018-02-09 18:23:36 +01:00
|
|
|
p->direction,
|
2022-01-22 20:55:40 +01:00
|
|
|
p->port_id,
|
2018-02-09 18:23:36 +01:00
|
|
|
0, 0, NULL, NULL);
|
|
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
res = do_sync(c);
|
2022-01-23 10:16:42 +01:00
|
|
|
if (res < 0) {
|
|
|
|
|
pw_log_warn("can't unregister port %s: %s", o->port.name,
|
|
|
|
|
spa_strerror(res));
|
|
|
|
|
}
|
2021-06-17 09:18:29 +02:00
|
|
|
done:
|
2023-05-10 12:10:03 +02:00
|
|
|
thaw_callbacks(c);
|
2018-02-13 18:05:55 +01:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
|
|
|
|
|
|
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2021-12-07 15:27:18 +01:00
|
|
|
static struct buffer *get_mix_buffer(struct mix *mix, jack_nframes_t frames)
|
2021-08-03 17:39:14 +02:00
|
|
|
{
|
|
|
|
|
struct spa_io_buffers *io;
|
|
|
|
|
|
2021-12-07 15:27:18 +01:00
|
|
|
if (mix->peer_port != NULL)
|
|
|
|
|
prepare_output(mix->peer_port, frames);
|
|
|
|
|
|
2021-08-03 17:39:14 +02:00
|
|
|
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];
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-30 11:59:41 +02:00
|
|
|
static void *get_buffer_input_float(struct port *p, jack_nframes_t frames)
|
2018-10-29 14:29:59 +00:00
|
|
|
{
|
|
|
|
|
struct mix *mix;
|
|
|
|
|
struct buffer *b;
|
|
|
|
|
void *ptr = NULL;
|
2022-07-10 21:59:05 +02:00
|
|
|
float *mix_ptr[MAX_MIX], *np;
|
|
|
|
|
uint32_t n_ptr = 0;
|
|
|
|
|
bool ptr_aligned = true;
|
2018-10-29 14:29:59 +00:00
|
|
|
|
|
|
|
|
spa_list_for_each(mix, &p->mix, port_link) {
|
2021-11-25 15:17:42 +01:00
|
|
|
struct spa_data *d;
|
|
|
|
|
uint32_t offset, size;
|
|
|
|
|
|
2021-12-07 15:27:18 +01:00
|
|
|
pw_log_trace_fp("%p: port %s mix %d.%d get buffer %d",
|
2022-01-22 23:29:45 +01:00
|
|
|
p->client, p->object->port.name, p->port_id, mix->id, frames);
|
2021-08-03 17:39:14 +02:00
|
|
|
|
2021-12-07 15:27:18 +01:00
|
|
|
if ((b = get_mix_buffer(mix, frames)) == NULL)
|
2018-10-29 14:29:59 +00:00
|
|
|
continue;
|
|
|
|
|
|
2021-11-25 15:17:42 +01:00
|
|
|
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;
|
|
|
|
|
|
2022-07-10 21:59:05 +02:00
|
|
|
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];
|
2022-07-12 15:10:02 +02:00
|
|
|
} else if (n_ptr > 1) {
|
2022-07-10 21:59:05 +02:00
|
|
|
ptr = p->emptyptr;
|
|
|
|
|
mix_function(ptr, mix_ptr, n_ptr, ptr_aligned, frames);
|
|
|
|
|
p->zeroed = false;
|
2018-10-29 14:29:59 +00:00
|
|
|
}
|
2020-09-30 11:59:41 +02:00
|
|
|
if (ptr == NULL)
|
|
|
|
|
ptr = init_buffer(p);
|
2018-10-29 14:29:59 +00:00
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-30 11:59:41 +02:00
|
|
|
static void *get_buffer_input_midi(struct port *p, jack_nframes_t frames)
|
2018-10-29 14:29:59 +00:00
|
|
|
{
|
|
|
|
|
struct mix *mix;
|
2019-09-16 21:08:35 +02:00
|
|
|
void *ptr = p->emptyptr;
|
2022-07-10 21:59:05 +02:00
|
|
|
struct spa_pod_sequence *seq[MAX_MIX];
|
2019-09-26 11:56:25 +02:00
|
|
|
uint32_t n_seq = 0;
|
2019-09-16 21:08:35 +02:00
|
|
|
|
2019-10-28 15:03:31 +01:00
|
|
|
jack_midi_clear_buffer(ptr);
|
2018-10-29 14:29:59 +00:00
|
|
|
|
|
|
|
|
spa_list_for_each(mix, &p->mix, port_link) {
|
2019-09-26 11:56:25 +02:00
|
|
|
struct spa_data *d;
|
2021-10-26 12:28:31 +02:00
|
|
|
struct buffer *b;
|
2019-09-26 11:56:25 +02:00
|
|
|
void *pod;
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_trace_fp("%p: port %p mix %d.%d get buffer %d",
|
2022-01-22 23:29:45 +01:00
|
|
|
p->client, p, p->port_id, mix->id, frames);
|
2019-09-16 21:08:35 +02:00
|
|
|
|
2021-12-07 15:27:18 +01:00
|
|
|
if ((b = get_mix_buffer(mix, frames)) == NULL)
|
2018-10-29 14:29:59 +00:00
|
|
|
continue;
|
|
|
|
|
|
2021-10-26 12:28:31 +02:00
|
|
|
d = &b->datas[0];
|
2019-09-26 11:56:25 +02:00
|
|
|
|
|
|
|
|
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;
|
2022-07-10 21:59:05 +02:00
|
|
|
if (n_seq == MAX_MIX)
|
2022-02-09 15:40:45 +01:00
|
|
|
break;
|
2018-10-29 14:29:59 +00:00
|
|
|
}
|
2022-06-20 15:51:33 +02:00
|
|
|
convert_to_midi(seq, n_seq, ptr, p->client->fix_midi_events);
|
2019-09-26 11:56:25 +02:00
|
|
|
|
2018-10-29 14:29:59 +00:00
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-30 11:59:41 +02:00
|
|
|
static void *get_buffer_output_float(struct port *p, jack_nframes_t frames)
|
2019-09-16 21:08:35 +02:00
|
|
|
{
|
|
|
|
|
void *ptr;
|
|
|
|
|
|
2021-09-20 12:22:58 +02:00
|
|
|
ptr = get_buffer_output(p, frames, sizeof(float), NULL);
|
2020-09-30 11:59:41 +02:00
|
|
|
if (SPA_UNLIKELY(p->empty_out = (ptr == NULL)))
|
2019-09-16 21:08:35 +02:00
|
|
|
ptr = p->emptyptr;
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-30 11:59:41 +02:00
|
|
|
static void *get_buffer_output_midi(struct port *p, jack_nframes_t frames)
|
2019-09-16 21:08:35 +02:00
|
|
|
{
|
2020-01-29 10:29:26 +01:00
|
|
|
p->empty_out = true;
|
2019-09-16 21:08:35 +02:00
|
|
|
return p->emptyptr;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-30 11:59:41 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o = (struct object *) port;
|
|
|
|
|
struct port *p;
|
2020-09-30 11:59:41 +02:00
|
|
|
void *ptr;
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, NULL);
|
2019-09-10 18:49:19 +02:00
|
|
|
|
2021-10-09 12:49:21 +02:00
|
|
|
if (o->type != INTERFACE_Port || o->client == NULL)
|
2021-08-25 11:54:41 +02:00
|
|
|
return NULL;
|
|
|
|
|
|
2021-08-03 17:39:14 +02:00
|
|
|
if ((p = o->port.port) == NULL) {
|
|
|
|
|
struct mix *mix;
|
|
|
|
|
struct buffer *b;
|
2021-11-25 15:17:42 +01:00
|
|
|
struct spa_data *d;
|
|
|
|
|
uint32_t offset, size;
|
2021-08-03 17:39:14 +02:00
|
|
|
|
|
|
|
|
if ((mix = find_mix_peer(o->client, o->id)) == NULL)
|
|
|
|
|
return NULL;
|
2021-11-25 15:17:42 +01:00
|
|
|
|
|
|
|
|
pw_log_trace("peer mix: %p %d", mix, mix->peer_id);
|
|
|
|
|
|
2021-12-07 15:27:18 +01:00
|
|
|
if ((b = get_mix_buffer(mix, frames)) == NULL)
|
2021-08-03 17:39:14 +02:00
|
|
|
return NULL;
|
2021-11-25 15:17:42 +01:00
|
|
|
|
|
|
|
|
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);
|
2021-08-03 17:39:14 +02:00
|
|
|
}
|
2022-10-20 21:34:01 +02:00
|
|
|
if (!p->valid)
|
|
|
|
|
return NULL;
|
2021-03-29 11:14:37 +02:00
|
|
|
|
2020-09-30 11:59:41 +02:00
|
|
|
ptr = p->get_buffer(p, frames);
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_trace_fp("%p: port %p buffer %p empty:%u", p->client, p, ptr, p->empty_out);
|
2018-05-21 13:12:26 +02:00
|
|
|
return ptr;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_uuid_t jack_port_uuid (const jack_port_t *port)
|
|
|
|
|
{
|
2019-08-21 18:37:02 +02:00
|
|
|
struct object *o = (struct object *) port;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, 0);
|
2022-01-26 17:44:49 +01:00
|
|
|
return jack_port_uuid_generate(o->serial);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-04 17:06:54 +01:00
|
|
|
static const char *port_name(struct object *o)
|
|
|
|
|
{
|
|
|
|
|
const char *name;
|
|
|
|
|
struct client *c = o->client;
|
2023-04-21 11:40:43 +02:00
|
|
|
if (c == NULL)
|
|
|
|
|
return NULL;
|
2022-02-04 17:06:54 +01:00
|
|
|
if (c->default_as_system && is_port_default(c, o))
|
|
|
|
|
name = o->port.system;
|
|
|
|
|
else
|
|
|
|
|
name = o->port.name;
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
const char * jack_port_name (const jack_port_t *port)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o = (struct object *) port;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, NULL);
|
2022-02-04 17:06:54 +01:00
|
|
|
return port_name(o);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
const char * jack_port_short_name (const jack_port_t *port)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o = (struct object *) port;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, NULL);
|
2022-02-04 17:06:54 +01:00
|
|
|
return strchr(port_name(o), ':') + 1;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_flags (const jack_port_t *port)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o = (struct object *) port;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, 0);
|
2018-02-15 17:57:43 +01:00
|
|
|
return o->port.flags;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
const char * jack_port_type (const jack_port_t *port)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o = (struct object *) port;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, NULL);
|
2018-05-21 13:12:26 +02:00
|
|
|
return type_to_string(o->port.type_id);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_port_type_id_t jack_port_type_id (const jack_port_t *port)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o = (struct object *) port;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, 0);
|
2018-02-15 17:57:43 +01:00
|
|
|
return o->port.type_id;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_is_mine (const jack_client_t *client, const jack_port_t *port)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o = (struct object *) port;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, 0);
|
2021-10-01 09:21:27 +02:00
|
|
|
return o->type == INTERFACE_Port &&
|
|
|
|
|
o->port.port != NULL &&
|
|
|
|
|
o->port.port->client == (struct client*)client;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_connected (const jack_port_t *port)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o = (struct object *) port;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct client *c;
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *l;
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, 0);
|
2021-10-09 12:49:21 +02:00
|
|
|
if (o->type != INTERFACE_Port || o->client == NULL)
|
|
|
|
|
return 0;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
|
|
|
|
c = o->client;
|
|
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_for_each(l, &c->context.objects, link) {
|
|
|
|
|
if (l->type != INTERFACE_Link || l->removed)
|
|
|
|
|
continue;
|
2022-01-14 21:04:08 +01:00
|
|
|
if (l->port_link.src_serial == o->serial ||
|
|
|
|
|
l->port_link.dst_serial == o->serial)
|
2018-02-15 17:57:43 +01:00
|
|
|
res++;
|
|
|
|
|
}
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2022-01-14 21:04:08 +01:00
|
|
|
pw_log_debug("%p: id:%u/%u res:%d", port, o->id, o->serial, res);
|
2021-01-03 21:25:55 +01:00
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_connected_to (const jack_port_t *port,
|
|
|
|
|
const char *port_name)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o = (struct object *) port;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct client *c;
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *p, *l;
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, 0);
|
|
|
|
|
return_val_if_fail(port_name != NULL, 0);
|
2021-10-09 12:49:21 +02:00
|
|
|
if (o->type != INTERFACE_Port || o->client == NULL)
|
|
|
|
|
return 0;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
|
|
|
|
c = o->client;
|
|
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2022-01-14 21:04:08 +01:00
|
|
|
p = find_port_by_name(c, port_name);
|
2018-02-15 17:57:43 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2023-04-24 12:57:36 +02:00
|
|
|
if ((l = find_link(c, o->id, p->id)) != NULL)
|
2018-02-16 12:08:16 +01:00
|
|
|
res = 1;
|
2018-02-15 17:57:43 +01:00
|
|
|
|
|
|
|
|
exit:
|
2020-04-30 16:18:40 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
2022-01-14 21:04:08 +01:00
|
|
|
pw_log_debug("%p: id:%u/%u name:%s res:%d", port, o->id,
|
|
|
|
|
o->serial, port_name, res);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
|
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
const char ** jack_port_get_connections (const jack_port_t *port)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o = (struct object *) port;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, NULL);
|
2021-10-09 12:49:21 +02:00
|
|
|
if (o->type != INTERFACE_Port || o->client == NULL)
|
|
|
|
|
return NULL;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
|
|
|
|
return jack_port_get_all_connections((jack_client_t *)o->client, port);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
const char ** jack_port_get_all_connections (const jack_client_t *client,
|
|
|
|
|
const jack_port_t *port)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
struct object *o = (struct object *) port;
|
|
|
|
|
struct object *p, *l;
|
2020-02-24 11:25:31 +01:00
|
|
|
const char **res;
|
2018-02-15 17:57:43 +01:00
|
|
|
int count = 0;
|
2022-02-09 15:40:45 +01:00
|
|
|
struct pw_array tmp;
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, NULL);
|
|
|
|
|
return_val_if_fail(o != NULL, NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2022-02-09 15:40:45 +01:00
|
|
|
pw_array_init(&tmp, sizeof(void*) * 32);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_for_each(l, &c->context.objects, link) {
|
|
|
|
|
if (l->type != INTERFACE_Link || l->removed)
|
|
|
|
|
continue;
|
2022-01-14 21:04:08 +01:00
|
|
|
if (l->port_link.src_serial == o->serial)
|
2021-09-03 15:46:21 +02:00
|
|
|
p = find_type(c, l->port_link.dst, INTERFACE_Port, true);
|
2022-01-14 21:04:08 +01:00
|
|
|
else if (l->port_link.dst_serial == o->serial)
|
2021-09-03 15:46:21 +02:00
|
|
|
p = find_type(c, l->port_link.src, INTERFACE_Port, true);
|
2018-02-15 17:57:43 +01:00
|
|
|
else
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (p == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
2022-02-09 15:40:45 +01:00
|
|
|
pw_array_add_ptr(&tmp, (void*)port_name(p));
|
|
|
|
|
count++;
|
2018-02-15 17:57:43 +01:00
|
|
|
}
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2018-02-16 12:08:16 +01:00
|
|
|
if (count == 0) {
|
2022-02-09 15:40:45 +01:00
|
|
|
pw_array_clear(&tmp);
|
2018-02-16 12:08:16 +01:00
|
|
|
res = NULL;
|
2022-02-09 15:40:45 +01:00
|
|
|
} else {
|
|
|
|
|
pw_array_add_ptr(&tmp, NULL);
|
|
|
|
|
res = tmp.data;
|
|
|
|
|
}
|
2018-02-15 17:57:43 +01:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_tie (jack_port_t *src, jack_port_t *dst)
|
|
|
|
|
{
|
2023-08-25 14:28:38 +02:00
|
|
|
struct object *s = (struct object *) src;
|
|
|
|
|
struct object *d = (struct object *) dst;
|
|
|
|
|
struct port *sp, *dp;
|
|
|
|
|
|
|
|
|
|
sp = s->port.port;
|
|
|
|
|
dp = d->port.port;
|
|
|
|
|
if (sp == NULL || !sp->valid ||
|
|
|
|
|
dp == NULL || !dp->valid ||
|
|
|
|
|
sp->client != dp->client)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
dp->tied = sp;
|
|
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_untie (jack_port_t *port)
|
|
|
|
|
{
|
2023-08-25 14:28:38 +02:00
|
|
|
struct object *o = (struct object *) port;
|
|
|
|
|
struct port *p;
|
|
|
|
|
|
|
|
|
|
p = o->port.port;
|
|
|
|
|
if (p == NULL || !p->valid)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
p->tied = NULL;
|
|
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_set_name (jack_port_t *port, const char *port_name)
|
|
|
|
|
{
|
2019-09-09 17:15:52 +02:00
|
|
|
pw_log_warn("deprecated");
|
|
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-10-23 16:50:59 +02:00
|
|
|
int jack_port_rename (jack_client_t* client, jack_port_t *port, const char *port_name)
|
|
|
|
|
{
|
2019-09-09 17:15:52 +02:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
struct object *o = (struct object *) port;
|
|
|
|
|
struct port *p;
|
2021-06-17 09:18:29 +02:00
|
|
|
int res = 0;
|
2019-09-09 17:15:52 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(o != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(port_name != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-09-09 17:15:52 +02:00
|
|
|
pw_thread_loop_lock(c->context.loop);
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: port rename %p %s -> %s:%s",
|
2021-06-08 09:38:07 +02:00
|
|
|
client, port, o->port.name, c->name, port_name);
|
|
|
|
|
|
2022-01-22 20:55:40 +01:00
|
|
|
p = o->port.port;
|
2021-06-17 09:18:29 +02:00
|
|
|
if (p == NULL || !p->valid) {
|
|
|
|
|
res = -EINVAL;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-25 17:55:13 +02:00
|
|
|
pw_properties_set(p->props, PW_KEY_PORT_NAME, port_name);
|
2021-06-08 09:38:07 +02:00
|
|
|
snprintf(o->port.name, sizeof(o->port.name), "%s:%s", c->name, port_name);
|
2021-05-25 17:55:13 +02:00
|
|
|
|
|
|
|
|
p->info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS;
|
|
|
|
|
p->info.props = &p->props->dict;
|
2019-09-09 17:15:52 +02:00
|
|
|
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_client_node_port_update(c->node,
|
2019-09-09 17:15:52 +02:00
|
|
|
p->direction,
|
2022-01-22 20:55:40 +01:00
|
|
|
p->port_id,
|
2019-09-09 17:15:52 +02:00
|
|
|
PW_CLIENT_NODE_PORT_UPDATE_INFO,
|
|
|
|
|
0, NULL,
|
2021-05-25 17:55:13 +02:00
|
|
|
&p->info);
|
|
|
|
|
p->info.change_mask = 0;
|
|
|
|
|
|
2021-06-17 09:18:29 +02:00
|
|
|
done:
|
2019-09-09 17:15:52 +02:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
|
|
|
|
|
2021-06-17 09:18:29 +02:00
|
|
|
return res;
|
2018-10-23 16:50:59 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_set_alias (jack_port_t *port, const char *alias)
|
|
|
|
|
{
|
2019-09-09 17:15:52 +02:00
|
|
|
struct object *o = (struct object *) port;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct client *c;
|
2019-09-09 17:15:52 +02:00
|
|
|
struct port *p;
|
|
|
|
|
const char *key;
|
2021-06-17 09:18:29 +02:00
|
|
|
int res = 0;
|
2019-09-09 17:15:52 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(alias != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
|
|
|
|
c = o->client;
|
2022-01-22 20:55:40 +01:00
|
|
|
if (o->type != INTERFACE_Port || c == NULL)
|
|
|
|
|
return -EINVAL;
|
2019-09-09 17:15:52 +02:00
|
|
|
|
|
|
|
|
pw_thread_loop_lock(c->context.loop);
|
|
|
|
|
|
2022-01-22 20:55:40 +01:00
|
|
|
p = o->port.port;
|
2021-06-17 10:46:04 +02:00
|
|
|
if (p == NULL || !p->valid) {
|
|
|
|
|
res = -EINVAL;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-09 17:15:52 +02:00
|
|
|
if (o->port.alias1[0] == '\0') {
|
2019-09-23 20:50:12 +02:00
|
|
|
key = PW_KEY_OBJECT_PATH;
|
2019-09-09 17:15:52 +02:00
|
|
|
snprintf(o->port.alias1, sizeof(o->port.alias1), "%s", alias);
|
|
|
|
|
}
|
|
|
|
|
else if (o->port.alias2[0] == '\0') {
|
2019-09-23 20:50:12 +02:00
|
|
|
key = PW_KEY_PORT_ALIAS;
|
2019-09-09 17:15:52 +02:00
|
|
|
snprintf(o->port.alias2, sizeof(o->port.alias2), "%s", alias);
|
|
|
|
|
}
|
2021-06-17 09:18:29 +02:00
|
|
|
else {
|
|
|
|
|
res = -1;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
2019-09-09 17:15:52 +02:00
|
|
|
|
2021-05-25 17:55:13 +02:00
|
|
|
pw_properties_set(p->props, key, alias);
|
|
|
|
|
|
|
|
|
|
p->info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS;
|
|
|
|
|
p->info.props = &p->props->dict;
|
2019-09-09 17:15:52 +02:00
|
|
|
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_client_node_port_update(c->node,
|
2019-09-09 17:15:52 +02:00
|
|
|
p->direction,
|
2022-01-22 20:55:40 +01:00
|
|
|
p->port_id,
|
2019-09-09 17:15:52 +02:00
|
|
|
PW_CLIENT_NODE_PORT_UPDATE_INFO,
|
|
|
|
|
0, NULL,
|
2021-05-25 17:55:13 +02:00
|
|
|
&p->info);
|
|
|
|
|
p->info.change_mask = 0;
|
|
|
|
|
|
2021-06-17 09:18:29 +02:00
|
|
|
done:
|
2019-09-09 17:15:52 +02:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
|
|
|
|
|
2021-06-17 09:18:29 +02:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_unset_alias (jack_port_t *port, const char *alias)
|
|
|
|
|
{
|
2019-09-09 17:15:52 +02:00
|
|
|
struct object *o = (struct object *) port;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct client *c;
|
2019-09-09 17:15:52 +02:00
|
|
|
struct port *p;
|
|
|
|
|
const char *key;
|
2021-06-17 09:18:29 +02:00
|
|
|
int res = 0;
|
2019-09-09 17:15:52 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(alias != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
|
|
|
|
c = o->client;
|
2022-01-22 20:55:40 +01:00
|
|
|
if (o->type != INTERFACE_Port || c == NULL)
|
|
|
|
|
return -EINVAL;
|
2019-09-09 17:15:52 +02:00
|
|
|
|
|
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2022-01-22 20:55:40 +01:00
|
|
|
p = o->port.port;
|
2021-06-17 10:46:04 +02:00
|
|
|
if (p == NULL || !p->valid) {
|
|
|
|
|
res = -EINVAL;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-18 11:36:13 +10:00
|
|
|
if (spa_streq(o->port.alias1, alias))
|
2019-09-23 20:50:12 +02:00
|
|
|
key = PW_KEY_OBJECT_PATH;
|
2021-05-18 11:36:13 +10:00
|
|
|
else if (spa_streq(o->port.alias2, alias))
|
2019-09-23 20:50:12 +02:00
|
|
|
key = PW_KEY_PORT_ALIAS;
|
2021-06-17 09:18:29 +02:00
|
|
|
else {
|
|
|
|
|
res = -1;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
2019-09-09 17:15:52 +02:00
|
|
|
|
2021-05-25 17:55:13 +02:00
|
|
|
pw_properties_set(p->props, key, NULL);
|
|
|
|
|
|
|
|
|
|
p->info.change_mask |= SPA_PORT_CHANGE_MASK_PROPS;
|
|
|
|
|
p->info.props = &p->props->dict;
|
2019-09-09 17:15:52 +02:00
|
|
|
|
2019-12-11 16:13:46 +01:00
|
|
|
pw_client_node_port_update(c->node,
|
2019-09-09 17:15:52 +02:00
|
|
|
p->direction,
|
2022-01-22 20:55:40 +01:00
|
|
|
p->port_id,
|
2019-09-09 17:15:52 +02:00
|
|
|
PW_CLIENT_NODE_PORT_UPDATE_INFO,
|
|
|
|
|
0, NULL,
|
2021-05-25 17:55:13 +02:00
|
|
|
&p->info);
|
|
|
|
|
p->info.change_mask = 0;
|
|
|
|
|
|
2021-06-17 09:18:29 +02:00
|
|
|
done:
|
2019-09-09 17:15:52 +02:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
|
|
|
|
|
2021-06-17 09:18:29 +02:00
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_get_aliases (const jack_port_t *port, char* const aliases[2])
|
|
|
|
|
{
|
2018-02-16 12:08:16 +01:00
|
|
|
struct object *o = (struct object *) port;
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(aliases != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(aliases[0] != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(aliases[1] != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2018-02-16 12:08:16 +01:00
|
|
|
if (o->port.alias1[0] != '\0') {
|
2018-05-21 13:12:26 +02:00
|
|
|
snprintf(aliases[0], REAL_JACK_PORT_NAME_SIZE+1, "%s", o->port.alias1);
|
2018-02-16 12:08:16 +01:00
|
|
|
res++;
|
|
|
|
|
}
|
|
|
|
|
if (o->port.alias2[0] != '\0') {
|
2018-05-21 13:12:26 +02:00
|
|
|
snprintf(aliases[1], REAL_JACK_PORT_NAME_SIZE+1, "%s", o->port.alias2);
|
2018-02-16 12:08:16 +01:00
|
|
|
res++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_request_monitor (jack_port_t *port, int onoff)
|
|
|
|
|
{
|
2019-09-03 13:44:12 +02:00
|
|
|
struct object *o = (struct object *) port;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-09-03 13:44:12 +02:00
|
|
|
if (onoff)
|
|
|
|
|
o->port.monitor_requests++;
|
|
|
|
|
else if (o->port.monitor_requests > 0)
|
|
|
|
|
o->port.monitor_requests--;
|
|
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_request_monitor_by_name (jack_client_t *client,
|
|
|
|
|
const char *port_name, int onoff)
|
|
|
|
|
{
|
2019-09-03 13:44:12 +02:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
struct object *p;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(port_name != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2022-01-14 21:04:08 +01:00
|
|
|
p = find_port_by_name(c, port_name);
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
2019-09-03 13:44:12 +02:00
|
|
|
|
|
|
|
|
if (p == NULL) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: jack_port_request_monitor_by_name called"
|
2019-09-03 13:44:12 +02:00
|
|
|
" with an incorrect port %s", client, port_name);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return jack_port_request_monitor((jack_port_t*)p, onoff);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_ensure_monitor (jack_port_t *port, int onoff)
|
|
|
|
|
{
|
2019-09-03 13:44:12 +02:00
|
|
|
struct object *o = (struct object *) port;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-09-03 13:44:12 +02:00
|
|
|
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;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_monitoring_input (jack_port_t *port)
|
|
|
|
|
{
|
2019-09-03 13:44:12 +02:00
|
|
|
struct object *o = (struct object *) port;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, -EINVAL);
|
2019-09-03 13:44:12 +02:00
|
|
|
return o->port.monitor_requests > 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-04 21:05:45 +01:00
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
|
2021-03-23 12:24:38 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-11-04 21:05:45 +01:00
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_connect (jack_client_t *client,
|
|
|
|
|
const char *source_port,
|
|
|
|
|
const char *destination_port)
|
|
|
|
|
{
|
|
|
|
|
struct client *c = (struct client *) client;
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *src, *dst;
|
|
|
|
|
struct spa_dict props;
|
2020-06-17 13:17:28 +02:00
|
|
|
struct spa_dict_item items[6];
|
2020-06-04 12:59:55 +02:00
|
|
|
struct pw_proxy *proxy;
|
2020-11-04 21:05:45 +01:00
|
|
|
struct spa_hook listener;
|
2018-02-15 17:57:43 +01:00
|
|
|
char val[4][16];
|
2020-11-04 21:05:45 +01:00
|
|
|
int res, link_res = 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, EINVAL);
|
|
|
|
|
return_val_if_fail(source_port != NULL, EINVAL);
|
|
|
|
|
return_val_if_fail(destination_port != NULL, EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: connect %s %s", client, source_port, destination_port);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2023-05-10 12:10:03 +02:00
|
|
|
freeze_callbacks(c);
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2022-01-14 21:04:08 +01:00
|
|
|
src = find_port_by_name(c, source_port);
|
|
|
|
|
dst = find_port_by_name(c, destination_port);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
if (src == NULL || dst == NULL ||
|
|
|
|
|
!(src->port.flags & JackPortIsOutput) ||
|
2019-09-19 13:16:42 +02:00
|
|
|
!(dst->port.flags & JackPortIsInput) ||
|
|
|
|
|
src->port.type_id != dst->port.type_id) {
|
2018-02-16 12:08:16 +01:00
|
|
|
res = -EINVAL;
|
2018-02-13 18:05:55 +01:00
|
|
|
goto exit;
|
|
|
|
|
}
|
2021-03-23 12:24:38 +01:00
|
|
|
if ((res = check_connect(c, src, dst)) != 1)
|
|
|
|
|
goto exit;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2019-08-21 18:37:02 +02:00
|
|
|
snprintf(val[0], sizeof(val[0]), "%d", src->port.node_id);
|
2018-02-15 17:57:43 +01:00
|
|
|
snprintf(val[1], sizeof(val[1]), "%d", src->id);
|
2019-08-21 18:37:02 +02:00
|
|
|
snprintf(val[2], sizeof(val[2]), "%d", dst->port.node_id);
|
2018-02-15 17:57:43 +01:00
|
|
|
snprintf(val[3], sizeof(val[3]), "%d", dst->id);
|
2018-02-13 18:05:55 +01:00
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
props = SPA_DICT_INIT(items, 0);
|
2019-05-24 15:47:28 +02:00
|
|
|
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]);
|
2020-03-03 18:17:46 +01:00
|
|
|
items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_OBJECT_LINGER, "true");
|
2023-03-08 16:51:40 +01:00
|
|
|
if (c->passive_links)
|
2020-06-17 13:17:28 +02:00
|
|
|
items[props.n_items++] = SPA_DICT_ITEM_INIT(PW_KEY_LINK_PASSIVE, "true");
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2020-06-04 12:59:55 +02:00
|
|
|
proxy = pw_core_create_object(c->core,
|
2018-02-13 18:05:55 +01:00
|
|
|
"link-factory",
|
2018-08-29 09:54:21 +02:00
|
|
|
PW_TYPE_INTERFACE_Link,
|
2019-12-11 15:11:56 +01:00
|
|
|
PW_VERSION_LINK,
|
2018-02-15 17:57:43 +01:00
|
|
|
&props,
|
2018-02-13 18:05:55 +01:00
|
|
|
0);
|
2020-11-04 21:05:45 +01:00
|
|
|
if (proxy == NULL) {
|
|
|
|
|
res = -errno;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spa_zero(listener);
|
|
|
|
|
pw_proxy_add_listener(proxy, &listener, &link_proxy_events, &link_res);
|
|
|
|
|
|
2018-02-13 18:05:55 +01:00
|
|
|
res = do_sync(c);
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2020-11-04 21:05:45 +01:00
|
|
|
spa_hook_remove(&listener);
|
|
|
|
|
|
|
|
|
|
if (link_res < 0)
|
|
|
|
|
res = link_res;
|
|
|
|
|
|
2020-06-04 12:59:55 +02:00
|
|
|
pw_proxy_destroy(proxy);
|
|
|
|
|
|
2023-04-21 15:04:45 +02:00
|
|
|
exit:
|
2023-05-10 16:49:41 +02:00
|
|
|
pw_log_debug("%p: connect %s %s done %d", client, source_port, destination_port, res);
|
2023-05-10 12:10:03 +02:00
|
|
|
thaw_callbacks(c);
|
2018-02-13 18:05:55 +01:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
|
|
|
|
|
2020-11-04 21:05:45 +01:00
|
|
|
return -res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_disconnect (jack_client_t *client,
|
|
|
|
|
const char *source_port,
|
|
|
|
|
const char *destination_port)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2018-02-16 12:08:16 +01:00
|
|
|
struct object *src, *dst, *l;
|
2018-02-15 17:57:43 +01:00
|
|
|
int res;
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(source_port != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(destination_port != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: disconnect %s %s", client, source_port, destination_port);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
|
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2023-05-10 12:10:03 +02:00
|
|
|
freeze_callbacks(c);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2022-01-14 21:04:08 +01:00
|
|
|
src = find_port_by_name(c, source_port);
|
|
|
|
|
dst = find_port_by_name(c, destination_port);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: %d %d", client, src->id, dst->id);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
|
|
|
|
if (src == NULL || dst == NULL ||
|
|
|
|
|
!(src->port.flags & JackPortIsOutput) ||
|
|
|
|
|
!(dst->port.flags & JackPortIsInput)) {
|
2018-02-16 12:08:16 +01:00
|
|
|
res = -EINVAL;
|
2018-02-15 17:57:43 +01:00
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-23 12:24:38 +01:00
|
|
|
if ((res = check_connect(c, src, dst)) != 1)
|
|
|
|
|
goto exit;
|
|
|
|
|
|
2018-02-16 12:08:16 +01:00
|
|
|
if ((l = find_link(c, src->id, dst->id)) == NULL) {
|
2018-02-15 17:57:43 +01:00
|
|
|
res = -ENOENT;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-11 09:44:48 +01:00
|
|
|
pw_registry_destroy(c->registry, l->id);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
|
|
|
|
res = do_sync(c);
|
|
|
|
|
|
|
|
|
|
exit:
|
2023-05-10 12:10:03 +02:00
|
|
|
thaw_callbacks(c);
|
2018-02-15 17:57:43 +01:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
|
|
|
|
|
2020-11-04 21:05:45 +01:00
|
|
|
return -res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_disconnect (jack_client_t *client, jack_port_t *port)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
struct object *o = (struct object *) port;
|
|
|
|
|
struct object *l;
|
|
|
|
|
int res;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(o != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: disconnect %p", client, port);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
|
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2023-05-10 12:10:03 +02:00
|
|
|
freeze_callbacks(c);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_for_each(l, &c->context.objects, link) {
|
|
|
|
|
if (l->type != INTERFACE_Link || l->removed)
|
|
|
|
|
continue;
|
2022-01-14 21:04:08 +01:00
|
|
|
if (l->port_link.src_serial == o->serial ||
|
|
|
|
|
l->port_link.dst_serial == o->serial) {
|
2019-12-11 09:44:48 +01:00
|
|
|
pw_registry_destroy(c->registry, l->id);
|
2018-02-15 17:57:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
res = do_sync(c);
|
|
|
|
|
|
2023-05-10 12:10:03 +02:00
|
|
|
thaw_callbacks(c);
|
2018-02-15 17:57:43 +01:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
|
|
|
|
|
2020-11-04 21:05:45 +01:00
|
|
|
return -res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_name_size(void)
|
|
|
|
|
{
|
2018-05-21 13:12:26 +02:00
|
|
|
return REAL_JACK_PORT_NAME_SIZE+1;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_port_type_size(void)
|
|
|
|
|
{
|
2018-05-21 13:12:26 +02:00
|
|
|
return JACK_PORT_TYPE_SIZE+1;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
size_t jack_port_type_get_buffer_size (jack_client_t *client, const char *port_type)
|
|
|
|
|
{
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(client != NULL, 0);
|
|
|
|
|
return_val_if_fail(port_type != NULL, 0);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-05-18 11:40:50 +10:00
|
|
|
if (spa_streq(JACK_DEFAULT_AUDIO_TYPE, port_type))
|
2019-09-02 17:18:19 +02:00
|
|
|
return jack_get_buffer_size(client) * sizeof(float);
|
2021-05-18 11:40:50 +10:00
|
|
|
else if (spa_streq(JACK_DEFAULT_MIDI_TYPE, port_type))
|
2019-10-24 10:23:01 +02:00
|
|
|
return MAX_BUFFER_FRAMES * sizeof(float);
|
2021-05-18 11:40:50 +10:00
|
|
|
else if (spa_streq(JACK_DEFAULT_VIDEO_TYPE, port_type))
|
2019-10-28 15:03:31 +01:00
|
|
|
return 320 * 240 * 4 * sizeof(float);
|
2018-05-21 13:12:26 +02:00
|
|
|
else
|
|
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void jack_port_set_latency (jack_port_t *port, jack_nframes_t frames)
|
|
|
|
|
{
|
2019-09-02 17:16:57 +02:00
|
|
|
struct object *o = (struct object *) port;
|
2021-12-07 12:09:59 +01:00
|
|
|
struct client *c;
|
2021-03-27 18:33:22 +01:00
|
|
|
jack_latency_range_t range = { frames, frames };
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_if_fail(o != NULL);
|
2021-12-07 12:09:59 +01:00
|
|
|
c = o->client;
|
|
|
|
|
|
|
|
|
|
pw_log_debug("%p: %s set latency %d", c, o->port.name, frames);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-09-02 17:16:57 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range)
|
|
|
|
|
{
|
2019-09-02 17:16:57 +02:00
|
|
|
struct object *o = (struct object *) port;
|
2020-09-10 09:15:47 +02:00
|
|
|
struct client *c;
|
2021-05-24 18:01:19 +02:00
|
|
|
jack_nframes_t nframes, rate;
|
2021-05-21 10:02:34 +02:00
|
|
|
int direction;
|
2021-05-24 18:01:19 +02:00
|
|
|
struct spa_latency_info *info;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_if_fail(o != NULL);
|
2021-10-09 12:49:21 +02:00
|
|
|
if (o->type != INTERFACE_Port || o->client == NULL)
|
|
|
|
|
return;
|
2020-09-10 09:15:47 +02:00
|
|
|
c = o->client;
|
|
|
|
|
|
2021-05-21 10:02:34 +02:00
|
|
|
if (mode == JackCaptureLatency)
|
|
|
|
|
direction = SPA_DIRECTION_OUTPUT;
|
|
|
|
|
else
|
|
|
|
|
direction = SPA_DIRECTION_INPUT;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-05-21 10:02:34 +02:00
|
|
|
nframes = jack_get_buffer_size((jack_client_t*)c);
|
2021-05-24 18:01:19 +02:00
|
|
|
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;
|
2021-05-25 17:55:13 +02:00
|
|
|
|
2021-12-07 12:09:59 +01:00
|
|
|
pw_log_debug("%p: %s get %d latency range %d %d", c, o->port.name,
|
|
|
|
|
mode, range->min, range->max);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2021-06-07 11:16:24 +02:00
|
|
|
static int
|
2023-05-11 18:21:57 +02:00
|
|
|
do_port_check_latency(struct spa_loop *loop,
|
2021-06-07 11:16:24 +02:00
|
|
|
bool async, uint32_t seq, const void *data, size_t size, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
struct port *p = user_data;
|
2023-05-11 18:21:57 +02:00
|
|
|
const struct spa_latency_info *latency = data;
|
|
|
|
|
port_check_latency(p, latency);
|
2021-06-07 11:16:24 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void jack_port_set_latency_range (jack_port_t *port, jack_latency_callback_mode_t mode, jack_latency_range_t *range)
|
|
|
|
|
{
|
2019-09-02 17:16:57 +02:00
|
|
|
struct object *o = (struct object *) port;
|
2021-05-27 15:13:48 +02:00
|
|
|
struct client *c;
|
2021-05-25 17:55:13 +02:00
|
|
|
enum spa_direction direction;
|
2023-05-11 18:21:57 +02:00
|
|
|
struct spa_latency_info latency;
|
2021-12-03 17:59:56 +01:00
|
|
|
jack_nframes_t nframes;
|
2021-05-25 17:55:13 +02:00
|
|
|
struct port *p;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_if_fail(o != NULL);
|
2021-10-09 12:49:21 +02:00
|
|
|
if (o->type != INTERFACE_Port || o->client == NULL)
|
|
|
|
|
return;
|
2021-05-27 15:13:48 +02:00
|
|
|
c = o->client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-05-25 17:55:13 +02:00
|
|
|
if (mode == JackCaptureLatency)
|
|
|
|
|
direction = SPA_DIRECTION_OUTPUT;
|
|
|
|
|
else
|
|
|
|
|
direction = SPA_DIRECTION_INPUT;
|
|
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: %s set %d latency range %d %d", c, o->port.name, mode, range->min, range->max);
|
2021-05-27 15:13:48 +02:00
|
|
|
|
2021-12-03 17:59:56 +01:00
|
|
|
latency = SPA_LATENCY_INFO(direction);
|
2021-05-27 15:13:48 +02:00
|
|
|
|
|
|
|
|
nframes = jack_get_buffer_size((jack_client_t*)c);
|
2023-03-09 17:14:05 +01:00
|
|
|
if (nframes == 0)
|
2023-03-09 17:27:57 +01:00
|
|
|
nframes = 1;
|
2021-05-27 15:13:48 +02:00
|
|
|
|
2021-11-22 12:12:29 +01:00
|
|
|
latency.min_rate = range->min;
|
2021-12-03 17:59:56 +01:00
|
|
|
if (latency.min_rate >= nframes) {
|
|
|
|
|
latency.min_quantum = latency.min_rate / nframes;
|
|
|
|
|
latency.min_rate %= nframes;
|
|
|
|
|
}
|
2021-11-22 12:12:29 +01:00
|
|
|
|
|
|
|
|
latency.max_rate = range->max;
|
2021-12-03 17:59:56 +01:00
|
|
|
if (latency.max_rate >= nframes) {
|
|
|
|
|
latency.max_quantum = latency.max_rate / nframes;
|
|
|
|
|
latency.max_rate %= nframes;
|
|
|
|
|
}
|
2021-05-27 15:13:48 +02:00
|
|
|
|
2021-05-25 17:55:13 +02:00
|
|
|
if ((p = o->port.port) == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2023-05-11 18:21:57 +02:00
|
|
|
pw_loop_invoke(c->context.l, do_port_check_latency, 0,
|
|
|
|
|
&latency, sizeof(latency), false, p);
|
2021-07-05 16:06:34 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_recompute_total_latencies (jack_client_t *client)
|
|
|
|
|
{
|
2021-07-05 16:06:34 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2023-08-23 17:28:24 +02:00
|
|
|
return queue_notify(c, NOTIFY_TYPE_TOTAL_LATENCY, NULL, 0, NULL);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-21 10:02:34 +02:00
|
|
|
static jack_nframes_t port_get_latency (jack_port_t *port)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2019-09-02 17:16:57 +02:00
|
|
|
struct object *o = (struct object *) port;
|
2021-03-27 18:33:22 +01:00
|
|
|
jack_latency_range_t range = { 0, 0 };
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(o != NULL, 0);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-09-02 17:16:57 +02:00
|
|
|
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;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-21 10:02:34 +02:00
|
|
|
SPA_EXPORT
|
|
|
|
|
jack_nframes_t jack_port_get_latency (jack_port_t *port)
|
|
|
|
|
{
|
|
|
|
|
return port_get_latency(port);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_nframes_t jack_port_get_total_latency (jack_client_t *client,
|
|
|
|
|
jack_port_t *port)
|
|
|
|
|
{
|
2021-05-21 10:02:34 +02:00
|
|
|
return port_get_latency(port);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
int jack_recompute_total_latency (jack_client_t *client, jack_port_t* port)
|
|
|
|
|
{
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_warn("%p: not implemented %p", client, port);
|
2019-09-02 17:16:57 +02:00
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2020-08-02 12:50:56 +02:00
|
|
|
static int port_compare_func(const void *v1, const void *v2)
|
2019-10-22 12:21:37 +02:00
|
|
|
{
|
|
|
|
|
const struct object *const*o1 = v1, *const*o2 = v2;
|
2020-08-02 12:50:56 +02:00
|
|
|
struct client *c = (*o1)->client;
|
2019-11-15 13:31:22 +01:00
|
|
|
int res;
|
2020-07-17 17:23:43 +02:00
|
|
|
bool is_cap1, is_cap2, is_def1 = false, is_def2 = false;
|
|
|
|
|
|
2021-01-05 15:38:13 +01:00
|
|
|
is_cap1 = ((*o1)->port.flags & JackPortIsOutput) == JackPortIsOutput &&
|
|
|
|
|
!(*o1)->port.is_monitor;
|
|
|
|
|
is_cap2 = ((*o2)->port.flags & JackPortIsOutput) == JackPortIsOutput &&
|
|
|
|
|
!(*o2)->port.is_monitor;
|
2020-08-18 17:18:37 +02:00
|
|
|
|
2020-07-23 11:59:13 +02:00
|
|
|
if (c->metadata) {
|
2021-03-05 17:37:12 +01:00
|
|
|
struct object *ot1, *ot2;
|
|
|
|
|
|
|
|
|
|
ot1 = (*o1)->port.node;
|
|
|
|
|
|
2020-07-23 11:59:13 +02:00
|
|
|
if (is_cap1)
|
2021-05-18 11:36:13 +10:00
|
|
|
is_def1 = ot1 != NULL && spa_streq(ot1->node.node_name,
|
|
|
|
|
c->metadata->default_audio_source);
|
2020-07-23 11:59:13 +02:00
|
|
|
else if (!is_cap1)
|
2021-05-18 11:36:13 +10:00
|
|
|
is_def1 = ot1 != NULL && spa_streq(ot1->node.node_name,
|
|
|
|
|
c->metadata->default_audio_sink);
|
2021-03-05 17:37:12 +01:00
|
|
|
ot2 = (*o2)->port.node;
|
2019-10-22 12:21:37 +02:00
|
|
|
|
2020-07-23 11:59:13 +02:00
|
|
|
if (is_cap2)
|
2021-05-18 11:36:13 +10:00
|
|
|
is_def2 = ot2 != NULL && spa_streq(ot2->node.node_name,
|
|
|
|
|
c->metadata->default_audio_source);
|
2020-07-23 11:59:13 +02:00
|
|
|
else if (!is_cap2)
|
2021-05-18 11:36:13 +10:00
|
|
|
is_def2 = ot2 != NULL && spa_streq(ot2->node.node_name,
|
|
|
|
|
c->metadata->default_audio_sink);
|
2020-07-23 11:59:13 +02:00
|
|
|
}
|
2019-10-22 12:21:37 +02:00
|
|
|
if ((*o1)->port.type_id != (*o2)->port.type_id)
|
2019-11-15 13:31:22 +01:00
|
|
|
res = (*o1)->port.type_id - (*o2)->port.type_id;
|
2020-07-17 17:23:43 +02:00
|
|
|
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;
|
2019-11-15 13:31:22 +01:00
|
|
|
else if ((*o1)->port.priority != (*o2)->port.priority)
|
|
|
|
|
res = (*o2)->port.priority - (*o1)->port.priority;
|
2022-04-02 09:42:22 +02:00
|
|
|
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;
|
2021-11-08 09:23:20 +01:00
|
|
|
if (res == 0)
|
|
|
|
|
res = (*o1)->port.system_id - (*o2)->port.system_id;
|
|
|
|
|
if (res == 0)
|
2022-01-14 21:04:08 +01:00
|
|
|
res = (*o1)->serial - (*o2)->serial;
|
2021-11-08 09:23:20 +01:00
|
|
|
}
|
|
|
|
|
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,
|
2019-11-15 13:31:22 +01:00
|
|
|
(*o1)->port.type_id, (*o2)->port.type_id,
|
2020-07-17 17:23:43 +02:00
|
|
|
is_def1, is_def2,
|
2019-11-15 13:31:22 +01:00
|
|
|
(*o1)->port.priority, (*o2)->port.priority,
|
2022-01-14 21:04:08 +01:00
|
|
|
(*o1)->serial, (*o2)->serial, res);
|
2019-11-15 13:31:22 +01:00
|
|
|
return res;
|
2019-10-22 12:21:37 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
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;
|
2019-10-22 12:21:37 +02:00
|
|
|
const char **res;
|
2018-02-15 17:57:43 +01:00
|
|
|
struct object *o;
|
2022-02-09 15:40:45 +01:00
|
|
|
struct pw_array tmp;
|
2018-05-21 13:12:26 +02:00
|
|
|
const char *str;
|
2023-01-10 17:11:43 +01:00
|
|
|
uint32_t i, count;
|
2021-11-29 12:46:50 +01:00
|
|
|
int r;
|
2018-05-24 16:11:54 +02:00
|
|
|
regex_t port_regex, type_regex;
|
2018-05-21 13:12:26 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-01-10 17:11:43 +01:00
|
|
|
str = getenv("PIPEWIRE_NODE");
|
2018-02-09 18:23:36 +01:00
|
|
|
|
2021-11-29 12:46:50 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-24 16:11:54 +02:00
|
|
|
|
2023-01-10 17:11:43 +01:00
|
|
|
pw_log_debug("%p: ports target:%s name:\"%s\" type:\"%s\" flags:%08lx", c, str,
|
2019-08-08 11:02:40 +02:00
|
|
|
port_name_pattern, type_name_pattern, flags);
|
2018-05-21 13:12:26 +02:00
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2022-02-09 15:40:45 +01:00
|
|
|
pw_array_init(&tmp, sizeof(void*) * 32);
|
2019-10-22 12:21:37 +02:00
|
|
|
count = 0;
|
2022-02-09 15:40:45 +01:00
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_for_each(o, &c->context.objects, link) {
|
2023-08-07 19:57:01 +02:00
|
|
|
if (o->type != INTERFACE_Port || o->removed || !o->visible)
|
2022-01-13 13:45:53 +01:00
|
|
|
continue;
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: check port type:%d flags:%08lx name:\"%s\"", c,
|
2019-10-22 12:21:37 +02:00
|
|
|
o->port.type_id, o->port.flags, o->port.name);
|
2020-03-02 16:50:37 +01:00
|
|
|
if (o->port.type_id > TYPE_ID_VIDEO)
|
2019-10-22 12:21:37 +02:00
|
|
|
continue;
|
|
|
|
|
if (!SPA_FLAG_IS_SET(o->port.flags, flags))
|
|
|
|
|
continue;
|
2023-01-10 17:11:43 +01:00
|
|
|
if (str != NULL && o->port.node != NULL) {
|
|
|
|
|
if (!spa_strstartswith(o->port.name, str) &&
|
|
|
|
|
o->port.node->serial != atoll(str))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2019-10-22 12:21:37 +02:00
|
|
|
|
|
|
|
|
if (port_name_pattern && port_name_pattern[0]) {
|
2021-03-07 09:44:40 +01:00
|
|
|
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)
|
2019-09-19 15:26:06 +02:00
|
|
|
continue;
|
2019-10-22 12:21:37 +02:00
|
|
|
}
|
|
|
|
|
if (type_name_pattern && type_name_pattern[0]) {
|
|
|
|
|
if (regexec(&type_regex, type_to_string(o->port.type_id),
|
|
|
|
|
0, NULL, 0) == REG_NOMATCH)
|
2018-05-24 16:11:54 +02:00
|
|
|
continue;
|
2019-09-19 15:26:06 +02:00
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: port \"%s\" prio:%d matches (%d)",
|
2019-10-22 12:21:37 +02:00
|
|
|
c, o->port.name, o->port.priority, count);
|
2022-02-09 15:40:45 +01:00
|
|
|
|
|
|
|
|
pw_array_add_ptr(&tmp, o);
|
|
|
|
|
count++;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
|
|
|
|
|
2019-10-22 12:21:37 +02:00
|
|
|
if (count > 0) {
|
2022-02-09 15:40:45 +01:00
|
|
|
qsort(tmp.data, count, sizeof(struct object *), port_compare_func);
|
|
|
|
|
pw_array_add_ptr(&tmp, NULL);
|
|
|
|
|
res = tmp.data;
|
2019-10-22 12:21:37 +02:00
|
|
|
for (i = 0; i < count; i++)
|
2022-02-09 15:40:45 +01:00
|
|
|
res[i] = port_name((struct object*)res[i]);
|
2019-10-22 12:21:37 +02:00
|
|
|
} else {
|
2022-02-09 15:40:45 +01:00
|
|
|
pw_array_clear(&tmp);
|
2019-10-22 12:21:37 +02:00
|
|
|
res = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-24 16:11:54 +02:00
|
|
|
if (port_name_pattern && port_name_pattern[0])
|
|
|
|
|
regfree(&port_regex);
|
|
|
|
|
if (type_name_pattern && type_name_pattern[0])
|
|
|
|
|
regfree(&type_regex);
|
|
|
|
|
|
2018-02-09 18:23:36 +01:00
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_port_t * jack_port_by_name (jack_client_t *client, const char *port_name)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
|
|
|
|
struct object *res;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2022-01-14 21:04:08 +01:00
|
|
|
res = find_port_by_name(c, port_name);
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2021-06-21 11:25:17 +02:00
|
|
|
if (res == NULL)
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: port \"%s\" not found", c, port_name);
|
2021-06-21 11:25:17 +02:00
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
return (jack_port_t *)res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_port_t * jack_port_by_id (jack_client_t *client,
|
|
|
|
|
jack_port_id_t port_id)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2021-06-22 11:16:45 +02:00
|
|
|
struct object *res = NULL;
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2022-01-13 13:45:53 +01:00
|
|
|
pthread_mutex_lock(&c->context.lock);
|
2022-01-26 17:44:49 +01:00
|
|
|
res = find_by_serial(c, port_id);
|
|
|
|
|
if (res && res->type != INTERFACE_Port)
|
|
|
|
|
res = NULL;
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: port %d -> %p", c, port_id, res);
|
2022-01-13 13:45:53 +01:00
|
|
|
pthread_mutex_unlock(&c->context.lock);
|
2018-02-15 17:57:43 +01:00
|
|
|
|
2021-06-21 11:25:17 +02:00
|
|
|
if (res == NULL)
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_info("%p: port %d not found", c, port_id);
|
2021-06-21 11:25:17 +02:00
|
|
|
|
2018-02-15 17:57:43 +01:00
|
|
|
return (jack_port_t *)res;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client)
|
|
|
|
|
{
|
2018-11-07 09:58:20 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct spa_io_position *pos;
|
2018-11-07 09:58:20 +01:00
|
|
|
uint64_t diff;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, 0);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2020-04-30 16:15:22 +02:00
|
|
|
if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
|
2019-08-27 14:41:15 +02:00
|
|
|
return 0;
|
|
|
|
|
|
2023-04-10 14:45:27 +02:00
|
|
|
diff = get_time_ns() - pos->clock.nsec;
|
2021-07-06 17:55:38 +02:00
|
|
|
return (jack_nframes_t) floor(((double)c->sample_rate * diff) / SPA_NSEC_PER_SEC);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_nframes_t jack_frame_time (const jack_client_t *client)
|
|
|
|
|
{
|
2023-04-10 14:45:27 +02:00
|
|
|
return jack_time_to_frames(client, jack_get_time());
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_nframes_t jack_last_frame_time (const jack_client_t *client)
|
|
|
|
|
{
|
2018-05-24 16:13:23 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct spa_io_position *pos;
|
2019-08-27 14:41:15 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, 0);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2020-04-30 16:15:22 +02:00
|
|
|
if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
|
2019-08-27 14:41:15 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return pos->clock.position;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
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)
|
|
|
|
|
{
|
2018-11-07 09:58:20 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct spa_io_position *pos;
|
2019-08-27 14:41:15 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2020-04-30 16:15:22 +02:00
|
|
|
if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
|
2020-02-24 11:25:31 +01:00
|
|
|
return -EIO;
|
2019-08-27 14:41:15 +02:00
|
|
|
|
|
|
|
|
*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;
|
2018-11-07 09:58:20 +01:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_trace("%p: %d %"PRIu64" %"PRIu64" %f", c, *current_frames,
|
2018-11-07 09:58:20 +01:00
|
|
|
*current_usecs, *next_usecs, *period_usecs);
|
|
|
|
|
return 0;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames)
|
|
|
|
|
{
|
2018-11-07 09:58:20 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct spa_io_position *pos;
|
2019-08-27 14:41:15 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-02-09 16:44:47 +01:00
|
|
|
if (SPA_UNLIKELY((pos = c->rt.position) == NULL) || c->buffer_frames == 0)
|
2019-08-27 14:41:15 +02:00
|
|
|
return 0;
|
|
|
|
|
|
2023-02-16 10:16:36 +01:00
|
|
|
uint32_t nf = (uint32_t)pos->clock.position;
|
2023-02-09 16:44:47 +01:00
|
|
|
uint64_t w = pos->clock.nsec/SPA_NSEC_PER_USEC;
|
|
|
|
|
uint64_t nw = pos->clock.next_nsec/SPA_NSEC_PER_USEC;
|
2023-02-16 10:16:36 +01:00
|
|
|
int32_t df = frames - nf;
|
2023-02-09 16:44:47 +01:00
|
|
|
int64_t dp = nw - w;
|
|
|
|
|
return w + (int64_t)rint((double) df * (double) dp / c->buffer_frames);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usecs)
|
|
|
|
|
{
|
2018-11-07 09:58:20 +01:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct spa_io_position *pos;
|
2019-08-27 14:41:15 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2020-04-30 16:15:22 +02:00
|
|
|
if (SPA_UNLIKELY((pos = c->rt.position) == NULL))
|
2019-08-27 14:41:15 +02:00
|
|
|
return 0;
|
|
|
|
|
|
2023-02-16 10:16:36 +01:00
|
|
|
uint32_t nf = (uint32_t)pos->clock.position;
|
2023-02-09 16:44:47 +01:00
|
|
|
uint64_t w = pos->clock.nsec/SPA_NSEC_PER_USEC;
|
|
|
|
|
uint64_t nw = pos->clock.next_nsec/SPA_NSEC_PER_USEC;
|
|
|
|
|
int64_t du = usecs - w;
|
|
|
|
|
int64_t dp = nw - w;
|
2023-02-16 10:16:36 +01:00
|
|
|
return nf + (int32_t)rint((double)du / (double)dp * c->buffer_frames);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2022-10-10 17:49:57 +03:00
|
|
|
jack_time_t jack_get_time(void)
|
2018-02-09 18:23:36 +01:00
|
|
|
{
|
2023-04-10 14:45:27 +02:00
|
|
|
return get_time_ns()/SPA_NSEC_PER_USEC;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-10 10:48:49 +01:00
|
|
|
SPA_EXPORT
|
|
|
|
|
void default_jack_error_callback(const char *desc)
|
|
|
|
|
{
|
|
|
|
|
pw_log_error("pw jack error: %s",desc);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-10 10:59:24 +01:00
|
|
|
SPA_EXPORT
|
|
|
|
|
void silent_jack_error_callback(const char *desc)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-10 10:48:49 +01:00
|
|
|
SPA_EXPORT
|
|
|
|
|
void (*jack_error_callback)(const char *msg);
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void jack_set_error_function (void (*func)(const char *))
|
|
|
|
|
{
|
2020-02-10 10:48:49 +01:00
|
|
|
jack_error_callback = (func == NULL) ? &default_jack_error_callback : func;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-10 10:59:24 +01:00
|
|
|
SPA_EXPORT
|
|
|
|
|
void default_jack_info_callback(const char *desc)
|
|
|
|
|
{
|
2020-04-22 12:47:18 +02:00
|
|
|
pw_log_info("pw jack info: %s", desc);
|
2020-02-10 10:59:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SPA_EXPORT
|
|
|
|
|
void silent_jack_info_callback(const char *desc)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SPA_EXPORT
|
|
|
|
|
void (*jack_info_callback)(const char *msg);
|
|
|
|
|
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void jack_set_info_function (void (*func)(const char *))
|
|
|
|
|
{
|
2020-02-10 10:59:24 +01:00
|
|
|
jack_info_callback = (func == NULL) ? &default_jack_info_callback : func;
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-02-09 18:23:36 +01:00
|
|
|
void jack_free(void* ptr)
|
|
|
|
|
{
|
2018-02-15 17:57:43 +01:00
|
|
|
free(ptr);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_release_timebase (jack_client_t *client)
|
|
|
|
|
{
|
2019-08-27 14:41:15 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct pw_node_activation *a;
|
2019-08-28 13:55:50 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
|
|
|
|
if ((a = c->driver_activation) == NULL)
|
2019-08-28 13:55:50 +02:00
|
|
|
return -EIO;
|
|
|
|
|
|
2023-07-13 12:25:22 +02:00
|
|
|
if (!SPA_ATOMIC_CAS(a->segment_owner[0], c->node_id, 0))
|
2019-08-28 13:55:50 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
2019-08-27 14:41:15 +02:00
|
|
|
c->timebase_callback = NULL;
|
|
|
|
|
c->timebase_arg = NULL;
|
2019-08-29 14:01:20 +02:00
|
|
|
c->activation->pending_new_pos = false;
|
2019-08-28 13:55:50 +02:00
|
|
|
|
2019-08-27 14:41:15 +02:00
|
|
|
return 0;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_set_sync_callback (jack_client_t *client,
|
|
|
|
|
JackSyncCallback sync_callback,
|
|
|
|
|
void *arg)
|
|
|
|
|
{
|
2021-06-17 10:46:04 +02:00
|
|
|
int res = 0;
|
2018-05-24 16:13:23 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2019-08-28 13:55:50 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-06-17 10:46:04 +02:00
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2023-05-10 12:10:03 +02:00
|
|
|
freeze_callbacks(c);
|
2021-06-17 10:46:04 +02:00
|
|
|
|
2018-05-24 16:13:23 +02:00
|
|
|
c->sync_callback = sync_callback;
|
|
|
|
|
c->sync_arg = arg;
|
2019-08-28 13:55:50 +02:00
|
|
|
|
2019-08-29 15:32:30 +02:00
|
|
|
if ((res = do_activate(c)) < 0)
|
2021-06-17 10:46:04 +02:00
|
|
|
goto done;
|
2019-08-29 14:01:20 +02:00
|
|
|
|
|
|
|
|
c->activation->pending_sync = true;
|
2021-06-17 10:46:04 +02:00
|
|
|
done:
|
2023-05-10 12:10:03 +02:00
|
|
|
thaw_callbacks(c);
|
2021-06-17 10:46:04 +02:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
|
|
|
|
|
|
|
|
|
return res;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_set_sync_timeout (jack_client_t *client,
|
|
|
|
|
jack_time_t timeout)
|
|
|
|
|
{
|
2022-05-20 15:29:59 +00:00
|
|
|
int res = 0;
|
2019-08-28 13:55:50 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct pw_node_activation *a;
|
2019-08-28 13:55:50 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2022-05-20 15:29:59 +00:00
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2019-08-28 13:55:50 +02:00
|
|
|
|
2022-05-20 15:29:59 +00:00
|
|
|
if ((a = c->activation) == NULL)
|
|
|
|
|
res = -EIO;
|
|
|
|
|
else
|
|
|
|
|
a->sync_timeout = timeout;
|
|
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
2019-08-28 13:55:50 +02:00
|
|
|
|
2022-05-20 15:29:59 +00:00
|
|
|
return res;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_set_timebase_callback (jack_client_t *client,
|
|
|
|
|
int conditional,
|
|
|
|
|
JackTimebaseCallback timebase_callback,
|
|
|
|
|
void *arg)
|
|
|
|
|
{
|
2021-06-17 10:46:04 +02:00
|
|
|
int res = 0;
|
2018-05-24 16:13:23 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2019-10-01 16:27:09 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(timebase_callback != NULL, -EINVAL);
|
2019-08-27 21:35:28 +02:00
|
|
|
|
2021-06-17 10:46:04 +02:00
|
|
|
pw_thread_loop_lock(c->context.loop);
|
2023-05-10 12:10:03 +02:00
|
|
|
freeze_callbacks(c);
|
2021-06-17 10:46:04 +02:00
|
|
|
|
2018-05-24 16:13:23 +02:00
|
|
|
c->timebase_callback = timebase_callback;
|
|
|
|
|
c->timebase_arg = arg;
|
2020-08-06 13:49:33 +02:00
|
|
|
c->timeowner_conditional = conditional;
|
|
|
|
|
install_timeowner(c);
|
2019-08-29 14:01:20 +02:00
|
|
|
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_debug("%p: timebase set id:%u", c, c->node_id);
|
2019-10-01 16:27:09 +02:00
|
|
|
|
2019-08-29 15:32:30 +02:00
|
|
|
if ((res = do_activate(c)) < 0)
|
2021-06-17 10:46:04 +02:00
|
|
|
goto done;
|
2019-08-29 14:01:20 +02:00
|
|
|
|
|
|
|
|
c->activation->pending_new_pos = true;
|
2021-06-17 10:46:04 +02:00
|
|
|
done:
|
2023-05-10 12:10:03 +02:00
|
|
|
thaw_callbacks(c);
|
2021-06-17 10:46:04 +02:00
|
|
|
pw_thread_loop_unlock(c->context.loop);
|
2019-08-28 13:55:50 +02:00
|
|
|
|
2021-06-17 10:46:04 +02:00
|
|
|
return res;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_transport_locate (jack_client_t *client,
|
|
|
|
|
jack_nframes_t frame)
|
|
|
|
|
{
|
2019-08-27 14:41:15 +02:00
|
|
|
jack_position_t pos;
|
|
|
|
|
pos.frame = frame;
|
|
|
|
|
pos.valid = (jack_position_bits_t)0;
|
|
|
|
|
return jack_transport_reposition(client, &pos);
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
jack_transport_state_t jack_transport_query (const jack_client_t *client,
|
|
|
|
|
jack_position_t *pos)
|
|
|
|
|
{
|
2018-05-24 16:13:23 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct pw_node_activation *a;
|
2019-08-27 14:41:15 +02:00
|
|
|
jack_transport_state_t jack_state = JackTransportStopped;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, JackTransportStopped);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-07-19 11:50:28 +02:00
|
|
|
if (SPA_LIKELY((a = c->rt.driver_activation) != NULL)) {
|
2019-09-02 12:02:34 +02:00
|
|
|
jack_state = position_to_jack(a, pos);
|
2021-07-19 11:50:28 +02:00
|
|
|
} else if ((a = c->driver_activation) != NULL) {
|
|
|
|
|
jack_state = position_to_jack(a, pos);
|
|
|
|
|
} else if (pos != NULL) {
|
2019-08-27 14:41:15 +02:00
|
|
|
memset(pos, 0, sizeof(jack_position_t));
|
2021-07-19 11:50:28 +02:00
|
|
|
pos->frame_rate = jack_get_sample_rate((jack_client_t*)client);
|
|
|
|
|
}
|
2019-08-27 14:41:15 +02:00
|
|
|
return jack_state;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client)
|
|
|
|
|
{
|
2019-08-27 14:41:15 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct pw_node_activation *a;
|
2019-08-27 14:41:15 +02:00
|
|
|
struct spa_io_position *pos;
|
2019-08-27 21:35:28 +02:00
|
|
|
struct spa_io_segment *seg;
|
2019-08-28 10:27:26 +02:00
|
|
|
uint64_t running;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
if (SPA_UNLIKELY((a = c->rt.driver_activation) == NULL))
|
2020-02-24 11:25:31 +01:00
|
|
|
return -EIO;
|
2019-08-27 14:41:15 +02:00
|
|
|
|
|
|
|
|
pos = &a->position;
|
2019-08-28 10:27:26 +02:00
|
|
|
running = pos->clock.position - pos->offset;
|
2019-08-27 14:41:15 +02:00
|
|
|
|
2019-08-27 21:35:28 +02:00
|
|
|
if (pos->state == SPA_IO_POSITION_STATE_RUNNING) {
|
2023-04-10 14:45:27 +02:00
|
|
|
uint64_t nsecs = get_time_ns() - pos->clock.nsec;
|
2021-07-06 17:55:38 +02:00
|
|
|
running += (uint64_t)floor((((double) c->sample_rate) / SPA_NSEC_PER_SEC) * nsecs);
|
2019-08-27 14:41:15 +02:00
|
|
|
}
|
2019-08-27 21:35:28 +02:00
|
|
|
seg = &pos->segments[0];
|
|
|
|
|
|
2019-08-28 10:27:26 +02:00
|
|
|
return (running - seg->start) * seg->rate + seg->position;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_transport_reposition (jack_client_t *client,
|
|
|
|
|
const jack_position_t *pos)
|
|
|
|
|
{
|
2019-08-27 14:41:15 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct pw_node_activation *a, *na;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
a = c->rt.driver_activation;
|
2020-02-24 11:25:31 +01:00
|
|
|
na = c->activation;
|
2019-09-02 12:02:34 +02:00
|
|
|
if (!a || !na)
|
2019-08-27 14:41:15 +02:00
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
if (pos->valid & ~(JackPositionBBT|JackPositionTimecode))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
pw_log_debug("frame:%u", pos->frame);
|
2021-12-22 19:53:28 +01:00
|
|
|
spa_zero(na->reposition);
|
2019-09-02 12:02:34 +02:00
|
|
|
na->reposition.flags = 0;
|
|
|
|
|
na->reposition.start = 0;
|
|
|
|
|
na->reposition.duration = 0;
|
|
|
|
|
na->reposition.position = pos->frame;
|
|
|
|
|
na->reposition.rate = 1.0;
|
2023-07-13 12:25:22 +02:00
|
|
|
SPA_ATOMIC_STORE(a->reposition_owner, c->node_id);
|
2019-08-27 14:41:15 +02:00
|
|
|
|
|
|
|
|
return 0;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
static void update_command(struct client *c, uint32_t command)
|
2019-08-27 21:35:28 +02:00
|
|
|
{
|
2020-04-30 12:57:56 +02:00
|
|
|
struct pw_node_activation *a = c->rt.driver_activation;
|
2019-08-29 14:01:20 +02:00
|
|
|
if (!a)
|
|
|
|
|
return;
|
2023-07-13 12:25:22 +02:00
|
|
|
SPA_ATOMIC_STORE(a->command, command);
|
2019-08-27 21:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
void jack_transport_start (jack_client_t *client)
|
|
|
|
|
{
|
2019-08-27 14:41:15 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_if_fail(c != NULL);
|
2019-08-29 14:01:20 +02:00
|
|
|
update_command(c, PW_NODE_ACTIVATION_COMMAND_START);
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
void jack_transport_stop (jack_client_t *client)
|
|
|
|
|
{
|
2019-08-27 14:41:15 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_if_fail(c != NULL);
|
2019-08-29 14:01:20 +02:00
|
|
|
update_command(c, PW_NODE_ACTIVATION_COMMAND_STOP);
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
void jack_get_transport_info (jack_client_t *client,
|
|
|
|
|
jack_transport_info_t *tinfo)
|
|
|
|
|
{
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: deprecated", client);
|
2019-08-27 14:41:15 +02:00
|
|
|
if (tinfo)
|
|
|
|
|
memset(tinfo, 0, sizeof(jack_transport_info_t));
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
void jack_set_transport_info (jack_client_t *client,
|
|
|
|
|
jack_transport_info_t *tinfo)
|
|
|
|
|
{
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: deprecated", client);
|
2019-08-27 14:41:15 +02:00
|
|
|
if (tinfo)
|
|
|
|
|
memset(tinfo, 0, sizeof(jack_transport_info_t));
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_set_session_callback (jack_client_t *client,
|
|
|
|
|
JackSessionCallback session_callback,
|
|
|
|
|
void *arg)
|
|
|
|
|
{
|
2019-08-29 14:01:20 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2019-08-29 14:01:20 +02:00
|
|
|
if (c->active) {
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_error("%p: can't set callback on active client", c);
|
2019-08-29 14:01:20 +02:00
|
|
|
return -EIO;
|
|
|
|
|
}
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_warn("%p: not implemented", client);
|
2018-05-21 13:12:26 +02:00
|
|
|
return -ENOTSUP;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_session_reply (jack_client_t *client,
|
|
|
|
|
jack_session_event_t *event)
|
|
|
|
|
{
|
2021-10-03 08:52:27 +02:00
|
|
|
pw_log_warn("%p: not implemented", client);
|
2018-05-21 13:12:26 +02:00
|
|
|
return -ENOTSUP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
void jack_session_event_free (jack_session_event_t *event)
|
|
|
|
|
{
|
2021-02-04 15:48:36 +01:00
|
|
|
if (event) {
|
|
|
|
|
free((void *)event->session_dir);
|
|
|
|
|
free((void *)event->client_uuid);
|
|
|
|
|
free(event->command_line);
|
|
|
|
|
free(event);
|
|
|
|
|
}
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
char *jack_client_get_uuid (jack_client_t *client)
|
|
|
|
|
{
|
2019-08-21 18:37:02 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2022-01-26 17:44:49 +01:00
|
|
|
return spa_aprintf("%"PRIu64, client_make_uuid(c->serial, false));
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2021-02-04 15:48:36 +01:00
|
|
|
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;
|
2021-02-09 10:26:14 +01:00
|
|
|
jack_session_command_t *cmds;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, NULL);
|
2021-02-04 15:48:36 +01:00
|
|
|
pw_log_warn("not implemented");
|
2021-02-09 10:26:14 +01:00
|
|
|
cmds = calloc(1, sizeof(jack_session_command_t));
|
|
|
|
|
return cmds;
|
2021-02-04 15:48:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -1);
|
2021-02-04 15:48:36 +01:00
|
|
|
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;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -1);
|
2021-02-04 15:48:36 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_client_real_time_priority (jack_client_t * client)
|
|
|
|
|
{
|
2021-06-28 11:46:41 +02:00
|
|
|
return jack_client_max_real_time_priority(client) - 5;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_client_max_real_time_priority (jack_client_t *client)
|
|
|
|
|
{
|
2021-06-28 11:46:41 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2021-07-05 11:35:27 +02:00
|
|
|
int min, max;
|
2021-06-28 11:46:41 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, -1);
|
2021-06-28 11:46:41 +02:00
|
|
|
|
2022-03-30 20:31:42 +02:00
|
|
|
spa_thread_utils_get_rt_range(&c->context.thread_utils, NULL, &min, &max);
|
2021-06-28 11:46:41 +02:00
|
|
|
return SPA_MIN(max, c->rt_max) - 1;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_acquire_real_time_scheduling (jack_native_thread_t thread, int priority)
|
|
|
|
|
{
|
2022-10-13 12:09:36 +02:00
|
|
|
struct spa_thread *t = (struct spa_thread*)thread;
|
|
|
|
|
pw_log_info("acquire %p", t);
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(globals.thread_utils != NULL, -1);
|
|
|
|
|
return_val_if_fail(t != NULL, -1);
|
2022-10-13 12:09:36 +02:00
|
|
|
return spa_thread_utils_acquire_rt(globals.thread_utils, t, priority);
|
2021-06-28 11:46:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SPA_EXPORT
|
|
|
|
|
int jack_drop_real_time_scheduling (jack_native_thread_t thread)
|
|
|
|
|
{
|
2022-10-13 12:09:36 +02:00
|
|
|
struct spa_thread *t = (struct spa_thread*)thread;
|
|
|
|
|
pw_log_info("drop %p", t);
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(globals.thread_utils != NULL, -1);
|
|
|
|
|
return_val_if_fail(t != NULL, -1);
|
2022-10-13 12:09:36 +02:00
|
|
|
return spa_thread_utils_drop_rt(globals.thread_utils, t);
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_client_create_thread (jack_client_t* client,
|
|
|
|
|
jack_native_thread_t *thread,
|
|
|
|
|
int priority,
|
2019-08-27 14:41:15 +02:00
|
|
|
int realtime, /* boolean */
|
2018-05-21 13:12:26 +02:00
|
|
|
void *(*start_routine)(void*),
|
|
|
|
|
void *arg)
|
|
|
|
|
{
|
2022-03-30 20:31:42 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2021-07-05 10:40:25 +02:00
|
|
|
int res = 0;
|
2022-03-29 11:37:47 +02:00
|
|
|
struct spa_thread *thr;
|
2021-06-28 11:46:41 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(client != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(thread != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(start_routine != NULL, -EINVAL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
2021-07-05 10:40:25 +02:00
|
|
|
pw_log_info("client %p: create thread rt:%d prio:%d", client, realtime, priority);
|
2018-05-21 13:12:26 +02:00
|
|
|
|
2022-03-30 20:31:42 +02:00
|
|
|
thr = spa_thread_utils_create(&c->context.thread_utils, NULL, start_routine, arg);
|
2022-03-29 11:37:47 +02:00
|
|
|
if (thr == NULL)
|
|
|
|
|
res = -errno;
|
|
|
|
|
*thread = (pthread_t)thr;
|
2021-06-28 11:46:41 +02:00
|
|
|
|
2022-03-29 11:37:47 +02:00
|
|
|
if (res != 0) {
|
|
|
|
|
pw_log_warn("client %p: create RT thread failed: %s",
|
|
|
|
|
client, strerror(res));
|
|
|
|
|
} else if (realtime) {
|
2021-06-28 11:46:41 +02:00
|
|
|
/* 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;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_client_stop_thread(jack_client_t* client, jack_native_thread_t thread)
|
|
|
|
|
{
|
2022-03-30 20:31:42 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2019-09-05 13:17:00 +02:00
|
|
|
void* status;
|
|
|
|
|
|
|
|
|
|
if (thread == (jack_native_thread_t)NULL)
|
2020-02-24 11:25:31 +01:00
|
|
|
return -EINVAL;
|
2019-09-05 13:17:00 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(client != NULL, -EINVAL);
|
2022-03-30 20:31:42 +02:00
|
|
|
|
2023-02-25 20:45:28 +01:00
|
|
|
pw_log_debug("join thread %p", (void *) thread);
|
2022-03-30 20:31:42 +02:00
|
|
|
spa_thread_utils_join(&c->context.thread_utils, (struct spa_thread*)thread, &status);
|
2023-02-25 20:45:28 +01:00
|
|
|
pw_log_debug("stopped thread %p", (void *) thread);
|
2019-09-05 13:17:00 +02:00
|
|
|
return 0;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
int jack_client_kill_thread(jack_client_t* client, jack_native_thread_t thread)
|
|
|
|
|
{
|
2022-03-30 20:31:42 +02:00
|
|
|
struct client *c = (struct client *) client;
|
2019-09-05 13:17:00 +02:00
|
|
|
void* status;
|
|
|
|
|
|
|
|
|
|
if (thread == (jack_native_thread_t)NULL)
|
2020-02-24 11:25:31 +01:00
|
|
|
return -EINVAL;
|
2019-09-05 13:17:00 +02:00
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(client != NULL, -EINVAL);
|
2022-03-30 20:31:42 +02:00
|
|
|
|
2023-02-25 20:45:28 +01:00
|
|
|
pw_log_debug("cancel thread %p", (void *) thread);
|
2021-07-05 10:40:25 +02:00
|
|
|
pthread_cancel(thread);
|
2023-02-25 20:45:28 +01:00
|
|
|
pw_log_debug("join thread %p", (void *) thread);
|
2022-03-30 20:31:42 +02:00
|
|
|
spa_thread_utils_join(&c->context.thread_utils, (struct spa_thread*)thread, &status);
|
2023-02-25 20:45:28 +01:00
|
|
|
pw_log_debug("stopped thread %p", (void *) thread);
|
2019-09-05 13:17:00 +02:00
|
|
|
return 0;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-05-21 13:12:26 +02:00
|
|
|
void jack_set_thread_creator (jack_thread_creator_t creator)
|
|
|
|
|
{
|
2021-07-05 10:40:25 +02:00
|
|
|
globals.creator = creator;
|
2018-05-21 13:12:26 +02:00
|
|
|
}
|
|
|
|
|
|
2018-10-03 20:13:57 +02:00
|
|
|
static inline uint8_t * midi_event_data (void* port_buffer,
|
|
|
|
|
const struct midi_event* event)
|
|
|
|
|
{
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_LIKELY(event->size <= MIDI_INLINE_MAX))
|
2018-10-03 20:13:57 +02:00
|
|
|
return (uint8_t *)event->inline_data;
|
|
|
|
|
else
|
2021-05-06 13:41:44 +10:00
|
|
|
return SPA_PTROFF(port_buffer, event->byte_offset, uint8_t);
|
2018-10-03 20:13:57 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-10-03 20:13:57 +02:00
|
|
|
uint32_t jack_midi_get_event_count(void* port_buffer)
|
|
|
|
|
{
|
|
|
|
|
struct midi_buffer *mb = port_buffer;
|
2021-08-25 11:54:41 +02:00
|
|
|
if (mb == NULL || mb->magic != MIDI_BUFFER_MAGIC)
|
2021-03-29 11:14:37 +02:00
|
|
|
return 0;
|
2018-10-03 20:13:57 +02:00
|
|
|
return mb->event_count;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-10-03 20:13:57 +02:00
|
|
|
int jack_midi_event_get(jack_midi_event_t *event,
|
|
|
|
|
void *port_buffer,
|
|
|
|
|
uint32_t event_index)
|
|
|
|
|
{
|
|
|
|
|
struct midi_buffer *mb = port_buffer;
|
2021-05-06 13:41:44 +10:00
|
|
|
struct midi_event *ev = SPA_PTROFF(mb, sizeof(*mb), struct midi_event);
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(mb != NULL, -EINVAL);
|
|
|
|
|
return_val_if_fail(ev != NULL, -EINVAL);
|
2020-10-19 23:30:44 +02:00
|
|
|
if (event_index >= mb->event_count)
|
|
|
|
|
return -ENOBUFS;
|
2018-10-03 20:13:57 +02:00
|
|
|
ev += event_index;
|
|
|
|
|
event->time = ev->time;
|
|
|
|
|
event->size = ev->size;
|
|
|
|
|
event->buffer = midi_event_data (port_buffer, ev);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-10-03 20:13:57 +02:00
|
|
|
void jack_midi_clear_buffer(void *port_buffer)
|
|
|
|
|
{
|
|
|
|
|
struct midi_buffer *mb = port_buffer;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_if_fail(mb != NULL);
|
2018-10-03 20:13:57 +02:00
|
|
|
mb->event_count = 0;
|
|
|
|
|
mb->write_pos = 0;
|
|
|
|
|
mb->lost_events = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-10-03 20:13:57 +02:00
|
|
|
void jack_midi_reset_buffer(void *port_buffer)
|
|
|
|
|
{
|
|
|
|
|
jack_midi_clear_buffer(port_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-10-03 20:13:57 +02:00
|
|
|
size_t jack_midi_max_event_size(void* port_buffer)
|
|
|
|
|
{
|
|
|
|
|
struct midi_buffer *mb = port_buffer;
|
2020-02-24 11:25:31 +01:00
|
|
|
size_t buffer_size;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(mb != NULL, 0);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
|
|
|
|
buffer_size = mb->buffer_size;
|
2018-10-03 20:13:57 +02:00
|
|
|
|
|
|
|
|
/* (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));
|
|
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(used_size > buffer_size)) {
|
2018-10-03 20:13:57 +02:00
|
|
|
return 0;
|
2020-03-16 12:52:28 +01:00
|
|
|
} else if (SPA_LIKELY((buffer_size - used_size) < MIDI_INLINE_MAX)) {
|
2018-10-03 20:13:57 +02:00
|
|
|
return MIDI_INLINE_MAX;
|
|
|
|
|
} else {
|
|
|
|
|
return buffer_size - used_size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-10-03 20:13:57 +02:00
|
|
|
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;
|
2021-05-06 13:41:44 +10:00
|
|
|
struct midi_event *events = SPA_PTROFF(mb, sizeof(*mb), struct midi_event);
|
2020-02-24 11:25:31 +01:00
|
|
|
size_t buffer_size;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(mb != NULL, NULL);
|
2020-02-24 11:25:31 +01:00
|
|
|
|
|
|
|
|
buffer_size = mb->buffer_size;
|
2018-10-03 20:13:57 +02:00
|
|
|
|
2020-03-17 11:37:56 +01:00
|
|
|
if (SPA_UNLIKELY(time >= mb->nframes)) {
|
2019-09-10 18:49:19 +02:00
|
|
|
pw_log_warn("midi %p: time:%d frames:%d", port_buffer, time, mb->nframes);
|
|
|
|
|
goto failed;
|
|
|
|
|
}
|
2018-10-03 20:13:57 +02:00
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(mb->event_count > 0 && time < events[mb->event_count - 1].time)) {
|
2019-09-10 18:49:19 +02:00
|
|
|
pw_log_warn("midi %p: time:%d ev:%d", port_buffer, time, mb->event_count);
|
|
|
|
|
goto failed;
|
|
|
|
|
}
|
2018-10-03 20:13:57 +02:00
|
|
|
|
|
|
|
|
/* Check if data_size is >0 and there is enough space in the buffer for the event. */
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(data_size <= 0)) {
|
2019-09-10 18:49:19 +02:00
|
|
|
pw_log_warn("midi %p: data_size:%zd", port_buffer, data_size);
|
2018-10-03 20:13:57 +02:00
|
|
|
goto failed; // return NULL?
|
2020-03-16 12:52:28 +01:00
|
|
|
} else if (SPA_UNLIKELY(jack_midi_max_event_size (port_buffer) < data_size)) {
|
2019-09-10 18:49:19 +02:00
|
|
|
pw_log_warn("midi %p: event too large: data_size:%zd", port_buffer, data_size);
|
2018-10-03 20:13:57 +02:00
|
|
|
goto failed;
|
|
|
|
|
} else {
|
|
|
|
|
struct midi_event *ev = &events[mb->event_count];
|
|
|
|
|
uint8_t *res;
|
|
|
|
|
|
|
|
|
|
ev->time = time;
|
|
|
|
|
ev->size = data_size;
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_LIKELY(data_size <= MIDI_INLINE_MAX)) {
|
2018-10-03 20:13:57 +02:00
|
|
|
res = ev->inline_data;
|
|
|
|
|
} else {
|
|
|
|
|
mb->write_pos += data_size;
|
|
|
|
|
ev->byte_offset = buffer_size - 1 - mb->write_pos;
|
2021-05-06 13:41:44 +10:00
|
|
|
res = SPA_PTROFF(mb, ev->byte_offset, uint8_t);
|
2018-10-03 20:13:57 +02:00
|
|
|
}
|
|
|
|
|
mb->event_count += 1;
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
failed:
|
|
|
|
|
mb->lost_events++;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-10-03 20:13:57 +02:00
|
|
|
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);
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(retbuf == NULL))
|
2020-02-24 11:25:31 +01:00
|
|
|
return -ENOBUFS;
|
2020-03-16 12:52:28 +01:00
|
|
|
memcpy (retbuf, data, data_size);
|
|
|
|
|
return 0;
|
2018-10-03 20:13:57 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-06 13:23:15 +01:00
|
|
|
SPA_EXPORT
|
2018-10-03 20:13:57 +02:00
|
|
|
uint32_t jack_midi_get_lost_event_count(void *port_buffer)
|
|
|
|
|
{
|
|
|
|
|
struct midi_buffer *mb = port_buffer;
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(mb != NULL, 0);
|
2018-10-03 20:13:57 +02:00
|
|
|
return mb->lost_events;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-07 13:31:49 +01:00
|
|
|
/** extensions */
|
|
|
|
|
|
|
|
|
|
SPA_EXPORT
|
|
|
|
|
int jack_get_video_image_size(jack_client_t *client, jack_image_size_t *size)
|
|
|
|
|
{
|
|
|
|
|
struct client *c = (struct client *) client;
|
2020-02-24 11:25:31 +01:00
|
|
|
struct pw_node_activation *a;
|
|
|
|
|
|
2023-04-24 13:23:40 +02:00
|
|
|
return_val_if_fail(c != NULL, 0);
|
2020-02-07 13:31:49 +01:00
|
|
|
|
2020-04-30 12:57:56 +02:00
|
|
|
a = c->rt.driver_activation;
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(a == NULL))
|
2020-02-07 13:31:49 +01:00
|
|
|
a = c->activation;
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(a == NULL))
|
2020-02-07 13:31:49 +01:00
|
|
|
return -EIO;
|
|
|
|
|
|
2020-03-16 12:52:28 +01:00
|
|
|
if (SPA_UNLIKELY(!(a->position.video.flags & SPA_IO_VIDEO_SIZE_VALID)))
|
2020-02-07 13:31:49 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-02-09 18:23:36 +01:00
|
|
|
static void reg(void) __attribute__ ((constructor));
|
|
|
|
|
static void reg(void)
|
|
|
|
|
{
|
|
|
|
|
pw_init(NULL, NULL);
|
2021-10-03 08:52:27 +02:00
|
|
|
PW_LOG_TOPIC_INIT(jack_log_topic);
|
2020-04-30 12:57:56 +02:00
|
|
|
pthread_mutex_init(&globals.lock, NULL);
|
|
|
|
|
pw_array_init(&globals.descriptions, 16);
|
2022-01-13 13:45:53 +01:00
|
|
|
spa_list_init(&globals.free_objects);
|
2018-02-09 18:23:36 +01:00
|
|
|
}
|