Implement routing groups and volume ramp up/down
authorManuel Bachmann <manuel.bachmann@iot.bzh>
Thu, 7 Jul 2016 15:29:08 +0000 (17:29 +0200)
committerYannick Gicquel <yannick.gicquel@iot.bzh>
Tue, 11 Oct 2016 15:09:07 +0000 (17:09 +0200)
Change-Id: I0e9d3b8b8be4d124907214c165617d86be6906fc
Signed-off-by: Manuel Bachmann <manuel.bachmann@iot.bzh>
config.c
config.h
discover.c
node.c
node.h
router.c
router.h
switch.c
utils.c
utils.h

index b978296..bbd8110 100644 (file)
--- a/config.c
+++ b/config.c
@@ -74,13 +74,12 @@ static zone_def zones[] = {
        { NULL }
 };
 
-
 static rtgroup_def rtgroups[] = {
        { agl_input,
          "Phone",
          "PhoneCard",
          agl_router_phone_accept,
-         agl_router_phone_compare
+         agl_router_phone_effect
        },
 
        { 0, NULL, NULL, NULL, NULL }
@@ -128,7 +127,7 @@ bool use_default_configuration (struct userdata *u)
 
        for (r = rtgroups; r->name; r++)
                agl_router_create_rtgroup (u, r->type, r->name, r->node_desc,
-                                             r->accept, r->compare);
+                                             r->accept, r->effect);
 
        for (c = classmap; c->rtgroup; c++)
                agl_router_assign_class_to_rtgroup (u, c->class, c->zone,
index f39c9fb..be0098d 100644 (file)
--- a/config.h
+++ b/config.h
@@ -36,7 +36,7 @@ typedef struct {
        const char *name;
        const char *node_desc;
        agl_rtgroup_accept_t accept;
-       agl_rtgroup_compare_t compare;
+       agl_rtgroup_effect_t effect;
 } rtgroup_def;
 
  /* CLASS MAP (agl_phone="phone" card routing group...) */
index 79164bb..ca3591b 100644 (file)
@@ -743,12 +743,10 @@ void agl_discover_register_source_output (struct userdata *u, pa_source_output *
 
 void agl_discover_add_sink_input (struct userdata *u, pa_sink_input *sinp) 
 {
-       pa_core *core;
        agl_node *node;
 
        pa_assert (u);
        pa_assert (sinp);
-       pa_assert_se (core = u->core);
 
        if (!sinp->client)
                return;
@@ -759,16 +757,22 @@ void agl_discover_add_sink_input (struct userdata *u, pa_sink_input *sinp)
 
         /* start routing */
        agl_router_register_node (u, node);
+
+        /* apply priority effects */
+       agl_router_apply_node_priority_effect (u, node, true);
 }
 
 void agl_discover_remove_sink_input (struct userdata *u, pa_sink_input *sinp)
 {
-       pa_core *core;
-       agl_node *node;
+       agl_nodeset *nodeset;
+       agl_node *node, *n;
+       pa_sink *sink;
+       int priority;
+       uint32_t index;
 
        pa_assert (u);
        pa_assert (sinp);
-       pa_assert_se (core = u->core);
+       pa_assert_se (nodeset = u->nodeset);
 
        if (!sinp->client)
                return;
@@ -779,4 +783,10 @@ void agl_discover_remove_sink_input (struct userdata *u, pa_sink_input *sinp)
 
         /* stop routing */
        agl_router_unregister_node (u, node);
+
+        /* un-apply priority effects */
+       agl_router_apply_node_priority_effect (u, node, false);
+
+        /* remove node */
+       agl_node_destroy (u, node);
 }
diff --git a/node.c b/node.c
index 1876a67..c85d512 100644 (file)
--- a/node.c
+++ b/node.c
@@ -20,6 +20,7 @@
  *
  */
 #include "node.h"
+#include "router.h"
 
 #include <pulsecore/idxset.h>
 
@@ -129,12 +130,22 @@ agl_node *agl_node_create (struct userdata *u, agl_node *data)
                }
        }
 
-        /* TODO : register the node to the router */
-       /* agl_router_register_node (u, node); */
-
        return node;
 }
 
+void agl_node_destroy (struct userdata *u, agl_node *node)
+{
+       agl_nodeset *ns;
+
+       pa_assert (u);
+       pa_assert (node);
+       pa_assert_se (ns = u->nodeset);
+
+       pa_idxset_remove_by_index (ns->nodes, node->index);
+
+       pa_xfree (node);
+}
+
 const char *agl_node_type_str (agl_node_type type)
 {
        switch (type) {
@@ -208,3 +219,24 @@ agl_node *agl_node_get_from_client (struct userdata *u, pa_client *client)
 
        return NULL;
 }
+
+bool agl_node_has_highest_priority (struct userdata *u, agl_node *node)
+{
+       agl_nodeset *nodeset;
+       agl_node *n;
+       int priority;
+       uint32_t index;
+
+       pa_assert (u);
+       pa_assert (node);
+       pa_assert (nodeset = u->nodeset);
+
+       priority = agl_router_get_node_priority (u, node);
+
+       PA_IDXSET_FOREACH(n, nodeset->nodes, index) {
+               if ((n != node) && (agl_router_get_node_priority (u, n) >= priority))
+                       return false;
+       }
+
+       return true;
+}
diff --git a/node.h b/node.h
index 30c375b..837ff5a 100644 (file)
--- a/node.h
+++ b/node.h
@@ -104,10 +104,13 @@ void agl_nodeset_done (struct userdata *);
 int agl_nodeset_add_role (struct userdata *, const char *, agl_node_type, agl_nodeset_resdef *);
 
 agl_node *agl_node_create (struct userdata *, agl_node *);
+void agl_node_destroy (struct userdata *, agl_node *);
 const char *agl_node_type_str (agl_node_type);
 const char *agl_node_direction_str (agl_direction);
 
 agl_node *agl_node_get_from_data (struct userdata *, agl_direction, void *);
 agl_node *agl_node_get_from_client (struct userdata *, pa_client *);
 
+bool agl_node_has_highest_priority (struct userdata *, agl_node *);
+
 #endif
index e47a3d3..4ef0180 100644 (file)
--- a/router.c
+++ b/router.c
@@ -97,19 +97,26 @@ bool agl_router_phone_accept (struct userdata *u, agl_rtgroup *rtg, agl_node *no
        return true;
 }
 
-int agl_router_default_compare (struct userdata *u, agl_rtgroup *rtg, agl_node *n1, agl_node *n2)
+int agl_router_default_effect (struct userdata *u, agl_rtgroup *rtg, agl_node *node, bool new)
 {
        /* TODO */
        return 1;
 }
 
-int agl_router_phone_compare (struct userdata *u, agl_rtgroup *rtg, agl_node *n1, agl_node *n2)
+int agl_router_phone_effect (struct userdata *u, agl_rtgroup *rtg, agl_node *node, bool new)
 {
-       /* TODO */
+       pa_assert (u);
+       pa_assert (node);
+
+       if (new)
+               agl_utils_volume_ramp (u, node->nullsink, false);
+       else
+               agl_utils_volume_ramp (u, node->nullsink, true);
+
        return 1;
 }
 
-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_compare_t compare)
+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)
 {
        agl_router *router;
        agl_rtgroup *rtg;
@@ -132,7 +139,7 @@ agl_rtgroup *agl_router_create_rtgroup (struct userdata *u, agl_direction type,
        rtg = pa_xnew0 (agl_rtgroup, 1);
        rtg->name = pa_xstrdup (name);
        rtg->accept = accept;
-       rtg->compare = compare;
+       rtg->effect = effect;
        AGL_DLIST_INIT(rtg->entries);
        /* associate an agl_output node for an agl_input routing group */
        if (type == agl_input) {
@@ -194,7 +201,7 @@ bool agl_router_assign_class_to_rtgroup (struct userdata *u, agl_node_type class
        agl_zone *rzone;
 
        pa_assert (u);
-       pa_assert (zone < AGL_ZONE_MAX);        
+       pa_assert (zone < AGL_ZONE_MAX);
        pa_assert (type == agl_input || type == agl_output);
        pa_assert (name);
        pa_assert_se (router = u->router);
@@ -225,7 +232,7 @@ bool agl_router_assign_class_to_rtgroup (struct userdata *u, agl_node_type class
        }
 
        zonemap = classmap[zone];
-       if (!zonemap) { /* THIS LOOKS LIKE A HACK TO IGNORE THE ERROR... */
+       if (!zonemap) {
                zonemap = pa_xnew0 (agl_rtgroup *, router->maplen);
                classmap[zone] = zonemap;
        }
@@ -261,6 +268,81 @@ void agl_router_assign_class_priority (struct userdata *u, agl_node_type class,
        }
 }
 
+int agl_router_get_node_priority (struct userdata *u, agl_node *node)
+{
+       agl_router *router;
+       int class;
+
+       pa_assert (u);
+       pa_assert (node);
+       pa_assert_se (router = u->router);
+
+       class = node->type;
+
+       if (class < 0 || class >= (int)router->maplen)
+               return 0;
+
+       return router->priormap[class];
+}
+
+bool agl_router_apply_node_priority_effect (struct userdata *u, agl_node *node, bool new)
+{
+       agl_router *router;
+       agl_rtgroup *rtg;
+       agl_nodeset *nodeset;
+       agl_node *n;
+       pa_sink *sink;
+       int priority;
+       uint32_t index;
+
+       pa_assert (u);
+       pa_assert (node);
+       pa_assert_se (router = u->router);
+       pa_assert_se (nodeset = u->nodeset);
+
+        /* do we have a routing group associated with this node ? It may have a custom effect */
+       if (node->direction == agl_input)
+               rtg = pa_hashmap_get (router->rtgroups.input, agl_node_type_str (node->type));
+       else
+               rtg = pa_hashmap_get (router->rtgroups.output, agl_node_type_str (node->type));
+
+       /* now let us compare priorities, and apply effect if needed */
+       /* "new" case */
+       if (new) {
+               priority = agl_router_get_node_priority (u, node);
+               PA_IDXSET_FOREACH(n, nodeset->nodes, index) {
+                       if (n->nullsink && (priority > agl_router_get_node_priority (u, n))) {
+                               sink = agl_utils_get_null_sink (u, n->nullsink);
+                               if (sink) {
+                                       /* do we have a custom effect ? otherwise, just mute it */
+                                       if (rtg && rtg->effect)
+                                               rtg->effect (u, rtg, n, new);
+                                       else
+                                               pa_sink_set_mute (sink, new, false);
+                               }
+                       }
+               }
+       } else {
+       /* "old" case */
+               if (!agl_node_has_highest_priority (u, node))
+                       return true;
+               PA_IDXSET_FOREACH(n, nodeset->nodes, index) {
+                       if (n->nullsink) {
+                               sink = agl_utils_get_null_sink (u, n->nullsink);
+                               if (sink) {
+                                       /* do we have a custom effect ? otherwise, just unmute it */
+                                       if (rtg && rtg->effect)
+                                               rtg->effect (u, rtg, n, new);
+                                       else
+                                               pa_sink_set_mute (sink, new, false);
+                               }
+                       }
+               }
+       }
+
+       return true;
+}
+
 void agl_router_register_node (struct userdata *u, agl_node *node)
 {
        agl_router *router;
@@ -393,12 +475,10 @@ void implement_default_route (struct userdata *u,
                               agl_node *start, agl_node *end,
                               uint32_t stamp)
 {
-       if (start->direction == agl_input) {
+       if (start->direction == agl_input)
                agl_switch_setup_link (u, start, end, false);
-               //agl_volume_add_limiting_class(u, end, volume_class(start), stamp);
-       } else {
+       else
                agl_switch_setup_link (u, end, start, false);
-       }
 }
 
 agl_node *find_default_route (struct userdata *u, agl_node *start, uint32_t stamp)
index 0bf21d5..cc7bf95 100644 (file)
--- a/router.h
+++ b/router.h
 #define AGL_ZONE_MAX 8 /* max 8 zones, demo is using 5 */ /* DEFINED IN MURPHY */
 
 typedef bool (*agl_rtgroup_accept_t)(struct userdata *, agl_rtgroup *, agl_node *);
-typedef int (*agl_rtgroup_compare_t)(struct userdata *, agl_rtgroup *, agl_node *, agl_node *);
+typedef int (*agl_rtgroup_effect_t)(struct userdata *, agl_rtgroup *, agl_node *, bool new);
 
 struct agl_rtgroup {
        char *name;          /**< name of the rtgroup */
        agl_dlist entries;   /**< listhead of ordered rtentries */
        agl_node *node;      /**< final node */
        agl_rtgroup_accept_t accept; /**< function pointer, whether to accept a node or not */
-       agl_rtgroup_compare_t compare; /**< function pointer, comparision for ordering */
+       agl_rtgroup_effect_t effect; /**< function pointer, custom action such as volume up, down */
 };
 
 typedef struct {
@@ -73,13 +73,15 @@ void agl_router_done (struct userdata *);
 
 bool agl_router_default_accept (struct userdata *, agl_rtgroup *, agl_node *);
 bool agl_router_phone_accept (struct userdata *, agl_rtgroup *, agl_node *);
-int agl_router_default_compare (struct userdata *, agl_rtgroup *, agl_node *, agl_node *);
-int agl_router_phone_compare (struct userdata *, agl_rtgroup *, agl_node *, agl_node *);
+int agl_router_default_effect (struct userdata *, agl_rtgroup *, agl_node *, bool);
+int agl_router_phone_effect (struct userdata *, agl_rtgroup *, agl_node *, bool);
 
-agl_rtgroup *agl_router_create_rtgroup (struct userdata *, agl_direction, const char *, const char *, agl_rtgroup_accept_t, agl_rtgroup_compare_t);
+agl_rtgroup *agl_router_create_rtgroup (struct userdata *, agl_direction, const char *, const char *, agl_rtgroup_accept_t, agl_rtgroup_effect_t);
 void agl_router_destroy_rtgroup (struct userdata *, agl_direction, const char *);
 bool agl_router_assign_class_to_rtgroup (struct userdata *, agl_node_type, uint32_t, agl_direction, const char *);
 void agl_router_assign_class_priority (struct userdata *, agl_node_type, int);
+int agl_router_get_node_priority (struct userdata *, agl_node *);
+bool agl_router_apply_node_priority_effect (struct userdata *, agl_node *, bool);
 
 void agl_router_register_node (struct userdata *, agl_node *);
 void agl_router_unregister_node (struct userdata *, agl_node *);
index 8111068..e3b5451 100644 (file)
--- a/switch.c
+++ b/switch.c
@@ -107,6 +107,7 @@ bool agl_switch_setup_link (struct userdata *u, agl_node *from, agl_node *to, bo
                                        /* STREAM TO DEVICE : OK */
                                        case agl_stream:
                                                sink = agl_utils_get_alsa_sink (u, to->paname);
+                                               if (!sink) break;
                                                source = agl_utils_get_null_source (u, from->nullsink);
 
                                                from->loopnode = agl_loopnode_create (u, AGL_LOOPNODE_SINK, from->index, source->index, sink->index);
@@ -147,7 +148,6 @@ bool agl_switch_setup_link (struct userdata *u, agl_node *from, agl_node *to, bo
 
                        sink = agl_utils_get_primary_alsa_sink (u);
                        source = agl_utils_get_null_source (u, from->nullsink);
-
                        from->loopnode = agl_loopnode_create (u, AGL_LOOPNODE_SINK, from->index, source->index, sink->index);
                }
        }
diff --git a/utils.c b/utils.c
index 9476c69..a841e2c 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -120,6 +120,39 @@ pa_source *agl_utils_get_null_source (struct userdata *u, struct agl_null_sink *
        return sink ? sink->monitor_source : NULL;
 }
 
+void agl_utils_volume_ramp (struct userdata *u, struct agl_null_sink *ns, bool up)
+{
+       pa_core *core;
+       pa_sink *sink;
+       pa_sink_input *sinp;
+       uint32_t index;
+       pa_cvolume_ramp rampvol;
+       pa_volume_t newvol;
+       long time;
+
+       if (up) {
+               newvol = PA_VOLUME_NORM;
+               time = 5000;
+       } else {
+               newvol = PA_VOLUME_NORM *10/100;
+               time = 3000;
+       }
+
+       pa_assert (u);
+       pa_assert_se ((core = u->core));
+
+       sink = agl_utils_get_null_sink (u, ns);
+       PA_IDXSET_FOREACH(sinp, core->sink_inputs, index) {
+               if (sinp->sink && sinp->sink == sink)
+                       break;
+               sinp = NULL;
+       }
+       if (!sinp) return;
+
+       pa_cvolume_ramp_set (&rampvol, sinp->volume.channels, PA_VOLUME_RAMP_TYPE_LINEAR,
+                            time, newvol);
+       pa_sink_input_set_volume_ramp (sinp, &rampvol, true, false);
+}
 
 const char *agl_utils_get_card_name (pa_card *card)
 {
@@ -196,7 +229,7 @@ pa_sink *agl_utils_get_primary_alsa_sink (struct userdata *u)
        pa_assert_se ((core = u->core));
 
         PA_IDXSET_FOREACH(sink, core->sinks, idx) {
-               if (sink->name && strstr (sink->name, "alsa_output") && strstr (sink->name, "pci"))
+               if (sink->name && strstr (sink->name, "alsa_output"))
                        return sink;
         }
 
diff --git a/utils.h b/utils.h
index 19faeb2..abe8bef 100644 (file)
--- a/utils.h
+++ b/utils.h
@@ -32,6 +32,7 @@ struct agl_null_sink *agl_utils_create_null_sink (struct userdata *, const char
 void agl_utils_destroy_null_sink (struct userdata *, struct agl_null_sink *);
 pa_sink *agl_utils_get_null_sink (struct userdata *, struct agl_null_sink *);
 pa_source *agl_utils_get_null_source (struct userdata *, struct agl_null_sink *);
+void agl_utils_volume_ramp (struct userdata *, struct agl_null_sink *, bool);
 
  /* general helper functions */ 
 const char *agl_utils_get_card_name (pa_card *);