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 void agl_router_assign_class_priority (struct userdata *u, agl_node_type class, int priority)
261 pa_assert_se (router = u->router);
262 pa_assert_se (priormap = router->priormap);
264 if (class > 0 && class < router->maplen) {
265 pa_log_debug ("assigning priority %d to class '%s'",
266 priority, agl_node_type_str (class));
267 priormap[class] = priority;
271 int agl_router_get_node_priority (struct userdata *u, agl_node *node)
278 pa_assert_se (router = u->router);
282 if (class < 0 || class >= (int)router->maplen)
285 return router->priormap[class];
288 bool agl_router_apply_node_priority_effect (struct userdata *u, agl_node *node, bool new)
292 agl_nodeset *nodeset;
300 pa_assert_se (router = u->router);
301 pa_assert_se (nodeset = u->nodeset);
303 /* do we have a routing group associated with this node ? It may have a custom effect */
304 if (node->direction == agl_input)
305 rtg = pa_hashmap_get (router->rtgroups.input, agl_node_type_str (node->type));
307 rtg = pa_hashmap_get (router->rtgroups.output, agl_node_type_str (node->type));
309 /* now let us compare priorities, and apply effect if needed */
312 priority = agl_router_get_node_priority (u, node);
313 PA_IDXSET_FOREACH(n, nodeset->nodes, index) {
314 if (n->nullsink && (priority > agl_router_get_node_priority (u, n))) {
315 sink = agl_utils_get_null_sink (u, n->nullsink);
317 /* do we have a custom effect ? otherwise, just mute it */
318 if (rtg && rtg->effect)
319 rtg->effect (u, rtg, n, new);
321 pa_sink_set_mute (sink, new, false);
327 if (!agl_node_has_highest_priority (u, node))
329 PA_IDXSET_FOREACH(n, nodeset->nodes, index) {
331 sink = agl_utils_get_null_sink (u, n->nullsink);
333 /* do we have a custom effect ? otherwise, just unmute it */
334 if (rtg && rtg->effect)
335 rtg->effect (u, rtg, n, new);
337 pa_sink_set_mute (sink, new, false);
346 void agl_router_register_node (struct userdata *u, agl_node *node)
353 pa_assert_se (router = u->router);
355 /* we try to discover node routing group from the configuration, "Phone" for instance,
356 * see defaults in "config.c. Otherwise we just say NULL, a.k.a. default */
357 if (node->direction == agl_input) {
358 rtg = pa_hashmap_get (router->rtgroups.input, agl_node_type_str (node->type));
360 implement_default_route (u, node, rtg->node, agl_utils_new_stamp ());
362 implement_default_route (u, node, NULL, agl_utils_new_stamp ());
364 rtg = pa_hashmap_get (router->rtgroups.output, agl_node_type_str (node->type));
366 implement_default_route (u, rtg->node, node, agl_utils_new_stamp ());
368 implement_default_route (u, NULL, node, agl_utils_new_stamp ());
372 void agl_router_unregister_node (struct userdata *u, agl_node *node)
377 remove_routes (u, node, NULL, agl_utils_new_stamp ());
380 agl_node *agl_router_make_prerouting (struct userdata *u, agl_node *data)
384 static bool done_prerouting;
386 agl_node *start, *end;
390 pa_assert_se (router = u->router);
391 pa_assert_se (data->implement == agl_stream);
393 //priority = node_priority (u, data);
395 done_prerouting = false;
397 stamp = agl_utils_new_stamp ();
399 //make_explicit_routes (u, stamp);
401 //pa_audiomgr_delete_default_routes(u);
403 AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) {
404 //if ((start->implement == agl_device) &&
405 // (!start->loop)) /* only manage looped real devices */
408 /*if (priority >= node_priority (u, start)) {
409 target = find_default_route (u, data, stamp);
411 implement_preroute (u, data, target, stamp);
413 done_prerouting = true;
416 if (start->stamp >= stamp)
419 //end = find_default_route (u, start, stamp);
421 // implement_default_route(u, start, end, stamp);
424 if (!done_prerouting) {
425 pa_log_debug ("Prerouting failed, trying to find default route as last resort");
427 //target = find_default_route (u, data, stamp);
429 // implement_preroute (u, data, target, stamp);
435 void agl_router_make_routing (struct userdata *u)
438 static bool ongoing_routing; /* true while we are actively routing */
440 agl_node *start, *end;
443 pa_assert_se (router = u->router);
445 if (ongoing_routing) /* already routing, canceling */
447 ongoing_routing = true;
448 stamp = agl_utils_new_stamp ();
450 pa_log_debug("stamp for routing: %d", stamp);
452 // make_explicit_routes (u, stamp);
454 // pa_audiomgr_delete_default_routes (u);
456 AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) {
457 //if ((start->implement == agl_device) &&
458 // (!start->loop)) /* only manage looped real devices */
461 if (start->stamp >= stamp)
464 end = find_default_route (u, start, stamp);
466 implement_default_route (u, start, end, stamp);
469 // pa_audiomgr_send_default_routes (u);
471 ongoing_routing = false;
474 void implement_default_route (struct userdata *u,
475 agl_node *start, agl_node *end,
478 if (start->direction == agl_input)
479 agl_switch_setup_link (u, start, end, false);
481 agl_switch_setup_link (u, end, start, false);
484 agl_node *find_default_route (struct userdata *u, agl_node *start, uint32_t stamp)
491 void remove_routes (struct userdata *u, agl_node *start, agl_node *end, uint32_t stamp)
493 if (start->direction == agl_input) {
494 agl_switch_teardown_link (u, start, end);
496 agl_switch_teardown_link (u, end, start);