pipewire-jack.c 127 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1
2
/* PipeWire
 *
3
 * Copyright © 2018 Wim Taymans
Wim Taymans's avatar
Wim Taymans committed
4
 *
5
6
7
8
9
10
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
Wim Taymans's avatar
Wim Taymans committed
11
 *
12
13
14
15
16
17
18
19
20
21
22
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
Wim Taymans's avatar
Wim Taymans committed
23
24
25
26
27
28
29
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
Wim Taymans's avatar
Wim Taymans committed
30
31
#include <unistd.h>
#include <sys/mman.h>
32
#include <regex.h>
Wim Taymans's avatar
Wim Taymans committed
33
#include <math.h>
Wim Taymans's avatar
Wim Taymans committed
34
35

#include <jack/jack.h>
Wim Taymans's avatar
Wim Taymans committed
36
37
#include <jack/session.h>
#include <jack/thread.h>
38
#include <jack/midiport.h>
Wim Taymans's avatar
Wim Taymans committed
39
#include <jack/uuid.h>
40
#include <jack/metadata.h>
Wim Taymans's avatar
Wim Taymans committed
41

42
#include <spa/support/cpu.h>
Wim Taymans's avatar
Wim Taymans committed
43
#include <spa/param/audio/format-utils.h>
44
#include <spa/param/video/format-utils.h>
Wim Taymans's avatar
Wim Taymans committed
45
#include <spa/debug/types.h>
46
#include <spa/debug/pod.h>
Wim Taymans's avatar
Wim Taymans committed
47
48

#include <pipewire/pipewire.h>
Wim Taymans's avatar
Wim Taymans committed
49
#include <pipewire/private.h>
50
#include <pipewire/data-loop.h>
Wim Taymans's avatar
Wim Taymans committed
51
52

#include "extensions/client-node.h"
53
#include "extensions/metadata.h"
54
#include "pipewire-jack-extensions.h"
Wim Taymans's avatar
Wim Taymans committed
55

56
57
#define JACK_DEFAULT_VIDEO_TYPE	"32 bit float RGBA video"

Wim Taymans's avatar
Wim Taymans committed
58
59
60
61
#define JACK_CLIENT_NAME_SIZE		64
#define JACK_PORT_NAME_SIZE		256
#define JACK_PORT_MAX			4096
#define JACK_PORT_TYPE_SIZE             32
Wim Taymans's avatar
Wim Taymans committed
62
#define CONNECTION_NUM_FOR_PORT		1024
Wim Taymans's avatar
Wim Taymans committed
63

64
#define MAX_BUFFER_FRAMES		8192
65

66
#define MAX_ALIGN			16
Wim Taymans's avatar
Wim Taymans committed
67
#define MAX_PORTS			1024
Wim Taymans's avatar
Wim Taymans committed
68
#define MAX_BUFFERS			2
Wim Taymans's avatar
Wim Taymans committed
69
#define MAX_BUFFER_DATAS		1u
Wim Taymans's avatar
Wim Taymans committed
70

Wim Taymans's avatar
Wim Taymans committed
71
#define REAL_JACK_PORT_NAME_SIZE (JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE)
Wim Taymans's avatar
Wim Taymans committed
72

Wim Taymans's avatar
Wim Taymans committed
73
74
#define NAME	"jack-client"

Wim Taymans's avatar
Wim Taymans committed
75
76
77
78
79
#define TYPE_ID_AUDIO	0
#define TYPE_ID_MIDI	1
#define TYPE_ID_VIDEO	2
#define TYPE_ID_OTHER	3

Wim Taymans's avatar
Wim Taymans committed
80
struct client;
Wim Taymans's avatar
Wim Taymans committed
81
struct port;
Wim Taymans's avatar
Wim Taymans committed
82

Wim Taymans's avatar
Wim Taymans committed
83
84
struct globals {
	jack_thread_creator_t creator;
Wim Taymans's avatar
Wim Taymans committed
85
	pthread_mutex_t lock;
86
	struct pw_array descriptions;
Wim Taymans's avatar
Wim Taymans committed
87
88
89
};

static struct globals globals;
90
static bool mlock_warned = false;
Wim Taymans's avatar
Wim Taymans committed
91

92
#define OBJECT_CHUNK	8
Wim Taymans's avatar
Wim Taymans committed
93

94
95
96
97
typedef void (*mix2_func) (float *dst, float *src1, float *src2, int n_samples);

static mix2_func mix2;

98
99
100
101
102
struct object {
	struct spa_list link;

	struct client *client;

Wim Taymans's avatar
Wim Taymans committed
103
104
105
#define INTERFACE_Port	0
#define INTERFACE_Node	1
#define INTERFACE_Link	2
106
	uint32_t type;
Wim Taymans's avatar
Wim Taymans committed
107
	uint32_t id;
108
109
110

	union {
		struct {
Wim Taymans's avatar
Wim Taymans committed
111
			char name[JACK_CLIENT_NAME_SIZE+1];
Wim Taymans's avatar
Wim Taymans committed
112
			int32_t priority;
113
			uint32_t client_id;
114
115
116
117
118
119
120
		} node;
		struct {
			uint32_t src;
			uint32_t dst;
		} port_link;
		struct {
			unsigned long flags;
Wim Taymans's avatar
Wim Taymans committed
121
122
123
			char name[REAL_JACK_PORT_NAME_SIZE+1];
			char alias1[REAL_JACK_PORT_NAME_SIZE+1];
			char alias2[REAL_JACK_PORT_NAME_SIZE+1];
124
			uint32_t type_id;
Wim Taymans's avatar
Wim Taymans committed
125
			uint32_t node_id;
126
			uint32_t port_id;
Wim Taymans's avatar
Wim Taymans committed
127
			uint32_t monitor_requests;
128
129
			jack_latency_range_t capture_latency;
			jack_latency_range_t playback_latency;
Wim Taymans's avatar
Wim Taymans committed
130
			int32_t priority;
Wim Taymans's avatar
Wim Taymans committed
131
			struct port *port;
132
			bool is_monitor;
133
134
		} port;
	};
Wim Taymans's avatar
Wim Taymans committed
135
136
};

Wim Taymans's avatar
Wim Taymans committed
137
138
139
140
141
142
143
144
145
146
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;
};

147
148
149
150
151
152
153
154
155
156
157
#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];
	};
};

Wim Taymans's avatar
Wim Taymans committed
158
159
160
161
162
163
164
165
166
167
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;

Wim Taymans's avatar
Wim Taymans committed
168
	struct pw_memmap *mem[MAX_BUFFER_DATAS+1];
Wim Taymans's avatar
Wim Taymans committed
169
170
171
	uint32_t n_mem;
};

Wim Taymans's avatar
Wim Taymans committed
172
173
174
175
176
177
178
179
180
181
182
183
184
struct mix {
	struct spa_list link;
	struct spa_list port_link;
	uint32_t id;
	struct port *port;

	struct spa_io_buffers *io;

	struct buffer buffers[MAX_BUFFERS];
	uint32_t n_buffers;
	struct spa_list queue;
};

Wim Taymans's avatar
Wim Taymans committed
185
186
struct port {
	bool valid;
Wim Taymans's avatar
Wim Taymans committed
187
	struct spa_list link;
Wim Taymans's avatar
Wim Taymans committed
188
189
190

	struct client *client;

Wim Taymans's avatar
Wim Taymans committed
191
	enum spa_direction direction;
Wim Taymans's avatar
Wim Taymans committed
192
	uint32_t id;
193
	struct object *object;
Wim Taymans's avatar
Wim Taymans committed
194

Wim Taymans's avatar
Wim Taymans committed
195
	struct spa_io_buffers io;
Wim Taymans's avatar
Wim Taymans committed
196
	struct spa_list mix;
Wim Taymans's avatar
Wim Taymans committed
197
	struct mix *global_mix;
Wim Taymans's avatar
Wim Taymans committed
198

199
200
201
	unsigned int empty_out:1;
	unsigned int zeroed:1;

202
	float *emptyptr;
203
	float empty[MAX_BUFFER_FRAMES + MAX_ALIGN];
Wim Taymans's avatar
Wim Taymans committed
204
205

	void *(*get_buffer) (struct port *p, jack_nframes_t frames);
Wim Taymans's avatar
Wim Taymans committed
206
207
};

208
209
210
211
212
213
214
215
216
217
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;
};

Wim Taymans's avatar
Wim Taymans committed
218
struct context {
219
	struct pw_loop *l;
Wim Taymans's avatar
Wim Taymans committed
220
	struct pw_thread_loop *loop;	/* thread_lock protects all below */
Wim Taymans's avatar
Wim Taymans committed
221
	struct pw_context *context;
Wim Taymans's avatar
Wim Taymans committed
222

223
	struct spa_list free_objects;
Wim Taymans's avatar
Wim Taymans committed
224
225
226

	pthread_mutex_t lock;		/* protects map and lists below, in addition to thread_lock */
	struct pw_map globals;
227
228
229
	struct spa_list ports;
	struct spa_list nodes;
	struct spa_list links;
Wim Taymans's avatar
Wim Taymans committed
230
231
};

232
233
#define GET_DIRECTION(f)	((f) & JackPortIsInput ? SPA_DIRECTION_INPUT : SPA_DIRECTION_OUTPUT)

234
235
#define GET_IN_PORT(c,p)	(c->port_pool[SPA_DIRECTION_INPUT][p])
#define GET_OUT_PORT(c,p)	(c->port_pool[SPA_DIRECTION_OUTPUT][p])
236
#define GET_PORT(c,d,p)		(d == SPA_DIRECTION_INPUT ? GET_IN_PORT(c,p) : GET_OUT_PORT(c,p))
Wim Taymans's avatar
Wim Taymans committed
237

238
239
240
struct metadata {
	struct pw_metadata *proxy;
	struct spa_hook listener;
241

242
243
	uint32_t default_audio_sink;
	uint32_t default_audio_source;
244
245
};

Wim Taymans's avatar
Wim Taymans committed
246
struct client {
Wim Taymans's avatar
Wim Taymans committed
247
	char name[JACK_CLIENT_NAME_SIZE+1];
Wim Taymans's avatar
Wim Taymans committed
248
249
250

	struct context context;

251
252
253
254
255
	char *server_name;
	char *load_name;		/* load module name */
	char *load_init;		/* initialization string */
	jack_uuid_t session_id;		/* requested session_id */

256
	struct pw_data_loop *loop;
257
	struct pw_properties *props;
258

Wim Taymans's avatar
Wim Taymans committed
259
	struct pw_core *core;
260
	struct spa_hook core_listener;
261
	struct pw_mempool *pool;
Wim Taymans's avatar
Wim Taymans committed
262
	int last_sync;
Wim Taymans's avatar
Wim Taymans committed
263
	int last_res;
Wim Taymans's avatar
Wim Taymans committed
264
265
	bool error;

Wim Taymans's avatar
Wim Taymans committed
266
	struct pw_registry *registry;
Wim Taymans's avatar
Wim Taymans committed
267
268
	struct spa_hook registry_listener;

269
	struct pw_client_node *node;
Wim Taymans's avatar
Wim Taymans committed
270
271
272
	struct spa_hook node_listener;
        struct spa_hook proxy_listener;

273
274
	struct metadata *metadata;

Wim Taymans's avatar
Wim Taymans committed
275
276
277
278
279
	uint32_t node_id;
	struct spa_source *socket_source;

	JackThreadCallback thread_callback;
	void *thread_arg;
280
281
282
283
284
285
	JackThreadInitCallback thread_init_callback;
	void *thread_init_arg;
	JackShutdownCallback shutdown_callback;
	void *shutdown_arg;
	JackInfoShutdownCallback info_shutdown_callback;
	void *info_shutdown_arg;
Wim Taymans's avatar
Wim Taymans committed
286
287
	JackProcessCallback process_callback;
	void *process_arg;
288
289
290
291
292
293
294
295
296
297
298
299
	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;
Wim Taymans's avatar
Wim Taymans committed
300
301
	JackPortRenameCallback rename_callback;
	void *rename_arg;
Wim Taymans's avatar
Wim Taymans committed
302
303
	JackGraphOrderCallback graph_callback;
	void *graph_arg;
Wim Taymans's avatar
Wim Taymans committed
304
305
	JackXRunCallback xrun_callback;
	void *xrun_arg;
306
307
	JackLatencyCallback latency_callback;
	void *latency_arg;
308
309
310
311
	JackSyncCallback sync_callback;
	void *sync_arg;
	JackTimebaseCallback timebase_callback;
	void *timebase_arg;
312
313
	JackPropertyChangeCallback property_callback;
	void *property_arg;
Wim Taymans's avatar
Wim Taymans committed
314

Wim Taymans's avatar
Wim Taymans committed
315
	struct spa_io_position *position;
Wim Taymans's avatar
Wim Taymans committed
316
	uint32_t sample_rate;
317
	uint32_t buffer_frames;
Wim Taymans's avatar
Wim Taymans committed
318

Wim Taymans's avatar
Wim Taymans committed
319
320
	struct spa_list free_mix;

321
322
	uint32_t n_port_pool[2];
	struct port *port_pool[2][MAX_PORTS];
Wim Taymans's avatar
Wim Taymans committed
323
324
	struct spa_list ports[2];
	struct spa_list free_ports[2];
Wim Taymans's avatar
Wim Taymans committed
325

326
	struct spa_list links;
Wim Taymans's avatar
Wim Taymans committed
327
328
	uint32_t driver_id;
	struct pw_node_activation *driver_activation;
Wim Taymans's avatar
Wim Taymans committed
329

Wim Taymans's avatar
Wim Taymans committed
330
	struct pw_memmap *mem;
Wim Taymans's avatar
Wim Taymans committed
331
	struct pw_node_activation *activation;
Wim Taymans's avatar
Wim Taymans committed
332
	uint32_t xrun_count;
333

Wim Taymans's avatar
Wim Taymans committed
334
	struct {
335
		struct spa_io_position *position;
Wim Taymans's avatar
Wim Taymans committed
336
337
338
		struct pw_node_activation *driver_activation;
		struct spa_list target_links;
	} rt;
339

340
341
	int pending;

342
	unsigned int started:1;
343
344
	unsigned int active:1;
	unsigned int destroyed:1;
Wim Taymans's avatar
Wim Taymans committed
345
	unsigned int first:1;
346
	unsigned int thread_entered:1;
347
	unsigned int has_transport:1;
Wim Taymans's avatar
Wim Taymans committed
348
	unsigned int allow_mlock:1;
Wim Taymans's avatar
Wim Taymans committed
349
350
	unsigned int timeowner_pending:1;
	unsigned int timeowner_conditional:1;
Wim Taymans's avatar
Wim Taymans committed
351
352
353

	jack_position_t jack_position;
	jack_transport_state_t jack_state;
Wim Taymans's avatar
Wim Taymans committed
354
355
};

Wim Taymans's avatar
Wim Taymans committed
356
357
static int do_sync(struct client *client);

358
359
#include "metadata.c"

Wim Taymans's avatar
Wim Taymans committed
360
361
362
363
static void init_port_pool(struct client *c, enum spa_direction direction)
{
	spa_list_init(&c->ports[direction]);
	spa_list_init(&c->free_ports[direction]);
364
	c->n_port_pool[direction] = 0;
Wim Taymans's avatar
Wim Taymans committed
365
366
}

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
static struct object * alloc_object(struct client *c)
{
	struct object *o;
	int i;

	if (spa_list_is_empty(&c->context.free_objects)) {
		o = calloc(OBJECT_CHUNK, sizeof(struct object));
		if (o == NULL)
			return NULL;
		for (i = 0; i < OBJECT_CHUNK; i++)
			spa_list_append(&c->context.free_objects, &o[i].link);
	}

        o = spa_list_first(&c->context.free_objects, struct object, link);
        spa_list_remove(&o->link);
	o->client = c;

	return o;
}

static void free_object(struct client *c, struct object *o)
{
Wim Taymans's avatar
Wim Taymans committed
389
	pthread_mutex_lock(&c->context.lock);
390
        spa_list_remove(&o->link);
Wim Taymans's avatar
Wim Taymans committed
391
	pthread_mutex_unlock(&c->context.lock);
392
393
394
	spa_list_append(&c->context.free_objects, &o->link);
}

Wim Taymans's avatar
Wim Taymans committed
395
396
397
398
399
400
401
402
403
404
405
static void init_mix(struct mix *mix, uint32_t mix_id, struct port *port)
{
	mix->id = mix_id;
	mix->port = port;
	mix->io = NULL;
	mix->n_buffers = 0;
	spa_list_init(&mix->queue);
	if (mix_id == SPA_ID_INVALID)
		port->global_mix = mix;
}

406
static struct mix *find_mix(struct client *c, struct port *port, uint32_t mix_id)
Wim Taymans's avatar
Wim Taymans committed
407
408
409
410
411
412
413
{
	struct mix *mix;

	spa_list_for_each(mix, &port->mix, port_link) {
		if (mix->id == mix_id)
			return mix;
	}
414
415
416
417
418
419
	return NULL;
}

static struct mix *ensure_mix(struct client *c, struct port *port, uint32_t mix_id)
{
	struct mix *mix;
420
	uint32_t i;
421
422
423
424

	if ((mix = find_mix(c, port, mix_id)) != NULL)
		return mix;

425
426
427
428
429
430
431
	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);
	}
Wim Taymans's avatar
Wim Taymans committed
432
433
434
435
436
	mix = spa_list_first(&c->free_mix, struct mix, link);
	spa_list_remove(&mix->link);

	spa_list_append(&port->mix, &mix->port_link);

Wim Taymans's avatar
Wim Taymans committed
437
	init_mix(mix, mix_id, port);
Wim Taymans's avatar
Wim Taymans committed
438
439
440
441

	return mix;
}

442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
static int clear_buffers(struct client *c, struct mix *mix)
{
	struct port *port = mix->port;
	struct buffer *b;
	uint32_t i, j;

	pw_log_debug(NAME" %p: port %p clear buffers", c, port);

	for (i = 0; i < mix->n_buffers; i++) {
		b = &mix->buffers[i];

		for (j = 0; j < b->n_mem; j++)
			pw_memmap_free(b->mem[j]);

		b->n_mem = 0;
	}
	mix->n_buffers = 0;
	spa_list_init(&mix->queue);
	return 0;
}

Wim Taymans's avatar
Wim Taymans committed
463
464
static void free_mix(struct client *c, struct mix *mix)
{
465
	clear_buffers(c, mix);
Wim Taymans's avatar
Wim Taymans committed
466
	spa_list_remove(&mix->port_link);
Wim Taymans's avatar
Wim Taymans committed
467
468
	if (mix->id == SPA_ID_INVALID)
		mix->port->global_mix = NULL;
Wim Taymans's avatar
Wim Taymans committed
469
470
471
	spa_list_append(&c->free_mix, &mix->link);
}

Wim Taymans's avatar
Wim Taymans committed
472
static struct port * alloc_port(struct client *c, enum spa_direction direction)
Wim Taymans's avatar
Wim Taymans committed
473
{
Wim Taymans's avatar
Wim Taymans committed
474
	struct port *p;
475
	struct object *o;
476
	uint32_t i, n;
Wim Taymans's avatar
Wim Taymans committed
477

478
479
480
481
482
483
484
485
486
487
488
489
490
	if (spa_list_is_empty(&c->free_ports[direction])) {
		p = calloc(OBJECT_CHUNK, sizeof(struct port));
		if (p == NULL)
			return NULL;
		n = c->n_port_pool[direction];
		for (i = 0; i < OBJECT_CHUNK; i++, n++) {
			p[i].direction = direction;
			p[i].id = n;
			p[i].emptyptr = SPA_PTR_ALIGN(p[i].empty, MAX_ALIGN, float);
			c->port_pool[direction][n] = &p[i];
			spa_list_append(&c->free_ports[direction], &p[i].link);
		}
		c->n_port_pool[direction] = n;
Wim Taymans's avatar
Wim Taymans committed
491

492
	}
Wim Taymans's avatar
Wim Taymans committed
493
494
	p = spa_list_first(&c->free_ports[direction], struct port, link);
	spa_list_remove(&p->link);
495
496

	o = alloc_object(c);
Wim Taymans's avatar
Wim Taymans committed
497
	o->type = INTERFACE_Port;
498
	o->id = SPA_ID_INVALID;
Wim Taymans's avatar
Wim Taymans committed
499
	o->port.node_id = c->node_id;
Wim Taymans's avatar
Wim Taymans committed
500
	o->port.port_id = p->id;
Wim Taymans's avatar
Wim Taymans committed
501
	o->port.port = p;
502

Wim Taymans's avatar
Wim Taymans committed
503
	p->valid = true;
Wim Taymans's avatar
Wim Taymans committed
504
	p->zeroed = false;
Wim Taymans's avatar
Wim Taymans committed
505
	p->client = c;
506
	p->object = o;
Wim Taymans's avatar
Wim Taymans committed
507
508
509
	spa_list_init(&p->mix);

	spa_list_append(&c->ports[direction], &p->link);
Wim Taymans's avatar
Wim Taymans committed
510

Wim Taymans's avatar
Wim Taymans committed
511
512
513
514
	pthread_mutex_lock(&c->context.lock);
	spa_list_append(&c->context.ports, &o->link);
	pthread_mutex_unlock(&c->context.lock);

Wim Taymans's avatar
Wim Taymans committed
515
	return p;
Wim Taymans's avatar
Wim Taymans committed
516
517
}

Wim Taymans's avatar
Wim Taymans committed
518
519
static void free_port(struct client *c, struct port *p)
{
Wim Taymans's avatar
Wim Taymans committed
520
	struct mix *m;
Wim Taymans's avatar
Wim Taymans committed
521

Wim Taymans's avatar
Wim Taymans committed
522
523
524
	if (!p->valid)
		return;

Wim Taymans's avatar
Wim Taymans committed
525
	spa_list_consume(m, &p->mix, port_link)
Wim Taymans's avatar
Wim Taymans committed
526
527
528
		free_mix(c, m);

	spa_list_remove(&p->link);
Wim Taymans's avatar
Wim Taymans committed
529
	p->valid = false;
530
	free_object(c, p->object);
Wim Taymans's avatar
Wim Taymans committed
531
	spa_list_append(&c->free_ports[p->direction], &p->link);
Wim Taymans's avatar
Wim Taymans committed
532
533
}

534
535
536
537
538
539
540
541
542
543
544
static struct object *find_node(struct client *c, const char *name)
{
	struct object *o;

	spa_list_for_each(o, &c->context.nodes, link) {
		if (!strcmp(o->node.name, name))
			return o;
	}
	return NULL;
}

545
static struct object *find_port(struct client *c, const char *name)
Wim Taymans's avatar
Wim Taymans committed
546
{
547
	struct object *o;
Wim Taymans's avatar
Wim Taymans committed
548

549
	spa_list_for_each(o, &c->context.ports, link) {
Wim Taymans's avatar
Wim Taymans committed
550
551
552
		if (strcmp(o->port.name, name) == 0 ||
		    strcmp(o->port.alias1, name) == 0 ||
		    strcmp(o->port.alias2, name) == 0)
553
			return o;
Wim Taymans's avatar
Wim Taymans committed
554
555
556
557
	}
	return NULL;
}

Wim Taymans's avatar
Wim Taymans committed
558
559
560
561
562
563
564
565
566
567
568
569
570
static struct object *find_link(struct client *c, uint32_t src, uint32_t dst)
{
	struct object *l;

	spa_list_for_each(l, &c->context.links, link) {
		if (l->port_link.src == src &&
		    l->port_link.dst == dst) {
			return l;
		}
	}
	return NULL;
}

Wim Taymans's avatar
Wim Taymans committed
571
static struct buffer *dequeue_buffer(struct mix *mix)
Wim Taymans's avatar
Wim Taymans committed
572
{
573
	struct buffer *b;
Wim Taymans's avatar
Wim Taymans committed
574

575
576
	if (SPA_UNLIKELY(spa_list_is_empty(&mix->queue)))
		return NULL;
Wim Taymans's avatar
Wim Taymans committed
577

578
579
	b = spa_list_first(&mix->queue, struct buffer, link);
	spa_list_remove(&b->link);
Wim Taymans's avatar
Wim Taymans committed
580
581
	SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT);

582
	return b;
Wim Taymans's avatar
Wim Taymans committed
583
584
}

585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
#if defined (__SSE__)
#include <xmmintrin.h>
static void mix2_sse(float *dst, float *src1, float *src2, int n_samples)
{
	int n, unrolled;
	__m128 in[2];

	if (SPA_IS_ALIGNED(src1, 16) &&
	    SPA_IS_ALIGNED(src2, 16) &&
	    SPA_IS_ALIGNED(dst, 16))
		unrolled = n_samples / 4;
	else
		unrolled = 0;

	for (n = 0; unrolled--; n += 4) {
		in[0] = _mm_load_ps(&src1[n]),
		in[1] = _mm_load_ps(&src2[n]),
		in[0] = _mm_add_ps(in[0], in[1]);
		_mm_store_ps(&dst[n], in[0]);
	}
	for (; n < n_samples; n++) {
		in[0] = _mm_load_ss(&src1[n]),
		in[1] = _mm_load_ss(&src2[n]),
		in[0] = _mm_add_ss(in[0], in[1]);
		_mm_store_ss(&dst[n], in[0]);
	}
}
#endif

static void mix2_c(float *dst, float *src1, float *src2, int n_samples)
{
	int i;
	for (i = 0; i < n_samples; i++)
		dst[i] = src1[i] + src2[i];
}

Wim Taymans's avatar
Wim Taymans committed
621
SPA_EXPORT
Wim Taymans's avatar
Wim Taymans committed
622
623
void jack_get_version(int *major_ptr, int *minor_ptr, int *micro_ptr, int *proto_ptr)
{
Wim Taymans's avatar
Wim Taymans committed
624
	if (major_ptr)
625
		*major_ptr = 3;
Wim Taymans's avatar
Wim Taymans committed
626
627
628
629
630
631
	if (minor_ptr)
		*minor_ptr = 0;
	if (micro_ptr)
		*micro_ptr = 0;
	if (proto_ptr)
		*proto_ptr = 0;
Wim Taymans's avatar
Wim Taymans committed
632
633
}

Wim Taymans's avatar
Wim Taymans committed
634
SPA_EXPORT
Wim Taymans's avatar
Wim Taymans committed
635
636
637
const char *
jack_get_version_string(void)
{
638
	static char name[1024];
639
	snprintf(name, sizeof(name), "3.0.0.0 (using PipeWire %s)", pw_get_library_version());
640
	return name;
Wim Taymans's avatar
Wim Taymans committed
641
642
}

643
static void on_sync_reply(void *data, uint32_t id, int seq)
Wim Taymans's avatar
Wim Taymans committed
644
645
{
	struct client *client = data;
Wim Taymans's avatar
Wim Taymans committed
646
	if (id != PW_ID_CORE)
647
648
649
		return;
	client->last_sync = seq;
	pw_thread_loop_signal(client->context.loop, false);
Wim Taymans's avatar
Wim Taymans committed
650
651
652
}


653
static void on_error(void *data, uint32_t id, int seq, int res, const char *message)
Wim Taymans's avatar
Wim Taymans committed
654
655
{
	struct client *client = data;
656
657
658
659

	pw_log_error(NAME" %p: error id:%u seq:%d res:%d (%s): %s", client,
			id, seq, res, spa_strerror(res), message);

Wim Taymans's avatar
Wim Taymans committed
660
	if (id == PW_ID_CORE) {
661
		client->error = true;
Wim Taymans's avatar
Wim Taymans committed
662
		client->last_res = res;
663
664
665
		if (client->shutdown_callback && !client->destroyed)
			client->shutdown_callback(client->shutdown_arg);
	}
Wim Taymans's avatar
Wim Taymans committed
666
667
668
	pw_thread_loop_signal(client->context.loop, false);
}

Wim Taymans's avatar
Wim Taymans committed
669
670
static const struct pw_core_events core_events = {
	PW_VERSION_CORE_EVENTS,
671
	.done = on_sync_reply,
672
	.error = on_error,
673
674
};

Wim Taymans's avatar
Wim Taymans committed
675
676
static int do_sync(struct client *client)
{
Wim Taymans's avatar
Wim Taymans committed
677
	int seq;
Wim Taymans's avatar
Wim Taymans committed
678

Wim Taymans's avatar
Wim Taymans committed
679
	seq = pw_proxy_sync((struct pw_proxy*)client->core, client->last_sync);
Wim Taymans's avatar
Wim Taymans committed
680
681

	while (true) {
Wim Taymans's avatar
Wim Taymans committed
682
	        pw_thread_loop_wait(client->context.loop);
Wim Taymans's avatar
Wim Taymans committed
683
684

		if (client->error)
Wim Taymans's avatar
Wim Taymans committed
685
			return client->last_res;
Wim Taymans's avatar
Wim Taymans committed
686
687
688
689
690
691
692

		if (client->last_sync == seq)
			break;
	}
	return 0;
}

Wim Taymans's avatar
Wim Taymans committed
693
static void on_node_removed(void *data)
Wim Taymans's avatar
Wim Taymans committed
694
695
{
	struct client *client = data;
Wim Taymans's avatar
Wim Taymans committed
696
697
	pw_proxy_destroy((struct pw_proxy*)client->node);
}
Wim Taymans's avatar
Wim Taymans committed
698

Wim Taymans's avatar
Wim Taymans committed
699
700
701
static void on_node_destroy(void *data)
{
	struct client *client = data;
702
	client->node = NULL;
Wim Taymans's avatar
Wim Taymans committed
703
704
705
	spa_hook_remove(&client->proxy_listener);
}

706
static void on_node_bound(void *data, uint32_t global_id)
707
708
709
710
711
{
	struct client *client = data;
	client->node_id = global_id;
}

Wim Taymans's avatar
Wim Taymans committed
712
static const struct pw_proxy_events node_proxy_events = {
Wim Taymans's avatar
Wim Taymans committed
713
	PW_VERSION_PROXY_EVENTS,
Wim Taymans's avatar
Wim Taymans committed
714
	.removed = on_node_removed,
715
716
	.destroy = on_node_destroy,
	.bound = on_node_bound,
Wim Taymans's avatar
Wim Taymans committed
717
718
};

719
static struct link *find_activation(struct spa_list *links, uint32_t node_id)
Wim Taymans's avatar
Wim Taymans committed
720
721
722
{
	struct link *l;

723
	spa_list_for_each(l, links, link) {
Wim Taymans's avatar
Wim Taymans committed
724
725
726
727
728
729
		if (l->node_id == node_id)
			return l;
	}
	return NULL;
}

Wim Taymans's avatar
Wim Taymans committed
730
731
732
733
734
735
736
static int
do_remove_sources(struct spa_loop *loop,
                  bool async, uint32_t seq, const void *data, size_t size, void *user_data)
{
	struct client *c = user_data;

	if (c->socket_source) {
737
		pw_loop_destroy_source(c->loop->loop, c->socket_source);
Wim Taymans's avatar
Wim Taymans committed
738
739
740
741
742
743
744
		c->socket_source = NULL;
	}
	return 0;
}

static void unhandle_socket(struct client *c)
{
Wim Taymans's avatar
Wim Taymans committed
745
746
	pw_data_loop_invoke(c->loop,
			do_remove_sources, 1, NULL, 0, true, c);
Wim Taymans's avatar
Wim Taymans committed
747
748
}

Wim Taymans's avatar
Wim Taymans committed
749
static inline void reuse_buffer(struct client *c, struct mix *mix, uint32_t id)
Wim Taymans's avatar
Wim Taymans committed
750
{
Wim Taymans's avatar
Wim Taymans committed
751
752
753
	struct buffer *b;

	b = &mix->buffers[id];
Wim Taymans's avatar
Wim Taymans committed
754

Wim Taymans's avatar
Wim Taymans committed
755
	if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) {
756
		pw_log_trace_fp(NAME" %p: port %p: recycle buffer %d", c, mix->port, id);
Wim Taymans's avatar
Wim Taymans committed
757
		spa_list_append(&mix->queue, &b->link);
Wim Taymans's avatar
Wim Taymans committed
758
		SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT);
Wim Taymans's avatar
Wim Taymans committed
759
760
761
	}
}

762

763
static void convert_from_midi(void *midi, void *buffer, size_t size)
764
765
766
{
	struct spa_pod_builder b = { 0, };
	uint32_t i, count;
Wim Taymans's avatar
Wim Taymans committed
767
	struct spa_pod_frame f;
768
769
770
771

	count = jack_midi_get_event_count(midi);

	spa_pod_builder_init(&b, buffer, size);
Wim Taymans's avatar
Wim Taymans committed
772
	spa_pod_builder_push_sequence(&b, &f, 0);
773
774
775
776

	for (i = 0; i < count; i++) {
		jack_midi_event_t ev;
		jack_midi_event_get(&ev, midi, i);
Wim Taymans's avatar
Wim Taymans committed
777
		spa_pod_builder_control(&b, ev.time, SPA_CONTROL_Midi);
778
779
		spa_pod_builder_bytes(&b, ev.buffer, ev.size);
	}
Wim Taymans's avatar
Wim Taymans committed
780
        spa_pod_builder_pop(&b, &f);
781
782
}

Wim Taymans's avatar
Wim Taymans committed
783
static void convert_to_midi(struct spa_pod_sequence **seq, uint32_t n_seq, void *midi)
784
{
Wim Taymans's avatar
Wim Taymans committed
785
786
	struct spa_pod_control *c[n_seq];
	uint32_t i;
787

788
	for (i = 0; i < n_seq; i++)
Wim Taymans's avatar
Wim Taymans committed
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
		c[i] = spa_pod_control_first(&seq[i]->body);

	while (true) {
		struct spa_pod_control *next = NULL;
		uint32_t next_index = 0;

		for (i = 0; i < n_seq; i++) {
			if (!spa_pod_control_is_inside(&seq[i]->body,
						SPA_POD_BODY_SIZE(seq[i]), c[i]))
				continue;

			if (next == NULL || c[i]->offset < next->offset) {
				next = c[i];
				next_index = i;
			}
		}
805
		if (SPA_UNLIKELY(next == NULL))
Wim Taymans's avatar
Wim Taymans committed
806
807
808
			break;

		switch(next->type) {
809
810
		case SPA_CONTROL_Midi:
			jack_midi_event_write(midi,
Wim Taymans's avatar
Wim Taymans committed
811
812
813
					next->offset,
					SPA_POD_BODY(&next->value),
					SPA_POD_BODY_SIZE(&next->value));
814
815
			break;
		}
Wim Taymans's avatar
Wim Taymans committed
816
		c[next_index] = spa_pod_control_next(c[next_index]);
817
818
819
	}
}

Wim Taymans's avatar
Wim Taymans committed
820

Wim Taymans's avatar
Wim Taymans committed
821
static inline void *get_buffer_output(struct port *p, uint32_t frames, uint32_t stride)
Wim Taymans's avatar
Wim Taymans committed
822
823
{
	struct mix *mix;
Wim Taymans's avatar
Wim Taymans committed
824
	struct client *c = p->client;
Wim Taymans's avatar
Wim Taymans committed
825
826
827
828
829
	void *ptr = NULL;

	p->io.status = -EPIPE;
	p->io.buffer_id = SPA_ID_INVALID;

830
831
832
	if (frames == 0)
		return NULL;

Wim Taymans's avatar
Wim Taymans committed
833
	if (SPA_LIKELY((mix = p->global_mix) != NULL)) {
Wim Taymans's avatar
Wim Taymans committed
834
835
		struct buffer *b;

836
		if (SPA_UNLIKELY(mix->n_buffers == 0))
837
838
			goto done;

839
		pw_log_trace_fp(NAME" %p: port %p %d get buffer %d n_buffers:%d",
Wim Taymans's avatar
Wim Taymans committed
840
841
				c, p, p->id, frames, mix->n_buffers);

842
		if (SPA_UNLIKELY((b = dequeue_buffer(mix)) == NULL)) {
Wim Taymans's avatar
Wim Taymans committed
843
844
845
846
847
848
849
			pw_log_warn("port %p: out of buffers", p);
			goto done;
		}
		reuse_buffer(c, mix, b->id);
		ptr = b->datas[0].data;

		b->datas[0].chunk->offset = 0;
850
		b->datas[0].chunk->size = frames * sizeof(float);
Wim Taymans's avatar
Wim Taymans committed
851
852
853
854
855
856
857
858
		b->datas[0].chunk->stride = stride;

		p->io.status = SPA_STATUS_HAVE_DATA;
		p->io.buffer_id = b->id;
	}
done:
	spa_list_for_each(mix, &p->mix, port_link) {
		struct spa_io_buffers *mio = mix->io;
859
		if (SPA_UNLIKELY(mio == NULL))
Wim Taymans's avatar
Wim Taymans committed
860
			continue;
861
		pw_log_trace_fp(NAME" %p: port %p tee %d.%d get buffer %d io:%p",
Wim Taymans's avatar
Wim Taymans committed
862
863
864
865
866
867
				c, p, p->id, mix->id, frames, mio);
		*mio = p->io;
	}
	return ptr;
}

868
static void process_tee(struct client *c, uint32_t frames)
869
870
871
872
{
	struct port *p;

	spa_list_for_each(p, &c->ports[SPA_DIRECTION_OUTPUT], link) {
873
874
		void *ptr;

875
		if (SPA_LIKELY(!p->empty_out))
Wim Taymans's avatar
Wim Taymans committed
876
			continue;
877
878

		switch (p->object->port.type_id) {
Wim Taymans's avatar
Wim Taymans committed
879
		case TYPE_ID_AUDIO:
Wim Taymans's avatar
Wim Taymans committed
880
			ptr = get_buffer_output(p, frames, sizeof(float));
881
			if (SPA_LIKELY(ptr != NULL))
882
883
				memcpy(ptr, p->emptyptr, frames * sizeof(float));
			break;
Wim Taymans's avatar
Wim Taymans committed
884
		case TYPE_ID_MIDI:
Wim Taymans's avatar
Wim Taymans committed
885
			ptr = get_buffer_output(p, MAX_BUFFER_FRAMES, 1);
886
			if (SPA_LIKELY(ptr != NULL))
887
888
889
				convert_from_midi(p->emptyptr, ptr, MAX_BUFFER_FRAMES * sizeof(float));
			break;
		default:
Wim Taymans's avatar
Wim Taymans committed
890
			pw_log_warn("port %p: unhandled format %d", p, p->object->port.type_id);
891
892
			break;
		}
893
894
895
	}
}

896
897
static inline void debug_position(struct client *c, jack_position_t *p)
{
Wim Taymans's avatar
Wim Taymans committed
898
	pw_log_trace("usecs:       %"PRIu64, p->usecs);
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
	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);
	}
}

Wim Taymans's avatar
Wim Taymans committed
933
static inline void jack_to_position(jack_position_t *s, struct pw_node_activation *a)
934
{
Wim Taymans's avatar
Wim Taymans committed
935
	struct spa_io_segment *d = &a->segment;
Wim Taymans's avatar
Wim Taymans committed
936

937
	if (s->valid & JackPositionBBT) {
Wim Taymans's avatar
Wim Taymans committed
938
		d->bar.flags = SPA_IO_SEGMENT_BAR_FLAG_VALID;
939
940
941
942
943
944
945
		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;
Wim Taymans's avatar
Wim Taymans committed
946
947
		d->bar.beat = (s->bar - 1) * s->beats_per_bar + (s->beat - 1) +
			(s->tick / s->ticks_per_beat);
948
949
950
	}
}

Wim Taymans's avatar
Wim Taymans committed
951
static inline jack_transport_state_t position_to_jack(struct pw_node_activation *a, jack_position_t *d)
952
{
Wim Taymans's avatar
Wim Taymans committed
953
	struct spa_io_position *s = &a->position;
Wim Taymans's avatar
Wim Taymans committed
954
955
	jack_transport_state_t state;
	struct spa_io_segment *seg = &s->segments[0];
Wim Taymans's avatar
Wim Taymans committed
956
	uint64_t running;
Wim Taymans's avatar
Wim Taymans committed
957

958
959
960
	switch (s->state) {
	default:
	case SPA_IO_POSITION_STATE_STOPPED:
Wim Taymans's avatar
Wim Taymans committed
961
		state = JackTransportStopped;
962
963
		break;
	case SPA_IO_POSITION_STATE_STARTING:
Wim Taymans's avatar
Wim Taymans committed
964
		state = JackTransportStarting;
965
966
		break;
	case SPA_IO_POSITION_STATE_RUNNING:
Wim Taymans's avatar
Wim Taymans committed
967
968
969
970
		if (seg->flags & SPA_IO_SEGMENT_FLAG_LOOPING)
			state = JackTransportLooping;
		else
			state = JackTransportRolling;
971
972
		break;
	}
973
	if (SPA_UNLIKELY(d == NULL))
Wim Taymans's avatar
Wim Taymans committed
974
975
		return state;

976
977
978
979
	d->unique_1++;
	d->usecs = s->clock.nsec / SPA_NSEC_PER_USEC;
	d->frame_rate = s->clock.rate.denom;

Wim Taymans's avatar
Wim Taymans committed
980
981
982
983
984
	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;
985
	else
Wim Taymans's avatar
Wim Taymans committed
986
		d->frame = seg->position;
987
988

	d->valid = 0;
Wim Taymans's avatar
Wim Taymans committed
989
	if (a->segment_owner[0] && SPA_FLAG_IS_SET(seg->bar.flags, SPA_IO_SEGMENT_BAR_FLAG_VALID)) {
Wim Taymans's avatar
Wim Taymans committed
990
991
		double abs_beat;
		long beats;
992
993
994

		d->valid |= JackPositionBBT;

Wim Taymans's avatar
Wim Taymans committed
995
996
		d->bbt_offset = seg->bar.offset;
		if (seg->bar.offset)
997
998
			d->valid |= JackBBTFrameOffset;

Wim Taymans's avatar
Wim Taymans committed
999
1000
		d->beats_per_bar = seg->bar.signature_num;
		d->beat_type = seg->bar.signature_denom;
1001
		d->ticks_per_beat = 1920.0f;
Wim Taymans's avatar
Wim Taymans committed
1002
		d->beats_per_minute = seg->bar.bpm;
1003

Wim Taymans's avatar
Wim Taymans committed
1004
		abs_beat = seg->bar.beat;
1005
1006

		d->bar = abs_beat / d->beats_per_bar;
Wim Taymans's avatar
Wim Taymans committed
1007
1008
1009
1010
1011
		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;
1012
		d->bar++;
Wim Taymans's avatar
Wim Taymans committed
1013
		d->beat++;
1014
1015
	}
	d->unique_2 = d->unique_1;
Wim Taymans's avatar
Wim Taymans committed
1016
	return state;
1017
1018
}

1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
static int
do_buffer_frames(struct spa_loop *loop,
		bool async, uint32_t seq, const void *data, size_t size, void *user_data)
{
	uint32_t buffer_frames = *((uint32_t*)data);
	struct client *c = user_data;
	if (c->bufsize_callback)
		c->bufsize_callback(buffer_frames, c->bufsize_arg);
	ATOMIC_DEC(c->pending);
	return 0;
}

static inline void check_buffer_frames(struct client *c, struct spa_io_position *pos, bool rt)
1032
{
1033
	uint32_t buffer_frames = pos->clock.duration;
1034
	if (SPA_UNLIKELY(buffer_frames != c->buffer_frames)) {
1035
		pw_log_info(NAME" %p: bufferframes %d", c, buffer_frames);
1036
		ATOMIC_INC(c->pending);
1037
		c->buffer_frames = buffer_frames;
1038
1039
1040
1041
1042
1043
		if (rt)
			pw_loop_invoke(c->context.l, do_buffer_frames, 0,
					&buffer_frames, sizeof(buffer_frames), false, c);
		else
			do_buffer_frames(c->context.l->loop, false, 0,
					&buffer_frames, sizeof(buffer_frames), c);
1044
1045
1046
	}
}

1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
static int
do_sample_rate(struct spa_loop *loop,
		bool async, uint32_t seq, const void *data, size_t size, void *user_data)
{
	struct client *c = user_data;
	uint32_t sample_rate = *((uint32_t*)data);
	if (c->srate_callback)
		c->srate_callback(sample_rate, c->srate_arg);
	ATOMIC_DEC(c->pending);
	return 0;
}

static inline void check_sample_rate(struct client *c, struct spa_io_position *pos, bool rt)
1060
{
1061
	uint32_t sample_rate = pos->clock.rate.denom;
1062
	if (SPA_UNLIKELY(sample_rate != c->sample_rate)) {
1063
		pw_log_info(NAME" %p: sample_rate %d", c, sample_rate);
1064
		ATOMIC_INC(c->pending);