routing: add loopback support to nodes
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / volume.c
1 /*
2  * module-murphy-ivi -- PulseAudio module for providing audio routing support
3  * Copyright (c) 2012, Intel Corporation.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU Lesser General Public License,
7  * version 2.1, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.
12  * See the GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
17  * MA 02110-1301 USA.
18  *
19  */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24
25 #include <pulsecore/pulsecore-config.h>
26
27 #include <pulse/proplist.h>
28 #include <pulsecore/core-util.h>
29 #include <pulsecore/module.h>
30
31 #include "volume.h"
32 #include "node.h"
33
34 #define VLIM_CLASS_ALLOC_BUCKET  8
35
36 typedef struct vlim_entry  vlim_entry;
37 typedef struct vlim_table  vlim_table;
38
39
40 struct vlim_entry {
41     mir_volume_func_t func;      /**< volume limit function */
42     void             *arg;       /**< arg given at registration time */
43 };
44
45 struct vlim_table {
46     size_t       nentry;
47     vlim_entry  *entries;
48 };
49
50 struct pa_mir_volume {
51     int          classlen;   /**< class table length  */
52     vlim_table  *classlim;   /**< class indexed table */
53     vlim_table   genlim;     /**< generic limit */
54 };
55
56
57 static void add_to_table(vlim_table *, mir_volume_func_t, void *);
58 static void destroy_table(vlim_table *);
59 static double apply_table(double,vlim_table*, struct userdata *,int,mir_node*);
60
61 static void reset_outdated_volume_limit(mir_vlim *, uint32_t);
62
63
64 pa_mir_volume *pa_mir_volume_init(struct userdata *u)
65 {
66     pa_mir_volume *volume = pa_xnew0(pa_mir_volume, 1);
67
68     (void)u;
69
70     return volume;
71 }
72
73 void pa_mir_volume_done(struct userdata *u)
74 {
75     pa_mir_volume *volume;
76     int i;
77
78     if (u && (volume = u->volume)) {
79         for (i = 0;   i < volume->classlen;   i++) {
80             destroy_table(volume->classlim + i);
81         }
82         free(volume->classlim);
83
84         destroy_table(&volume->genlim);
85
86         pa_xfree(volume);
87
88         u->volume = NULL;
89     }
90 }
91
92 void mir_volume_add_class_limit(struct userdata  *u,
93                                 int               class,
94                                 mir_volume_func_t func,
95                                 void             *arg)
96 {
97     pa_mir_volume *volume;
98     vlim_table    *classlim;
99     vlim_table    *table;
100     size_t         newlen;
101     size_t         size;
102     size_t         diff;
103
104     pa_assert(u);
105     pa_assert(func);
106     pa_assert(class > 0);
107     pa_assert_se((volume = u->volume));
108
109     if (class < volume->classlen)
110         table = volume->classlim + class;
111     else {
112         newlen = class + 1;
113         size = sizeof(vlim_table) * newlen;
114         diff = sizeof(vlim_table) * (newlen - volume->classlen);
115
116         pa_assert_se((classlim = realloc(volume->classlim, size)));
117         memset(classlim + volume->classlen, 0, diff);
118
119
120         volume->classlen = newlen;
121         volume->classlim = classlim;
122
123         table = classlim + class;
124     }
125
126     add_to_table(table, func, arg);
127 }
128
129
130 void mir_volume_add_generic_limit(struct userdata  *u,
131                                   mir_volume_func_t func,
132                                   void             *arg)
133 {
134     pa_mir_volume *volume;
135
136     pa_assert(u);
137     pa_assert(func);
138     pa_assert_se((volume = u->volume));
139
140     add_to_table(&volume->genlim, func, arg);
141 }
142
143 void mir_volume_add_limiting_class(struct userdata *u,
144                                    mir_node        *node,
145                                    int              class,
146                                    uint32_t         stamp)
147 {
148     pa_mir_volume *volume;
149     mir_vlim      *vlim;
150     int           *classes;
151     size_t         i;
152
153     pa_assert(u);
154     pa_assert(node);
155     pa_assert_se((volume = u->volume));
156     pa_assert(class >= 0);
157
158     vlim = &node->vlim;
159
160     reset_outdated_volume_limit(vlim, stamp);
161
162     if (class < volume->classlen && volume->classlim[class].nentry > 0) {
163         for (i = 0;   i < vlim->nclass;   i++) {
164             if (class == vlim->classes[i])
165                 return; /* it is already registered */
166         }
167
168         pa_log_debug("add limiting class %d (%s) to node '%s'",
169                      class, mir_node_type_str(class), node->amname);
170
171         if (vlim->nclass < vlim->maxentry)
172             classes = vlim->classes;
173         else {
174             vlim->maxentry += VLIM_CLASS_ALLOC_BUCKET;
175             vlim->classes = realloc(vlim->classes,sizeof(int*)*vlim->maxentry);
176             pa_assert_se((classes = vlim->classes));
177         }
178
179         vlim->classes[vlim->nclass++] = class;
180     }
181 }
182
183
184 double mir_volume_apply_limits(struct userdata *u,
185                                mir_node *node,
186                                int class,
187                                uint32_t stamp)
188 {
189     pa_mir_volume *volume;
190     mir_vlim *vlim;
191     double attenuation = 0.0;
192     vlim_table *tbl;
193     size_t i;
194     int c;
195
196     pa_assert(u);
197     pa_assert_se((volume = u->volume));
198
199     if (class < 0 || class >= volume->classlen) {
200         if (class < 0 || class >= mir_application_class_end)
201             attenuation = -90.0;
202     }
203     else {
204         /* generic limits */
205         attenuation = apply_table(attenuation, &volume->genlim, u,class,node); 
206
207         /* class-based limits */
208         if (node && (vlim = &node->vlim) && stamp <= vlim->stamp) {
209             for (i = 0;   i < vlim->nclass;   i++) {
210                 c = vlim->classes[i];
211                 
212                 pa_assert(c >= 0 && c < volume->classlen);
213                 tbl = volume->classlim + c;
214                 
215                 attenuation = apply_table(attenuation, tbl, u, class, node);
216             }
217         }
218     }
219
220     return attenuation;
221 }
222                                
223
224 double mir_volume_suppress(struct userdata *u, int class, mir_node *node,
225                            void *arg)
226 {
227     mir_volume_suppress_arg *suppress = arg;
228     size_t i;
229
230     pa_assert(u);
231     pa_assert(node);
232
233     if (suppress) {
234         for (i = 0;   i < suppress->exception.nclass;   i++) {
235             if (suppress->exception.classes[i] == class)
236                 return 0.0;
237         }
238
239         return suppress->attenuation;
240     }
241
242     return 0.0;
243 }
244
245 double mir_volume_correction(struct userdata *u, int class, mir_node *node,
246                              void *arg)
247 {
248     pa_assert(u);
249     pa_assert(node);
250
251     if (arg && node->implement == mir_device && node->privacy == mir_public)
252         return *(double *)arg;
253
254     return 0.0;
255 }
256
257 static void add_to_table(vlim_table *tbl, mir_volume_func_t func, void *arg)
258 {
259     size_t      size;
260     vlim_entry *entries;
261     vlim_entry *entry;
262
263     pa_assert(tbl);
264     pa_assert(func);
265
266     size = sizeof(vlim_entry *) * (tbl->nentry + 1);
267     pa_assert_se((entries = realloc(tbl->entries,  size)));
268     entry = entries + tbl->nentry;
269
270     entry->func = func;
271     entry->arg  = arg;
272
273     tbl->nentry += 1;
274     tbl->entries = entries;
275 }
276
277 static void destroy_table(vlim_table *tbl)
278 {
279     pa_assert(tbl);
280     
281     free(tbl->entries);
282 }
283
284 static double apply_table(double attenuation,
285                           vlim_table *tbl,
286                           struct userdata *u,
287                           int class,
288                           mir_node *node)
289 {
290     static mir_node fake_node;
291
292     double a;
293     vlim_entry *e;
294     size_t i;
295
296     pa_assert(tbl);
297     pa_assert(u);
298
299     if (!node)
300         node = &fake_node;
301
302     for (i = 0;   i < tbl->nentry;  i++) {
303         e = tbl->entries + i;
304         if ((a = e->func(u, class, node, e->arg)) < attenuation)
305             attenuation = a;
306     }
307
308     return attenuation;
309 }
310
311
312
313 static void reset_outdated_volume_limit(mir_vlim *vl, uint32_t stamp)
314 {
315     if (stamp > vl->stamp) {
316         vl->nclass = 0;
317         vl->stamp  = stamp;
318     }
319 }
320
321
322
323
324
325 /*
326  * Local Variables:
327  * c-basic-offset: 4
328  * indent-tabs-mode: nil
329  * End:
330  *
331  */