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>
39 #define VLIM_CLASS_ALLOC_BUCKET 16
41 typedef struct vlim_entry vlim_entry;
42 typedef struct vlim_table vlim_table;
46 mir_volume_func_t func; /**< volume limit function */
47 void *arg; /**< arg given at registration time */
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;
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 *);
69 static void reset_volume_limit(struct userdata *, mir_node *, uint32_t);
70 static void add_volume_limit(struct userdata *, mir_node *, int);
73 pa_mir_volume *pa_mir_volume_init(struct userdata *u)
75 pa_mir_volume *volume = pa_xnew0(pa_mir_volume, 1);
80 for (i = 0; i < mir_application_class_end; i++)
81 volume->maxlim[i] = MIR_VOLUME_MAX_ATTENUATION;
83 volume->main_volume_policy = pa_main_volume_policy_get(u->core);
88 void pa_mir_volume_done(struct userdata *u)
90 pa_mir_volume *volume;
93 if (u && (volume = u->volume)) {
94 if (volume->main_volume_policy)
95 pa_main_volume_policy_unref(volume->main_volume_policy);
97 for (i = 0; i < volume->classlen; i++) {
98 destroy_table(volume->classlim + i);
100 free(volume->classlim);
102 destroy_table(&volume->genlim);
110 void mir_volume_add_class_limit(struct userdata *u,
112 mir_volume_func_t func,
115 pa_mir_volume *volume;
116 vlim_table *classlim;
124 pa_assert(class > 0);
125 pa_assert_se((volume = u->volume));
127 if (class < volume->classlen)
128 table = volume->classlim + class;
130 newlen = (size_t)(class + 1);
131 size = sizeof(vlim_table) * newlen;
132 diff = sizeof(vlim_table) * (newlen - (size_t)volume->classlen);
134 pa_assert_se((classlim = realloc(volume->classlim, size)));
135 memset(classlim + volume->classlen, 0, diff);
138 volume->classlen = (int)newlen;
139 volume->classlim = classlim;
141 table = classlim + class;
144 add_to_table(table, func, arg);
148 void mir_volume_add_generic_limit(struct userdata *u,
149 mir_volume_func_t func,
152 pa_mir_volume *volume;
156 pa_assert_se((volume = u->volume));
158 add_to_table(&volume->genlim, func, arg);
162 void mir_volume_add_maximum_limit(struct userdata *u,
167 pa_mir_volume *volume;
172 pa_assert_se((volume = u->volume));
174 for (i = 0; i < nclass; i++) {
175 if ((class = classes[i]) < mir_application_class_end)
176 volume->maxlim[class] = maxlim;
181 void mir_volume_make_limiting(struct userdata *u)
187 stamp = pa_utils_new_stamp();
189 pa_fader_apply_volume_limits(u, stamp);
192 void mir_volume_add_limiting_class(struct userdata *u,
199 pa_assert(class >= 0);
201 if (node->implement == mir_device && node->direction == mir_output) {
202 if (stamp > node->vlim.stamp)
203 reset_volume_limit(u, node, stamp);
205 add_volume_limit(u, node, class);
210 double mir_volume_apply_limits(struct userdata *u,
216 pa_mir_volume *volume;
217 double attenuation = 0.0;
218 double devlim, classlim;
223 pa_assert_se((volume = u->volume));
225 if (class < 0 || class >= volume->classlen) {
226 if (class < 0 || class >= mir_application_class_end)
227 attenuation = maxlim = MIR_VOLUME_MAX_ATTENUATION;
229 attenuation = apply_table(0.0, &volume->genlim,
230 u,class,node,mask, "device");
234 devlim = apply_table(0.0, &volume->genlim, u,class,node,mask, "device");
238 pa_assert(class >= mir_application_class_begin);
239 pa_assert(class < mir_application_class_end);
241 maxlim = volume->maxlim[class];
243 if (class < volume->classlen && (tbl = volume->classlim + class))
244 classlim = apply_table(classlim, tbl, u,class,node,mask, "class");
246 if (classlim <= MIR_VOLUME_MAX_ATTENUATION)
247 classlim = MIR_VOLUME_MAX_ATTENUATION;
248 else if (classlim < maxlim)
252 attenuation = devlim + classlim;
258 uint32_t mir_volume_get_class_mask(int class)
260 if (class >= mir_application_class_begin &&
261 class < mir_application_class_end )
263 return ((uint32_t)1) << (class - mir_application_class_begin);
270 double mir_volume_suppress(struct userdata *u, int class,
271 mir_node *node, uint32_t mask, void *arg)
273 mir_volume_suppress_arg *suppress = arg;
274 uint32_t clmask, trigmask;
277 pa_assert(class >= mir_application_class_begin);
278 pa_assert(class < mir_application_class_end);
280 pa_assert(node->direction == mir_output);
281 pa_assert(node->implement == mir_device);
283 clmask = mir_volume_get_class_mask(class);
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);
290 if (!(trigmask & clmask) && (trigmask & mask /* node->vlim.clmask */))
291 return *suppress->attenuation;
297 double mir_volume_correction(struct userdata *u, int class,
298 mir_node *node, uint32_t mask, void *arg)
305 if (arg && *(double **)arg &&
306 node->implement == mir_device &&
307 node->privacy == mir_public)
309 return **(double **)arg;
315 void mir_volume_change_context(struct userdata *u, const char *volume_class)
317 pa_main_volume_policy *policy;
318 pa_main_volume_context *ctx;
323 pa_log_error("no volume class set");
327 policy = u->volume->main_volume_policy;
329 /* see if there is a context available that maps to the volume class */
331 ctx = (pa_main_volume_context *) pa_hashmap_get(policy->main_volume_contexts, volume_class);
334 pa_main_volume_policy_set_active_main_volume_context(policy, ctx);
335 pa_log_debug("volume context changed to: '%s'", volume_class);
338 /* TODO: change volume class here */
341 static void add_to_table(vlim_table *tbl, mir_volume_func_t func, void *arg)
350 size = sizeof(vlim_entry) * (tbl->nentry + 1);
351 pa_assert_se((entries = realloc(tbl->entries, size)));
352 entry = entries + tbl->nentry;
358 tbl->entries = entries;
361 static void destroy_table(vlim_table *tbl)
368 static double apply_table(double attenuation,
376 static mir_node fake_node;
389 for (i = 0; i < tbl->nentry; i++) {
390 e = tbl->entries + i;
391 a = e->func(u, class, node,mask, e->arg);
393 pa_log_debug(" %s limit = %.2lf", type, a);
404 static void reset_volume_limit(struct userdata *u,
408 mir_vlim *vlim = &node->vlim;
417 pa_assert_se((core = u->core));
419 pa_log_debug("reset volume classes on node '%s'", node->amname);
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);
434 static void add_volume_limit(struct userdata *u, mir_node *node, int class)
436 mir_vlim *vlim = &node->vlim;
437 pa_mir_volume *volume;
444 pa_assert_se((volume = u->volume));
445 pa_assert(class >= 0);
447 if (class < mir_application_class_begin ||
448 class >= mir_application_class_end )
450 pa_log_debug("refusing to add unknown volume class %d to node '%s'",
451 class, node->amname);
454 mask = mir_volume_get_class_mask(class);
456 if (class < volume->classlen && volume->classlim[class].nentry > 0) {
457 if (!(vlim->clmask & mask)) {
459 if (vlim->nclass < vlim->maxentry)
460 classes = vlim->classes;
462 vlim->maxentry += VLIM_CLASS_ALLOC_BUCKET;
463 classes_size = sizeof(int *) * vlim->maxentry;
464 vlim->classes = realloc(vlim->classes, classes_size);
466 pa_assert_se((classes = vlim->classes));
469 vlim->classes[vlim->nclass++] = class;
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,
479 vlim->clmask |= mask;
490 * indent-tabs-mode: nil