1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains implementation of the graph virtualizer object
9 **************************************************************************
11 * LADI Session Handler is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * LADI Session Handler is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
23 * or write to the Free Software Foundation, Inc.,
24 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "virtualizer.h"
28 #include "../dbus_constants.h"
29 #include "a2j_proxy.h"
31 #include "app_supervisor.h"
32 #include "studio_internal.h"
33 #include "../catdup.h"
38 graph_proxy_handle jack_graph_proxy
;
39 ladish_graph_handle jack_graph
;
40 ladish_graph_handle studio_graph
;
41 uint64_t system_client_id
;
42 unsigned int our_clients_count
;
45 /* 47c1cd18-7b21-4389-bec4-6e0658e1d6b1 */
46 UUID_DEFINE(g_system_capture_uuid
,0x47,0xC1,0xCD,0x18,0x7B,0x21,0x43,0x89,0xBE,0xC4,0x6E,0x06,0x58,0xE1,0xD6,0xB1);
48 /* b2a0bb06-28d8-4bfe-956e-eb24378f9629 */
49 UUID_DEFINE(g_system_playback_uuid
,0xB2,0xA0,0xBB,0x06,0x28,0xD8,0x4B,0xFE,0x95,0x6E,0xEB,0x24,0x37,0x8F,0x96,0x29);
51 /* be23a242-e2b2-11de-b795-002618af5e42 */
52 UUID_DEFINE(g_a2j_uuid
,0xBE,0x23,0xA2,0x42,0xE2,0xB2,0x11,0xDE,0xB7,0x95,0x00,0x26,0x18,0xAF,0x5E,0x42);
54 char * get_app_name(struct virtualizer
* virtualizer_ptr
, uint64_t client_id
, pid_t
* app_pid_ptr
)
57 unsigned long long ppid
;
60 if (!graph_proxy_get_client_pid(virtualizer_ptr
->jack_graph_proxy
, client_id
, &pid
))
65 log_info("client pid is %"PRId64
, pid
);
69 ppid
= (unsigned long long)pid
;
71 app_name
= ladish_app_supervisor_search_app(g_studio
.app_supervisor
, ppid
);
74 ppid
= procfs_get_process_parent(ppid
);
77 //log_info("parent pid %llu", ppid);
83 *app_pid_ptr
= (pid_t
)ppid
;
88 #define virtualizer_ptr ((struct virtualizer *)context)
90 static void clear(void * context
)
95 static void client_appeared(void * context
, uint64_t id
, const char * jack_name
)
97 ladish_client_handle client
;
98 const char * a2j_name
;
104 log_info("client_appeared(%"PRIu64
", %s)", id
, jack_name
);
106 a2j_name
= a2j_proxy_get_jack_client_name_cached();
107 is_a2j
= a2j_name
!= NULL
&& strcmp(a2j_name
, jack_name
) == 0;
109 app_name
= get_app_name(virtualizer_ptr
, id
, &pid
);
110 if (app_name
!= NULL
)
112 log_info("app name is '%s'", app_name
);
122 client
= ladish_graph_find_client_by_uuid(virtualizer_ptr
->jack_graph
, g_a2j_uuid
);
126 client
= ladish_graph_find_client_by_name(virtualizer_ptr
->jack_graph
, name
);
131 log_info("found existing client");
132 ASSERT(ladish_client_get_jack_id(client
) == 0); /* two JACK clients with same name? */
133 ladish_client_set_jack_id(client
, id
);
134 ladish_graph_show_client(virtualizer_ptr
->jack_graph
, client
);
138 if (!ladish_client_create(is_a2j
? g_a2j_uuid
: NULL
, true, false, false, &client
))
140 log_error("ladish_client_create() failed. Ignoring client %"PRIu64
" (%s)", id
, jack_name
);
144 ladish_client_set_jack_id(client
, id
);
146 if (!ladish_graph_add_client(virtualizer_ptr
->jack_graph
, client
, name
, false))
148 log_error("ladish_graph_add_client() failed to add client %"PRIu64
" (%s) to JACK graph", id
, name
);
149 ladish_client_destroy(client
);
154 if (strcmp(jack_name
, "system") == 0)
156 virtualizer_ptr
->system_client_id
= id
;
159 if (app_name
!= NULL
)
161 ladish_client_set_pid(client
, pid
);
162 virtualizer_ptr
->our_clients_count
++;
167 static void client_disappeared(void * context
, uint64_t id
)
169 ladish_client_handle client
;
172 log_info("client_disappeared(%"PRIu64
")", id
);
174 client
= ladish_graph_find_client_by_jack_id(virtualizer_ptr
->jack_graph
, id
);
177 log_error("Unknown JACK client with id %"PRIu64
" disappeared", id
);
181 log_info("client disappeared: '%s'", ladish_graph_get_client_name(virtualizer_ptr
->jack_graph
, client
));
183 pid
= ladish_client_get_pid(client
);
186 virtualizer_ptr
->our_clients_count
--;
189 if (id
== virtualizer_ptr
->system_client_id
)
191 virtualizer_ptr
->system_client_id
= 0;
194 if (true) /* if client is supposed to be persisted */
196 ladish_client_set_jack_id(client
, 0);
197 ladish_graph_hide_client(virtualizer_ptr
->jack_graph
, client
);
201 ladish_graph_remove_client(virtualizer_ptr
->jack_graph
, client
, false);
202 ladish_client_destroy(client
);
212 const char * real_jack_port_name
,
217 ladish_client_handle jack_client
;
218 ladish_client_handle studio_client
;
219 ladish_port_handle port
;
222 const char * jack_client_name
;
225 char * alsa_client_name
;
226 char * alsa_port_name
;
227 char * a2j_fake_jack_port_name
= NULL
;
228 uint32_t alsa_client_id
;
229 const char * jack_port_name
;
230 const char * studio_port_name
;
232 log_info("port_appeared(%"PRIu64
", %"PRIu64
", %s (%s, %s))", client_id
, port_id
, real_jack_port_name
, is_input
? "in" : "out", is_midi
? "midi" : "audio");
234 /********************/
235 /* gather info about the appeared port */
237 jack_client
= ladish_graph_find_client_by_jack_id(virtualizer_ptr
->jack_graph
, client_id
);
238 if (jack_client
== NULL
)
240 log_error("Port of unknown JACK client with id %"PRIu64
" appeared", client_id
);
244 ladish_client_get_uuid(jack_client
, client_uuid
);
245 is_a2j
= uuid_compare(client_uuid
, g_a2j_uuid
) == 0;
248 log_info("a2j port appeared");
249 if (!a2j_proxy_map_jack_port(real_jack_port_name
, &alsa_client_name
, &alsa_port_name
, &alsa_client_id
))
255 log_info("a2j: '%s':'%s' (%"PRIu32
")", alsa_client_name
, alsa_port_name
, alsa_client_id
);
258 a2j_fake_jack_port_name
= catdup4(alsa_client_name
, is_input
? " (playback)" : " (capture)", ": ", alsa_port_name
);
259 if (a2j_fake_jack_port_name
== NULL
)
261 log_error("catdup4() failed");
262 goto free_alsa_names
;
265 jack_port_name
= a2j_fake_jack_port_name
;
269 jack_port_name
= real_jack_port_name
;
272 jack_client_name
= ladish_graph_get_client_name(virtualizer_ptr
->jack_graph
, jack_client
);
274 type
= is_midi
? JACKDBUS_PORT_TYPE_MIDI
: JACKDBUS_PORT_TYPE_AUDIO
;
275 flags
= is_input
? JACKDBUS_PORT_FLAG_INPUT
: JACKDBUS_PORT_FLAG_OUTPUT
;
278 flags
|= JACKDBUS_PORT_FLAG_TERMINAL
;
281 /********************/
283 /* search (by name) the appeared port in jack graph
284 * if found - show it in both graphs.
285 * if not found - create new port and add it to the jack graph.
286 * The process to adding it to studio graph */
288 port
= ladish_graph_find_port_by_name(virtualizer_ptr
->jack_graph
, jack_client
, jack_port_name
);
291 log_info("found existing port");
293 if (ladish_port_get_jack_id(port
) != 0)
295 log_error("Ignoring duplicate JACK port '%s':'%s'", jack_client_name
, jack_port_name
);
296 goto free_alsa_names
;
299 ladish_port_set_jack_id(port
, port_id
);
300 ladish_graph_adjust_port(virtualizer_ptr
->jack_graph
, port
, type
, flags
);
301 ladish_graph_show_port(virtualizer_ptr
->jack_graph
, port
);
303 studio_client
= ladish_graph_get_port_client(virtualizer_ptr
->studio_graph
, port
);
304 if (studio_client
== NULL
)
306 log_error("JACK port not found in studio graph");
308 goto free_alsa_names
;
311 ladish_client_set_jack_id(studio_client
, client_id
);
312 ladish_graph_adjust_port(virtualizer_ptr
->studio_graph
, port
, type
, flags
);
313 ladish_graph_show_port(virtualizer_ptr
->studio_graph
, port
);
314 goto free_alsa_names
;
317 if (!ladish_port_create(NULL
, &port
))
319 log_error("ladish_port_create() failed.");
320 goto free_alsa_names
;
323 /* set port jack id so invisible connections to/from it can be restored */
324 ladish_port_set_jack_id(port
, port_id
);
326 if (!ladish_graph_add_port(virtualizer_ptr
->jack_graph
, jack_client
, port
, jack_port_name
, type
, flags
, false))
328 log_error("ladish_graph_add_port() failed.");
329 ladish_port_destroy(port
);
330 goto free_alsa_names
;
333 /********************/
334 /* find/create the studio client where port will be added */
338 studio_client
= ladish_graph_find_client_by_name(virtualizer_ptr
->studio_graph
, alsa_client_name
);
339 if (studio_client
== NULL
)
341 if (!ladish_client_create(NULL
, true, false, true, &studio_client
))
343 log_error("ladish_client_create() failed.");
344 goto free_alsa_names
;
347 if (!ladish_graph_add_client(virtualizer_ptr
->studio_graph
, studio_client
, alsa_client_name
, false))
349 log_error("ladish_graph_add_client() failed.");
350 ladish_client_destroy(studio_client
);
351 goto free_alsa_names
;
356 else if (client_id
== virtualizer_ptr
->system_client_id
)
358 log_info("system client port appeared");
361 { /* output capture port */
363 studio_client
= ladish_graph_find_client_by_uuid(virtualizer_ptr
->studio_graph
, g_system_capture_uuid
);
364 if (studio_client
== NULL
)
366 if (!ladish_client_create(g_system_capture_uuid
, true, false, true, &studio_client
))
368 log_error("ladish_client_create() failed.");
369 goto free_alsa_names
;
372 if (!ladish_graph_add_client(virtualizer_ptr
->studio_graph
, studio_client
, "Hardware Capture", false))
374 log_error("ladish_graph_add_client() failed.");
375 ladish_client_destroy(studio_client
);
376 goto free_alsa_names
;
381 { /* input playback port */
382 studio_client
= ladish_graph_find_client_by_uuid(virtualizer_ptr
->studio_graph
, g_system_playback_uuid
);
383 if (studio_client
== NULL
)
385 if (!ladish_client_create(g_system_playback_uuid
, true, false, true, &studio_client
))
387 log_error("ladish_client_create() failed.");
388 goto free_alsa_names
;
391 if (!ladish_graph_add_client(virtualizer_ptr
->studio_graph
, studio_client
, "Hardware Playback", false))
393 ladish_client_destroy(studio_client
);
394 goto free_alsa_names
;
400 { /* non-system client */
401 log_info("non-system client port appeared");
403 studio_client
= ladish_graph_find_client_by_jack_id(virtualizer_ptr
->studio_graph
, client_id
);
404 if (studio_client
== NULL
)
406 if (!ladish_client_create(NULL
, false, false, false, &studio_client
))
408 log_error("ladish_client_create() failed.");
409 goto free_alsa_names
;
412 ladish_client_set_jack_id(studio_client
, client_id
);
414 if (!ladish_graph_add_client(virtualizer_ptr
->studio_graph
, studio_client
, jack_client_name
, false))
416 log_error("ladish_graph_add_client() failed to add client '%s' to studio graph", jack_client_name
);
417 ladish_client_destroy(studio_client
);
418 goto free_alsa_names
;
423 /********************/
424 /* add newly appeared port to the studio graph */
428 studio_port_name
= alsa_port_name
;
432 studio_port_name
= jack_port_name
;
435 if (!ladish_graph_add_port(virtualizer_ptr
->studio_graph
, studio_client
, port
, studio_port_name
, type
, flags
, false))
437 log_error("ladish_graph_add_port() failed.");
438 goto free_alsa_names
;
442 if (a2j_fake_jack_port_name
!= NULL
)
444 free(a2j_fake_jack_port_name
);
449 free(alsa_client_name
);
450 free(alsa_port_name
);
457 static void port_disappeared(void * context
, uint64_t client_id
, uint64_t port_id
)
459 ladish_client_handle client
;
460 ladish_port_handle port
;
462 log_info("port_disappeared(%"PRIu64
")", port_id
);
464 client
= ladish_graph_find_client_by_jack_id(virtualizer_ptr
->jack_graph
, client_id
);
467 log_error("Port of unknown JACK client with id %"PRIu64
" disappeared", client_id
);
471 port
= ladish_graph_find_port_by_jack_id(virtualizer_ptr
->jack_graph
, port_id
);
474 log_error("Unknown JACK port with id %"PRIu64
" disappeared", port_id
);
478 if (true) /* if client is supposed to be persisted */
480 ladish_port_set_jack_id(port
, 0);
481 ladish_graph_hide_port(virtualizer_ptr
->jack_graph
, port
);
482 ladish_graph_hide_port(virtualizer_ptr
->studio_graph
, port
);
483 client
= ladish_graph_get_port_client(virtualizer_ptr
->studio_graph
, port
);
484 if (ladish_graph_is_client_looks_empty(virtualizer_ptr
->studio_graph
, client
))
486 ladish_graph_hide_client(virtualizer_ptr
->studio_graph
, client
);
491 ladish_graph_remove_port(virtualizer_ptr
->jack_graph
, port
);
493 client
= ladish_graph_remove_port(virtualizer_ptr
->studio_graph
, port
);
496 if (ladish_graph_is_client_empty(virtualizer_ptr
->studio_graph
, client
))
498 ladish_graph_remove_client(virtualizer_ptr
->studio_graph
, client
, false);
504 static bool ports_connect_request(void * context
, ladish_graph_handle graph_handle
, ladish_port_handle port1
, ladish_port_handle port2
)
509 ASSERT(graph_handle
== virtualizer_ptr
->studio_graph
);
510 log_info("virtualizer: ports connect request");
512 port1_id
= ladish_port_get_jack_id(port1
);
513 port2_id
= ladish_port_get_jack_id(port2
);
515 graph_proxy_connect_ports(virtualizer_ptr
->jack_graph_proxy
, port1_id
, port2_id
);
520 static bool ports_disconnect_request(void * context
, ladish_graph_handle graph_handle
, uint64_t connection_id
)
522 ladish_port_handle port1
;
523 ladish_port_handle port2
;
527 log_info("virtualizer: ports disconnect request");
529 ASSERT(graph_handle
== virtualizer_ptr
->studio_graph
);
531 if (!ladish_graph_get_connection_ports(virtualizer_ptr
->studio_graph
, connection_id
, &port1
, &port2
))
533 log_error("cannot find ports that are disconnect-requested");
538 port1_id
= ladish_port_get_jack_id(port1
);
539 port2_id
= ladish_port_get_jack_id(port2
);
541 graph_proxy_disconnect_ports(virtualizer_ptr
->jack_graph_proxy
, port1_id
, port2_id
);
546 static void ports_connected(void * context
, uint64_t client1_id
, uint64_t port1_id
, uint64_t client2_id
, uint64_t port2_id
)
548 ladish_port_handle port1
;
549 ladish_port_handle port2
;
550 uint64_t connection_id
;
552 log_info("ports_connected %"PRIu64
":%"PRIu64
" %"PRIu64
":%"PRIu64
"", client1_id
, port1_id
, client2_id
, port2_id
);
554 port1
= ladish_graph_find_port_by_jack_id(virtualizer_ptr
->jack_graph
, port1_id
);
557 log_error("Unknown JACK port with id %"PRIu64
" connected", port1_id
);
561 port2
= ladish_graph_find_port_by_jack_id(virtualizer_ptr
->jack_graph
, port2_id
);
564 log_error("Unknown JACK port with id %"PRIu64
" connected", port2_id
);
568 ladish_graph_add_connection(virtualizer_ptr
->jack_graph
, port1
, port2
, false);
570 if (ladish_graph_find_connection(virtualizer_ptr
->studio_graph
, port1
, port2
, &connection_id
))
572 log_info("showing hidden studio connection");
573 ladish_graph_show_connection(virtualizer_ptr
->studio_graph
, connection_id
);
577 log_info("creating new studio connection");
578 ladish_graph_add_connection(virtualizer_ptr
->studio_graph
, port1
, port2
, false);
582 static void ports_disconnected(void * context
, uint64_t client1_id
, uint64_t port1_id
, uint64_t client2_id
, uint64_t port2_id
)
584 ladish_port_handle port1
;
585 ladish_port_handle port2
;
586 uint64_t connection_id
;
588 log_info("ports_disconnected %"PRIu64
":%"PRIu64
" %"PRIu64
":%"PRIu64
"", client1_id
, port1_id
, client2_id
, port2_id
);
590 port1
= ladish_graph_find_port_by_jack_id(virtualizer_ptr
->jack_graph
, port1_id
);
593 log_error("Unknown JACK port with id %"PRIu64
" disconnected", port1_id
);
597 port2
= ladish_graph_find_port_by_jack_id(virtualizer_ptr
->jack_graph
, port2_id
);
600 log_error("Unknown JACK port with id %"PRIu64
" disconnected", port2_id
);
604 if (ladish_graph_find_connection(virtualizer_ptr
->jack_graph
, port1
, port2
, &connection_id
))
606 ladish_graph_remove_connection(virtualizer_ptr
->jack_graph
, connection_id
, true);
610 log_error("ports %"PRIu64
":%"PRIu64
" and %"PRIu64
":%"PRIu64
" are not connected in the JACK graph", client1_id
, port1_id
, client2_id
, port2_id
);
613 if (ladish_graph_find_connection(virtualizer_ptr
->studio_graph
, port1
, port2
, &connection_id
))
615 ladish_graph_remove_connection(virtualizer_ptr
->studio_graph
, connection_id
, false);
619 log_error("ports %"PRIu64
":%"PRIu64
" and %"PRIu64
":%"PRIu64
" are not connected in the Studio graph", client1_id
, port1_id
, client2_id
, port2_id
);
623 #undef virtualizer_ptr
626 ladish_virtualizer_create(
627 graph_proxy_handle jack_graph_proxy
,
628 ladish_graph_handle jack_graph
,
629 ladish_graph_handle studio_graph
,
630 ladish_virtualizer_handle
* handle_ptr
)
632 struct virtualizer
* virtualizer_ptr
;
634 virtualizer_ptr
= malloc(sizeof(struct virtualizer
));
635 if (virtualizer_ptr
== NULL
)
637 log_error("malloc() failed for struct virtualizer");
641 virtualizer_ptr
->jack_graph_proxy
= jack_graph_proxy
;
642 virtualizer_ptr
->jack_graph
= jack_graph
;
643 virtualizer_ptr
->studio_graph
= studio_graph
;
644 virtualizer_ptr
->system_client_id
= 0;
645 virtualizer_ptr
->our_clients_count
= 0;
647 if (!graph_proxy_attach(
658 free(virtualizer_ptr
);
662 ladish_graph_set_connection_handlers(studio_graph
, virtualizer_ptr
, ports_connect_request
, ports_disconnect_request
);
664 *handle_ptr
= (ladish_virtualizer_handle
)virtualizer_ptr
;
668 #define virtualizer_ptr ((struct virtualizer *)handle)
671 ladish_virtualizer_get_our_clients_count(
672 ladish_virtualizer_handle handle
)
674 return virtualizer_ptr
->our_clients_count
;
678 ladish_virtualizer_destroy(
679 ladish_virtualizer_handle handle
)
681 log_info("ladish_virtualizer_destroy() called");
683 graph_proxy_detach((graph_proxy_handle
)handle
, virtualizer_ptr
);
684 free(virtualizer_ptr
);
687 #undef virtualizer_ptr