scripting: support for zone based routing in application classes
[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 "fader.h"
33 #include "node.h"
34 #include "utils.h"
35
36 #define VLIM_CLASS_ALLOC_BUCKET  16
37
38 typedef struct vlim_entry  vlim_entry;
39 typedef struct vlim_table  vlim_table;
40
41
42 struct vlim_entry {
43     mir_volume_func_t func;      /**< volume limit function */
44     void             *arg;       /**< arg given at registration time */
45 };
46
47 struct vlim_table {
48     size_t       nentry;
49     vlim_entry  *entries;
50 };
51
52 struct pa_mir_volume {
53     int          classlen;   /**< class table length  */
54     vlim_table  *classlim;   /**< class indexed table */
55     vlim_table   genlim;     /**< generic limit */
56 };
57
58
59 static void add_to_table(vlim_table *, mir_volume_func_t, void *);
60 static void destroy_table(vlim_table *);
61 static double apply_table(double, vlim_table *, struct userdata *, int,
62                           mir_node *, const char *);
63
64 static void reset_volume_limit(struct userdata *, mir_node *, uint32_t);
65 static void add_volume_limit(struct userdata *, mir_node *, int);
66
67
68 pa_mir_volume *pa_mir_volume_init(struct userdata *u)
69 {
70     pa_mir_volume *volume = pa_xnew0(pa_mir_volume, 1);
71
72     (void)u;
73
74     return volume;
75 }
76
77 void pa_mir_volume_done(struct userdata *u)
78 {
79     pa_mir_volume *volume;
80     int i;
81
82     if (u && (volume = u->volume)) {
83         for (i = 0;   i < volume->classlen;   i++) {
84             destroy_table(volume->classlim + i);
85         }
86         free(volume->classlim);
87
88         destroy_table(&volume->genlim);
89
90         pa_xfree(volume);
91
92         u->volume = NULL;
93     }
94 }
95
96 void mir_volume_add_class_limit(struct userdata  *u,
97                                 int               class,
98                                 mir_volume_func_t func,
99                                 void             *arg)
100 {
101     pa_mir_volume *volume;
102     vlim_table    *classlim;
103     vlim_table    *table;
104     size_t         newlen;
105     size_t         size;
106     size_t         diff;
107
108     pa_assert(u);
109     pa_assert(func);
110     pa_assert(class > 0);
111     pa_assert_se((volume = u->volume));
112
113     if (class < volume->classlen)
114         table = volume->classlim + class;
115     else {
116         newlen = class + 1;
117         size = sizeof(vlim_table) * newlen;
118         diff = sizeof(vlim_table) * (newlen - volume->classlen);
119
120         pa_assert_se((classlim = realloc(volume->classlim, size)));
121         memset(classlim + volume->classlen, 0, diff);
122
123
124         volume->classlen = newlen;
125         volume->classlim = classlim;
126
127         table = classlim + class;
128     }
129
130     add_to_table(table, func, arg);
131 }
132
133
134 void mir_volume_add_generic_limit(struct userdata  *u,
135                                   mir_volume_func_t func,
136                                   void             *arg)
137 {
138     pa_mir_volume *volume;
139
140     pa_assert(u);
141     pa_assert(func);
142     pa_assert_se((volume = u->volume));
143
144     add_to_table(&volume->genlim, func, arg);
145 }
146
147
148 void mir_volume_make_limiting(struct userdata *u)
149 {
150     uint32_t stamp;
151
152     pa_assert(u);
153
154     stamp = pa_utils_new_stamp();
155
156     pa_fader_apply_volume_limits(u, stamp);
157 }
158
159 void mir_volume_add_limiting_class(struct userdata *u,
160                                    mir_node        *node,
161                                    int              class,
162                                    uint32_t         stamp)
163 {
164     pa_assert(u);
165     pa_assert(node);
166     pa_assert(class >= 0);
167
168     if (node->implement == mir_device && node->direction == mir_output) {
169         if (stamp > node->vlim.stamp)
170             reset_volume_limit(u, node, stamp);
171
172         add_volume_limit(u, node, class);
173     }
174 }
175
176
177 double mir_volume_apply_limits(struct userdata *u,
178                                mir_node *node,
179                                int class,
180                                uint32_t stamp)
181 {
182     pa_mir_volume *volume;
183     double attenuation = 0.0;
184     double devlim, classlim;
185     vlim_table *tbl;
186     uint32_t clmask;
187
188     pa_assert(u);
189     pa_assert_se((volume = u->volume));
190
191     if (class < 0 || class >= volume->classlen) {
192         if (class < 0 || class >= mir_application_class_end)
193             attenuation = -90.0;
194     }
195     else {
196         devlim = apply_table(0.0, &volume->genlim, u,class,node, "device");
197         classlim = 0.0;
198
199         if (class && node) {
200             pa_assert(class >= mir_application_class_begin);
201             pa_assert(class <  mir_application_class_end);
202
203             clmask = (uint32_t)1 << (class - mir_application_class_begin);
204             
205             if (class < volume->classlen && (tbl = volume->classlim + class))
206                 classlim = apply_table(classlim, tbl, u, class, node, "class");
207         }
208
209         attenuation = devlim + classlim;
210     }
211
212     return attenuation;
213 }
214                                
215
216 double mir_volume_suppress(struct userdata *u, int class, mir_node *node,
217                            void *arg)
218 {
219     mir_volume_suppress_arg *suppress = arg;
220     uint32_t clmask, trigmask;
221
222     pa_assert(u);
223     pa_assert(class >= mir_application_class_begin);
224     pa_assert(class <  mir_application_class_end);
225     pa_assert(node);
226     pa_assert(node->direction == mir_output);
227     pa_assert(node->implement == mir_device);
228
229     clmask = ((uint32_t)1) << (class - mir_application_class_begin);
230
231     if (suppress && (trigmask = suppress->trigger.clmask)) {
232         pa_log_debug("        volume_supress(class=%d, clmask=0x%x, "
233                      "trigmask=0x%x nodemask=0x%x)",
234                      class, clmask, trigmask, node->vlim.clmask);
235
236         if (!(trigmask & clmask) && (trigmask & node->vlim.clmask))
237             return *suppress->attenuation;
238     }
239
240     return 0.0;
241 }
242
243 double mir_volume_correction(struct userdata *u, int class, mir_node *node,
244                              void *arg)
245 {
246     pa_assert(u);
247     pa_assert(node);
248
249     if (arg && *(double **)arg &&
250         node->implement == mir_device &&
251         node->privacy == mir_public)
252     {
253         return **(double **)arg;
254     }
255
256     return 0.0;
257 }
258
259 static void add_to_table(vlim_table *tbl, mir_volume_func_t func, void *arg)
260 {
261     size_t      size;
262     vlim_entry *entries;
263     vlim_entry *entry;
264
265     pa_assert(tbl);
266     pa_assert(func);
267
268     size = sizeof(vlim_entry) * (tbl->nentry + 1);
269     pa_assert_se((entries = realloc(tbl->entries,  size)));
270     entry = entries + tbl->nentry;
271
272     entry->func = func;
273     entry->arg  = arg;
274
275     tbl->nentry += 1;
276     tbl->entries = entries;
277 }
278
279 static void destroy_table(vlim_table *tbl)
280 {
281     pa_assert(tbl);
282     
283     free(tbl->entries);
284 }
285
286 static double apply_table(double attenuation,
287                           vlim_table *tbl,
288                           struct userdata *u,
289                           int class,
290                           mir_node *node,
291                           const char *type)
292 {
293     static mir_node fake_node;
294
295     double a;
296     vlim_entry *e;
297     size_t i;
298
299     pa_assert(tbl);
300     pa_assert(u);
301     pa_assert(type);
302
303     if (!node)
304         node = &fake_node;
305
306     for (i = 0;   i < tbl->nentry;  i++) {
307         e = tbl->entries + i;
308         a = e->func(u, class, node, e->arg);
309
310         pa_log_debug("        %s limit = %.2lf", type, a);
311
312         if (a < attenuation)
313             attenuation = a;
314     }
315
316     return attenuation;
317 }
318
319
320
321 static void reset_volume_limit(struct userdata *u,
322                                mir_node        *node,
323                                uint32_t         stamp)
324 {
325     mir_vlim      *vlim = &node->vlim;
326     pa_core       *core;
327     pa_sink       *sink;
328     pa_sink_input *sinp;
329     int            class;
330     uint32_t       i;
331
332     pa_assert(u);
333     pa_assert(node);
334     pa_assert_se((core = u->core));
335
336     pa_log_debug("reset volume classes on node '%s'", node->amname);
337
338     vlim->nclass = 0;
339     vlim->clmask = 0;
340     vlim->stamp  = stamp;
341
342     if ((sink = pa_idxset_get_by_index(core->sinks, node->paidx))) {
343         PA_IDXSET_FOREACH(sinp, sink->inputs, i) {
344             class = pa_utils_get_stream_class(sinp->proplist);
345             add_volume_limit(u, node, class);
346         }
347     }
348 }
349
350
351 static void add_volume_limit(struct userdata *u, mir_node *node, int class)
352 {
353     mir_vlim      *vlim = &node->vlim;
354     pa_mir_volume *volume;
355     int           *classes;
356     size_t         classes_size;
357     uint32_t       mask;
358
359     pa_assert(u);
360     pa_assert(node);
361     pa_assert_se((volume = u->volume));
362     pa_assert(class >= 0);
363
364     if (class <  mir_application_class_begin ||
365         class >= mir_application_class_end      )
366     {
367         pa_log_debug("refusing to add unknown volume class %d to node '%s'",
368                      class, node->amname);
369     }
370     else {
371         mask = ((uint32_t)1) << (class - mir_application_class_begin);
372         
373         if (class < volume->classlen && volume->classlim[class].nentry > 0) {
374             if (!(vlim->clmask & mask)) {
375
376                 if (vlim->nclass < vlim->maxentry)
377                     classes = vlim->classes;
378                 else {
379                     vlim->maxentry += VLIM_CLASS_ALLOC_BUCKET;
380                     classes_size    = sizeof(int *) * vlim->maxentry;
381                     vlim->classes   = realloc(vlim->classes, classes_size);
382
383                     pa_assert_se((classes = vlim->classes));
384                 }
385
386                 vlim->classes[vlim->nclass++] = class;
387             }
388         }
389
390         if (!(vlim->clmask & mask)) {
391             pa_log_debug("add volume class %d (%s) to node '%s' (clmask 0x%x)",
392                          class, mir_node_type_str(class), node->amname,
393                          vlim->clmask);
394         }
395
396         vlim->clmask |= mask;
397
398     }
399 }
400
401
402
403
404 /*
405  * Local Variables:
406  * c-basic-offset: 4
407  * indent-tabs-mode: nil
408  * End:
409  *
410  */