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