2 * module-murphy-ivi -- PulseAudio module for providing audio routing support
3 * Copyright (c) 2012, Intel Corporation.
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.
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.
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,
25 #include <pulsecore/pulsecore-config.h>
27 #include <pulsemodule/modules/main-volume-policy/main-volume-policy.h>
29 #include <pulse/proplist.h>
30 #include <pulsecore/core-util.h>
31 #include <pulsecore/module.h>
38 #define VLIM_CLASS_ALLOC_BUCKET 16
40 typedef struct vlim_entry vlim_entry;
41 typedef struct vlim_table vlim_table;
45 mir_volume_func_t func; /**< volume limit function */
46 void *arg; /**< arg given at registration time */
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;
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 *);
68 static void reset_volume_limit(struct userdata *, mir_node *, uint32_t);
69 static void add_volume_limit(struct userdata *, mir_node *, int);
72 pa_mir_volume *pa_mir_volume_init(struct userdata *u)
74 pa_mir_volume *volume = pa_xnew0(pa_mir_volume, 1);
79 for (i = 0; i < mir_application_class_end; i++)
80 volume->maxlim[i] = MIR_VOLUME_MAX_ATTENUATION;
82 volume->main_volume_policy = pa_main_volume_policy_get(u->core);
87 void pa_mir_volume_done(struct userdata *u)
89 pa_mir_volume *volume;
92 if (u && (volume = u->volume)) {
93 if (volume->main_volume_policy)
94 pa_main_volume_policy_unref(volume->main_volume_policy);
96 for (i = 0; i < volume->classlen; i++) {
97 destroy_table(volume->classlim + i);
99 free(volume->classlim);
101 destroy_table(&volume->genlim);
109 void mir_volume_add_class_limit(struct userdata *u,
111 mir_volume_func_t func,
114 pa_mir_volume *volume;
115 vlim_table *classlim;
123 pa_assert(class > 0);
124 pa_assert_se((volume = u->volume));
126 if (class < volume->classlen)
127 table = volume->classlim + class;
130 size = sizeof(vlim_table) * newlen;
131 diff = sizeof(vlim_table) * (newlen - volume->classlen);
133 pa_assert_se((classlim = realloc(volume->classlim, size)));
134 memset(classlim + volume->classlen, 0, diff);
137 volume->classlen = newlen;
138 volume->classlim = classlim;
140 table = classlim + class;
143 add_to_table(table, func, arg);
147 void mir_volume_add_generic_limit(struct userdata *u,
148 mir_volume_func_t func,
151 pa_mir_volume *volume;
155 pa_assert_se((volume = u->volume));
157 add_to_table(&volume->genlim, func, arg);
161 void mir_volume_add_maximum_limit(struct userdata *u,
166 pa_mir_volume *volume;
171 pa_assert_se((volume = u->volume));
173 for (i = 0; i < nclass; i++) {
174 if ((class = classes[i]) < mir_application_class_end)
175 volume->maxlim[class] = maxlim;
180 void mir_volume_make_limiting(struct userdata *u)
186 stamp = pa_utils_new_stamp();
188 pa_fader_apply_volume_limits(u, stamp);
191 void mir_volume_add_limiting_class(struct userdata *u,
198 pa_assert(class >= 0);
200 if (node->implement == mir_device && node->direction == mir_output) {
201 if (stamp > node->vlim.stamp)
202 reset_volume_limit(u, node, stamp);
204 add_volume_limit(u, node, class);
209 double mir_volume_apply_limits(struct userdata *u,
214 pa_mir_volume *volume;
215 double attenuation = 0.0;
216 double devlim, classlim;
222 pa_assert_se((volume = u->volume));
224 if (class < 0 || class >= volume->classlen) {
225 if (class < 0 || class >= mir_application_class_end)
226 attenuation = maxlim = MIR_VOLUME_MAX_ATTENUATION;
228 attenuation = apply_table(0.0, &volume->genlim,
229 u,class,node, "device");
233 devlim = apply_table(0.0, &volume->genlim, u,class,node, "device");
237 pa_assert(class >= mir_application_class_begin);
238 pa_assert(class < mir_application_class_end);
240 maxlim = volume->maxlim[class];
241 clmask = (uint32_t)1 << (class - mir_application_class_begin);
243 if (class < volume->classlen && (tbl = volume->classlim + class))
244 classlim = apply_table(classlim, tbl, u, class, node, "class");
246 if (classlim <= MIR_VOLUME_MAX_ATTENUATION)
247 classlim = MIR_VOLUME_MAX_ATTENUATION;
248 else if (classlim < maxlim)
252 attenuation = devlim + classlim;
259 double mir_volume_suppress(struct userdata *u, int class, mir_node *node,
262 mir_volume_suppress_arg *suppress = arg;
263 uint32_t clmask, trigmask;
266 pa_assert(class >= mir_application_class_begin);
267 pa_assert(class < mir_application_class_end);
269 pa_assert(node->direction == mir_output);
270 pa_assert(node->implement == mir_device);
272 clmask = ((uint32_t)1) << (class - mir_application_class_begin);
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);
279 if (!(trigmask & clmask) && (trigmask & node->vlim.clmask))
280 return *suppress->attenuation;
286 double mir_volume_correction(struct userdata *u, int class, mir_node *node,
292 if (arg && *(double **)arg &&
293 node->implement == mir_device &&
294 node->privacy == mir_public)
296 return **(double **)arg;
302 void mir_volume_change_context(struct userdata *u, const char *volume_class)
304 pa_main_volume_policy *policy;
305 pa_main_volume_context *ctx;
310 pa_log_error("no volume class set");
314 policy = u->volume->main_volume_policy;
316 /* see if there is a context available that maps to the volume class */
318 ctx = (pa_main_volume_context *) pa_hashmap_get(policy->main_volume_contexts, volume_class);
321 pa_main_volume_policy_set_active_main_volume_context(policy, ctx);
322 pa_log_debug("volume context changed to: '%s'", volume_class);
325 /* TODO: change volume class here */
328 static void add_to_table(vlim_table *tbl, mir_volume_func_t func, void *arg)
337 size = sizeof(vlim_entry) * (tbl->nentry + 1);
338 pa_assert_se((entries = realloc(tbl->entries, size)));
339 entry = entries + tbl->nentry;
345 tbl->entries = entries;
348 static void destroy_table(vlim_table *tbl)
355 static double apply_table(double attenuation,
362 static mir_node fake_node;
375 for (i = 0; i < tbl->nentry; i++) {
376 e = tbl->entries + i;
377 a = e->func(u, class, node, e->arg);
379 pa_log_debug(" %s limit = %.2lf", type, a);
390 static void reset_volume_limit(struct userdata *u,
394 mir_vlim *vlim = &node->vlim;
403 pa_assert_se((core = u->core));
405 pa_log_debug("reset volume classes on node '%s'", node->amname);
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);
420 static void add_volume_limit(struct userdata *u, mir_node *node, int class)
422 mir_vlim *vlim = &node->vlim;
423 pa_mir_volume *volume;
430 pa_assert_se((volume = u->volume));
431 pa_assert(class >= 0);
433 if (class < mir_application_class_begin ||
434 class >= mir_application_class_end )
436 pa_log_debug("refusing to add unknown volume class %d to node '%s'",
437 class, node->amname);
440 mask = ((uint32_t)1) << (class - mir_application_class_begin);
442 if (class < volume->classlen && volume->classlim[class].nentry > 0) {
443 if (!(vlim->clmask & mask)) {
445 if (vlim->nclass < vlim->maxentry)
446 classes = vlim->classes;
448 vlim->maxentry += VLIM_CLASS_ALLOC_BUCKET;
449 classes_size = sizeof(int *) * vlim->maxentry;
450 vlim->classes = realloc(vlim->classes, classes_size);
452 pa_assert_se((classes = vlim->classes));
455 vlim->classes[vlim->nclass++] = class;
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,
465 vlim->clmask |= mask;
476 * indent-tabs-mode: nil