pump up version number and update changelog
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / fader.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/sink.h>
30 #include <pulsecore/sink-input.h>
31
32 #include "fader.h"
33 #include "node.h"
34 #include "discover.h"
35 #include "volume.h"
36 #include "utils.h"
37
38 typedef struct {
39     long fade_out;
40     long fade_in;
41 } transition_time;
42
43
44 struct pa_fader {
45     transition_time transit;
46 };
47
48 static void set_stream_volume_limit(struct userdata *, pa_sink_input *,
49                                     pa_volume_t, long);
50
51 pa_fader *pa_fader_init(const char *fade_out_str, const char *fade_in_str)
52 {
53     pa_fader *fader = pa_xnew0(pa_fader, 1);
54
55     if (!fade_out_str || pa_atol(fade_out_str, &fader->transit.fade_out) < 0)
56         fader->transit.fade_out = 100;
57
58     if (!fade_in_str || pa_atol(fade_in_str, &fader->transit.fade_in) < 0)
59         fader->transit.fade_in = 1000;
60
61     if (fader->transit.fade_out > 10000)
62         fader->transit.fade_out = 10000;
63
64     if (fader->transit.fade_in > 10000)
65         fader->transit.fade_in = 10000;
66
67     pa_log_info("fader transition times: out %ld ms, in %ld ms",
68                 fader->transit.fade_out, fader->transit.fade_in);
69
70     return fader;
71 }
72
73 void pa_fader_done(struct userdata *u)
74 {
75     if (u) {
76         pa_xfree(u->fader);
77     }
78 }
79
80
81
82 void pa_fader_apply_volume_limits(struct userdata *u, uint32_t stamp)
83 {
84     pa_core         *core;
85     transition_time *transit;
86     pa_sink         *sink;
87     pa_sink_input   *sinp, *origin;
88     pa_cvolume_ramp_int  *ramp;
89     mir_node        *device_node;
90     mir_node        *stream_node;
91     double           dB;
92     pa_volume_t      newvol;
93     pa_volume_t      oldvol;
94     long             time;
95     uint32_t         i,j;
96     int              class;
97     bool             rampit;
98     bool             corked;
99     bool             muted;
100     uint32_t         mask;
101
102     pa_assert(u);
103     pa_assert_se(u->fader);
104     pa_assert_se((core = u->core));
105
106     transit = &u->fader->transit;
107     rampit  = transit->fade_in > 0 &&  transit->fade_out > 0;
108
109     pa_log_debug("applying volume limits ...");
110
111     PA_IDXSET_FOREACH(sink, core->sinks, i) {
112         if ((device_node = pa_discover_find_node_by_ptr(u, sink))) {
113             pa_log_debug("   node '%s'", device_node->amname);
114
115             mask = 0;
116
117             PA_IDXSET_FOREACH(sinp, sink->inputs, j) {
118                 origin = pa_utils_get_stream_origin(u, sinp);
119                 stream_node = pa_discover_find_node_by_ptr(u, origin);
120
121                 if ((class = pa_utils_get_stream_class(sinp->proplist)) > 0) {
122
123                     corked = stream_node ? !stream_node->rset.grant : false;
124                     muted  = (sinp->muted  || pa_hashmap_get(sinp->volume_factor_items,
125                                                              "internal_mute"));
126                     if (origin != sinp) {
127                         muted |= (origin->muted || pa_hashmap_get(origin->volume_factor_items,
128                                                                   "internal_mute"));
129                     }
130
131                     if (!corked && !muted)
132                         mask |= mir_volume_get_class_mask(class);
133
134                     pa_log_debug("*** stream %u (origin %u) class: %d corked: %s muted: %s "
135                                  "(sinp:%s internal:%s)", sinp->index, origin->index,
136                                  class, corked?"yes":"no ",
137                            muted?"yes":"no", sinp->muted ? "yes":"no",
138                            pa_hashmap_get(sinp->volume_factor_items,"internal_mute")?"yes":"no");
139                 }
140                 else
141                     pa_log_debug("*** steam %u (origin %u) class: %d", sinp->index, origin->index, class);
142             }
143
144             pa_log_debug("*** mask: 0x%x", mask);
145
146             PA_IDXSET_FOREACH(sinp, sink->inputs, j) {
147                 class = pa_utils_get_stream_class(sinp->proplist);
148
149                 pa_log_debug("     stream %u (class %u)", sinp->index, class);
150
151                 if (!class) {
152                     if (!(sinp->flags & PA_SINK_INPUT_START_RAMP_MUTED))
153                         pa_log_debug("        skipping");
154                     else {
155                         sinp->flags &= ~((unsigned int)PA_SINK_INPUT_START_RAMP_MUTED);
156                         time = transit->fade_in;
157
158                         pa_log_debug("        attenuation 0 dB "
159                                      "transition time %ld ms", time);
160                         set_stream_volume_limit(u, sinp, PA_VOLUME_NORM, time);
161                     }
162                 }
163                 else {
164                     dB = mir_volume_apply_limits(u, device_node, mask, class, stamp);
165                     newvol = pa_sw_volume_from_dB(dB);
166
167                     if (rampit) {
168                         ramp   = &sinp->ramp;
169                         oldvol = ramp->ramps[0].target;
170
171                         if (oldvol > newvol)
172                             time = transit->fade_out;
173                         else if (oldvol < newvol)
174                             time = transit->fade_in;
175                         else
176                             time = 0;
177                     }
178                     else {
179                         oldvol = sinp->volume_factor.values[0];
180                         time = 0;
181                     }
182
183                     if (oldvol == newvol)
184                         pa_log_debug("         attenuation %.2lf dB",dB);
185                     else {
186                         pa_log_debug("         attenuation %.2lf dB "
187                                      "transition time %ld ms", dB, time);
188                         set_stream_volume_limit(u, sinp, newvol, time);
189                     }
190                 }
191             } /* PA_IDXSET_FOREACH sinp */
192         }
193     } /* PA_IDXSET_FOREACH sink */
194 }
195
196 void pa_fader_ramp_volume(struct userdata *u,
197                           pa_sink_input *sinp,
198                           pa_volume_t newvol)
199 {
200     transition_time      *transit;
201     bool                  rampit;
202     pa_volume_t           oldvol;
203     pa_cvolume_ramp_int  *ramp;
204     long                  time;
205     pa_cvolume_ramp       rampvol;
206
207     pa_assert(u);
208     pa_assert(u->fader);
209     pa_assert(sinp);
210
211     transit = &u->fader->transit;
212     rampit  = transit->fade_in > 0 &&  transit->fade_out > 0;
213     ramp    = &sinp->ramp;
214     oldvol  = ramp->ramps[0].target;
215
216     if (rampit && oldvol != newvol) {
217         time = (oldvol > newvol) ? transit->fade_out : transit->fade_in;
218
219         pa_cvolume_ramp_set(&rampvol,
220                             sinp->volume.channels,
221                             PA_VOLUME_RAMP_TYPE_LINEAR,
222                             time, newvol);
223
224         pa_sink_input_set_volume_ramp(sinp, &rampvol, true, false);
225     }
226 }
227
228
229 void pa_fader_set_volume(struct userdata *u,
230                           pa_sink_input *sinp,
231                           pa_volume_t newvol)
232 {
233     pa_volume_t oldvol;
234     pa_cvolume_ramp_int *ramp;
235     pa_cvolume_ramp  rampvol;
236
237     pa_assert(u);
238     pa_assert(sinp);
239
240     ramp   = &sinp->ramp;
241     oldvol = ramp->ramps[0].target;
242
243     if (oldvol != newvol) {
244         pa_cvolume_ramp_set(&rampvol,
245                             sinp->volume.channels,
246                             PA_VOLUME_RAMP_TYPE_LINEAR,
247                             0, newvol);
248
249         pa_sink_input_set_volume_ramp(sinp, &rampvol, true, false);
250     }
251 }
252
253 pa_volume_t pa_fader_get_volume(struct userdata *u, pa_sink_input *sinp)
254 {
255     pa_cvolume_ramp_int *ramp;
256     pa_volume_t vol;
257
258     pa_assert(u);
259     pa_assert(sinp);
260
261     ramp = &sinp->ramp;
262     vol  = ramp->ramps[0].target;
263
264     return vol;
265 }
266
267 static void set_stream_volume_limit(struct userdata *u,
268                                     pa_sink_input   *sinp,
269                                     pa_volume_t      vol,
270                                     long             ramp_time)
271 {
272     pa_sink *sink;
273     pa_cvolume_ramp rampvol;
274
275     pa_assert(u);
276     pa_assert(sinp);
277     pa_assert_se((sink = sinp->sink));
278
279     if (!ramp_time) {
280         pa_cvolume_set(&sinp->volume_factor, sinp->volume.channels, vol);
281
282         if (pa_sink_flat_volume_enabled(sink)) {
283             pa_sink_set_volume(sink, NULL, true, false);
284         }
285         else {
286             pa_sw_cvolume_multiply(&sinp->soft_volume, &sinp->real_ratio,
287                                    &sinp->volume_factor);
288
289             pa_asyncmsgq_send(sink->asyncmsgq, PA_MSGOBJECT(sinp),
290                               PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME,
291                               NULL, 0, NULL);
292         }
293     }
294     else {
295         pa_cvolume_ramp_set(&rampvol,
296                             sinp->volume.channels,
297                             PA_VOLUME_RAMP_TYPE_LINEAR,
298                             ramp_time,
299                             vol);
300
301         pa_sink_input_set_volume_ramp(sinp, &rampvol, true, false);
302     }
303 }
304
305
306 /*
307  * Local Variables:
308  * c-basic-offset: 4
309  * indent-tabs-mode: nil
310  * End:
311  *
312  */