2 * module-agl-audio -- PulseAudio module for providing audio routing support
3 * (forked from "module-murphy-ivi" - https://github.com/otcshare )
4 * Copyright (c) 2012, Intel Corporation.
5 * Copyright (c) 2016, IoT.bzh
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms and conditions of the GNU Lesser General Public License,
9 * version 2.1, as published by the Free Software Foundation.
11 * This program is distributed in the hope it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
27 agl_router *agl_router_init (struct userdata *u)
32 num_classes = agl_application_class_end;
34 router = pa_xnew0 (agl_router, 1);
35 router->rtgroups.input = pa_hashmap_new (pa_idxset_string_hash_func,
36 pa_idxset_string_compare_func);
37 router->rtgroups.output = pa_hashmap_new (pa_idxset_string_hash_func,
38 pa_idxset_string_compare_func);
39 router->maplen = num_classes;
40 router->priormap = pa_xnew0 (int, num_classes);
42 AGL_DLIST_INIT (router->nodlist);
43 AGL_DLIST_INIT (router->connlist);
48 void agl_router_done (struct userdata *u)
52 agl_connection *conn, *c;
57 if (u && (router = u->router)) {
58 AGL_DLIST_FOR_EACH_SAFE(agl_node, rtprilist, e,n, &router->nodlist)
59 AGL_DLIST_UNLINK(agl_node, rtprilist, e);
60 AGL_DLIST_FOR_EACH_SAFE(agl_connection, link, conn,c, &router->connlist) {
61 AGL_DLIST_UNLINK(agl_connection, link, conn);
65 PA_HASHMAP_FOREACH(rtg, router->rtgroups.input, state) {
66 rtgroup_destroy(u, rtg);
68 PA_HASHMAP_FOREACH(rtg, router->rtgroups.output, state) {
69 rtgroup_destroy(u, rtg);
71 pa_hashmap_free (router->rtgroups.input);
72 pa_hashmap_free (router->rtgroups.output);
74 for (i = 0; i < AGL_ZONE_MAX; i++) {
75 if ((map = router->classmap.input[i]))
77 if ((map = router->classmap.output[i]))
81 pa_xfree (router->priormap);
88 bool agl_router_default_accept (struct userdata *u, agl_rtgroup *rtg, agl_node *node)
94 bool agl_router_phone_accept (struct userdata *u, agl_rtgroup *rtg, agl_node *node)
100 int agl_router_default_effect (struct userdata *u, agl_rtgroup *rtg, agl_node *node, bool new)
106 int agl_router_phone_effect (struct userdata *u, agl_rtgroup *rtg, agl_node *node, bool new)
112 agl_utils_volume_ramp (u, node->nullsink, false);
114 agl_utils_volume_ramp (u, node->nullsink, true);
119 agl_rtgroup *agl_router_create_rtgroup (struct userdata *u, agl_direction type, const char *name, const char *node_desc, agl_rtgroup_accept_t accept, agl_rtgroup_effect_t effect)
123 agl_nodeset *nodeset;
128 pa_assert (type == agl_input || type == agl_output);
130 pa_assert_se (router = u->router);
131 pa_assert_se (nodeset = u->nodeset);
133 if (type == agl_input)
134 table = router->rtgroups.input;
136 table = router->rtgroups.output;
139 rtg = pa_xnew0 (agl_rtgroup, 1);
140 rtg->name = pa_xstrdup (name);
141 rtg->accept = accept;
142 rtg->effect = effect;
143 AGL_DLIST_INIT(rtg->entries);
144 /* associate an agl_output node for an agl_input routing group */
145 if (type == agl_input) {
146 node = agl_node_create (u, NULL);
147 node->direction = agl_output;
148 node->implement = agl_device;
149 node->visible = true;
150 node->available = true;
151 node->paname = pa_xstrdup (node_desc);
152 /* add to global nodeset */
153 pa_idxset_put (nodeset->nodes, node, &node->index);
160 pa_hashmap_put (table, rtg->name, rtg);
162 pa_log_debug ("routing group '%s' created", name);
167 void agl_router_destroy_rtgroup (struct userdata *u, agl_direction type, const char *name)
175 pa_assert_se (router = u->router);
177 if (type == agl_input)
178 table = router->rtgroups.input;
180 table = router->rtgroups.output;
183 rtg = pa_hashmap_remove (table, name);
185 pa_log_debug ("can't destroy routing group '%s': not found", name);
187 //rtgroup_destroy (u, rtg);
188 pa_log_debug ("routing group '%s' destroyed", name);
192 bool agl_router_assign_class_to_rtgroup (struct userdata *u, agl_node_type class, uint32_t zone, agl_direction type, const char *name)
196 agl_rtgroup ***classmap;
197 agl_rtgroup **zonemap;
198 const char *classname;
199 const char *direction;
204 pa_assert (zone < AGL_ZONE_MAX);
205 pa_assert (type == agl_input || type == agl_output);
207 pa_assert_se (router = u->router);
209 if (type == agl_input) {
210 rtable = router->rtgroups.input;
211 classmap = router->classmap.input;
213 rtable = router->rtgroups.output;
214 classmap = router->classmap.output;
217 if (class < 0 || class >= router->maplen) {
218 pa_log_debug ("Cannot assign class to routing group '%s': "
219 "id %d out of range (0 - %d)",
220 name, class, router->maplen);
224 classname = agl_node_type_str (class); /* "Player", "Radio"... */
225 direction = agl_node_direction_str (type); /* "input", "output" */
227 rtg = pa_hashmap_get (rtable, name);
229 pa_log_debug ("Cannot assign class to routing group '%s': "
230 "router group not found", name);
234 zonemap = classmap[zone];
236 zonemap = pa_xnew0 (agl_rtgroup *, router->maplen);
237 classmap[zone] = zonemap;
240 zonemap[class] = rtg;
242 /* try to get zone name for logging, if fails, only print id number */
243 rzone = agl_zoneset_get_zone_by_index (u, zone);
245 pa_log_debug ("class '%s'@'%s' assigned to routing group '%s'",
246 classname, rzone->name, name);
248 pa_log_debug ("class '%s'@zone%d assigned to routing group '%s'",
249 classname, zone, name);
255 agl_rtgroup * agl_router_get_rtgroup_from_class (struct userdata *u, agl_node_type class, uint32_t zone, agl_direction type)
259 agl_rtgroup ***classmap;
260 agl_rtgroup **zonemap;
264 pa_assert_se (router = u->router);
265 pa_assert (class >= 0 && class < router->maplen);
266 pa_assert (zone < AGL_ZONE_MAX);
267 pa_assert (type == agl_input || type == agl_output);
269 if (type == agl_input) {
270 rtable = router->rtgroups.input;
271 classmap = router->classmap.input;
273 rtable = router->rtgroups.output;
274 classmap = router->classmap.output;
277 zonemap = classmap[zone];
278 rtg = zonemap[class];
283 void agl_router_assign_class_priority (struct userdata *u, agl_node_type class, int priority)
289 pa_assert_se (router = u->router);
290 pa_assert_se (priormap = router->priormap);
292 if (class > 0 && class < router->maplen) {
293 pa_log_debug ("assigning priority %d to class '%s'",
294 priority, agl_node_type_str (class));
295 priormap[class] = priority;
299 int agl_router_get_node_priority (struct userdata *u, agl_node *node)
306 pa_assert_se (router = u->router);
310 if (class < 0 || class >= (int)router->maplen)
313 return router->priormap[class];
316 bool agl_router_apply_node_priority_effect (struct userdata *u, agl_node *node, bool new)
320 agl_nodeset *nodeset;
328 pa_assert_se (router = u->router);
329 pa_assert_se (nodeset = u->nodeset);
331 rtg = agl_router_get_rtgroup_from_class(u, node->type, 0, node->direction);
333 /* now let us compare priorities, and apply effect if needed */
336 priority = agl_router_get_node_priority (u, node);
337 PA_IDXSET_FOREACH(n, nodeset->nodes, index) {
338 if (n->nullsink && (priority > agl_router_get_node_priority (u, n))) {
339 sink = agl_utils_get_null_sink (u, n->nullsink);
341 /* do we have a custom effect ? otherwise, just mute it */
342 if (rtg && rtg->effect)
343 rtg->effect (u, rtg, n, new);
345 pa_sink_set_mute (sink, new, false);
351 if (!agl_node_has_highest_priority (u, node))
353 PA_IDXSET_FOREACH(n, nodeset->nodes, index) {
355 sink = agl_utils_get_null_sink (u, n->nullsink);
357 /* do we have a custom effect ? otherwise, just unmute it */
358 if (rtg && rtg->effect)
359 rtg->effect (u, rtg, n, new);
361 pa_sink_set_mute (sink, new, false);
370 void agl_router_register_node (struct userdata *u, agl_node *node)
377 pa_assert_se (router = u->router);
379 /* we try to discover node routing group from the configuration, "Phone" for instance,
380 * see defaults in "config.c. Otherwise we just say NULL, a.k.a. default */
381 rtg = agl_router_get_rtgroup_from_class(u, node->type, 0, node->direction);
383 if (node->direction == agl_input) {
385 implement_default_route (u, node, rtg->node, agl_utils_new_stamp ());
387 implement_default_route (u, node, NULL, agl_utils_new_stamp ());
390 implement_default_route (u, rtg->node, node, agl_utils_new_stamp ());
392 implement_default_route (u, NULL, node, agl_utils_new_stamp ());
396 void agl_router_unregister_node (struct userdata *u, agl_node *node)
401 remove_routes (u, node, NULL, agl_utils_new_stamp ());
404 agl_node *agl_router_make_prerouting (struct userdata *u, agl_node *data)
408 static bool done_prerouting;
410 agl_node *start, *end;
414 pa_assert_se (router = u->router);
415 pa_assert_se (data->implement == agl_stream);
417 //priority = node_priority (u, data);
419 done_prerouting = false;
421 stamp = agl_utils_new_stamp ();
423 //make_explicit_routes (u, stamp);
425 //pa_audiomgr_delete_default_routes(u);
427 AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) {
428 //if ((start->implement == agl_device) &&
429 // (!start->loop)) /* only manage looped real devices */
432 /*if (priority >= node_priority (u, start)) {
433 target = find_default_route (u, data, stamp);
435 implement_preroute (u, data, target, stamp);
437 done_prerouting = true;
440 if (start->stamp >= stamp)
443 //end = find_default_route (u, start, stamp);
445 // implement_default_route(u, start, end, stamp);
448 if (!done_prerouting) {
449 pa_log_debug ("Prerouting failed, trying to find default route as last resort");
451 //target = find_default_route (u, data, stamp);
453 // implement_preroute (u, data, target, stamp);
459 void agl_router_make_routing (struct userdata *u)
462 static bool ongoing_routing; /* true while we are actively routing */
464 agl_node *start, *end;
467 pa_assert_se (router = u->router);
469 if (ongoing_routing) /* already routing, canceling */
471 ongoing_routing = true;
472 stamp = agl_utils_new_stamp ();
474 pa_log_debug("stamp for routing: %d", stamp);
476 // make_explicit_routes (u, stamp);
478 // pa_audiomgr_delete_default_routes (u);
480 AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) {
481 //if ((start->implement == agl_device) &&
482 // (!start->loop)) /* only manage looped real devices */
485 if (start->stamp >= stamp)
488 end = find_default_route (u, start, stamp);
490 implement_default_route (u, start, end, stamp);
493 // pa_audiomgr_send_default_routes (u);
495 ongoing_routing = false;
498 void implement_default_route (struct userdata *u,
499 agl_node *start, agl_node *end,
502 if (start->direction == agl_input)
503 agl_switch_setup_link (u, start, end);
505 agl_switch_setup_link (u, end, start);
508 agl_node *find_default_route (struct userdata *u, agl_node *start, uint32_t stamp)
515 void remove_routes (struct userdata *u, agl_node *start, agl_node *end, uint32_t stamp)
517 if (start->direction == agl_input) {
518 agl_switch_teardown_link (u, start, end);
520 agl_switch_teardown_link (u, end, start);