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