check for NULL origin
[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 (origin == NULL) {
122                     pa_log_debug("could not find origin for sink-input %d", sinp->index);
123                 }
124                 else if ((class = pa_utils_get_stream_class(sinp->proplist)) > 0) {
125
126                     corked = stream_node ? !stream_node->rset.grant : false;
127                     muted  = (sinp->muted  || pa_hashmap_get(sinp->volume_factor_items,
128                                                              "internal_mute"));
129                     if (origin != sinp) {
130                         muted |= (origin->muted || pa_hashmap_get(origin->volume_factor_items,
131                                                                   "internal_mute"));
132                     }
133
134                     if (!corked && !muted)
135                         mask |= mir_volume_get_class_mask(class);
136
137                     pa_log_debug("*** stream %u (origin %u) class: %d corked: %s muted: %s "
138                                  "(sinp:%s internal:%s)", sinp->index, origin->index,
139                                  class, corked?"yes":"no ",
140                            muted?"yes":"no", sinp->muted ? "yes":"no",
141                            pa_hashmap_get(sinp->volume_factor_items,"internal_mute")?"yes":"no");
142                 }
143                 else
144                     pa_log_debug("*** steam %u (origin %u) class: %d", sinp->index, origin->index, class);
145             }
146
147             pa_log_debug("*** mask: 0x%x", mask);
148
149             PA_IDXSET_FOREACH(sinp, sink->inputs, j) {
150                 class = pa_utils_get_stream_class(sinp->proplist);
151
152                 pa_log_debug("     stream %u (class %u)", sinp->index, class);
153
154                 if (!class) {
155                     if (!(sinp->flags & PA_SINK_INPUT_START_RAMP_MUTED))
156                         pa_log_debug("        skipping");
157                     else {
158                         sinp->flags &= ~((unsigned int)PA_SINK_INPUT_START_RAMP_MUTED);
159                         time = transit->fade_in;
160
161                         pa_log_debug("        attenuation 0 dB "
162                                      "transition time %ld ms", time);
163                         set_stream_volume_limit(u, sinp, PA_VOLUME_NORM, time);
164                     }
165                 }
166                 else {
167                     dB = mir_volume_apply_limits(u, device_node, mask, class, stamp);
168                     newvol = pa_sw_volume_from_dB(dB);
169
170                     if (rampit) {
171                         ramp   = &sinp->ramp;
172                         oldvol = ramp->ramps[0].target;
173
174                         if (oldvol > newvol)
175                             time = transit->fade_out;
176                         else if (oldvol < newvol)
177                             time = transit->fade_in;
178                         else
179                             time = 0;
180                     }
181                     else {
182                         oldvol = sinp->volume_factor.values[0];
183                         time = 0;
184                     }
185
186                     if (oldvol == newvol)
187                         pa_log_debug("         attenuation %.2lf dB",dB);
188                     else {
189                         pa_log_debug("         attenuation %.2lf dB "
190                                      "transition time %ld ms", dB, time);
191                         set_stream_volume_limit(u, sinp, newvol, time);
192                     }
193                 }
194             } /* PA_IDXSET_FOREACH sinp */
195         }
196     } /* PA_IDXSET_FOREACH sink */
197 }
198
199 void pa_fader_ramp_volume(struct userdata *u,
200                           pa_sink_input *sinp,
201                           pa_volume_t newvol)
202 {
203     transition_time      *transit;
204     bool                  rampit;
205     pa_volume_t           oldvol;
206     pa_cvolume_ramp_int  *ramp;
207     long                  time;
208     pa_cvolume_ramp       rampvol;
209
210     pa_assert(u);
211     pa_assert(u->fader);
212     pa_assert(sinp);
213
214     transit = &u->fader->transit;
215     rampit  = transit->fade_in > 0 &&  transit->fade_out > 0;
216     ramp    = &sinp->ramp;
217     oldvol  = ramp->ramps[0].target;
218
219     if (rampit && oldvol != newvol) {
220         time = (oldvol > newvol) ? transit->fade_out : transit->fade_in;
221
222         pa_cvolume_ramp_set(&rampvol,
223                             sinp->volume.channels,
224                             PA_VOLUME_RAMP_TYPE_LINEAR,
225                             time, newvol);
226
227         pa_sink_input_set_volume_ramp(sinp, &rampvol, true, false);
228     }
229 }
230
231
232 void pa_fader_set_volume(struct userdata *u,
233                           pa_sink_input *sinp,
234                           pa_volume_t newvol)
235 {
236     pa_volume_t oldvol;
237     pa_cvolume_ramp_int *ramp;
238     pa_cvolume_ramp  rampvol;
239
240     pa_assert(u);
241     pa_assert(sinp);
242
243     ramp   = &sinp->ramp;
244     oldvol = ramp->ramps[0].target;
245
246     if (oldvol != newvol) {
247         pa_cvolume_ramp_set(&rampvol,
248                             sinp->volume.channels,
249                             PA_VOLUME_RAMP_TYPE_LINEAR,
250                             0, newvol);
251
252         pa_sink_input_set_volume_ramp(sinp, &rampvol, true, false);
253     }
254 }
255
256 pa_volume_t pa_fader_get_volume(struct userdata *u, pa_sink_input *sinp)
257 {
258     pa_cvolume_ramp_int *ramp;
259     pa_volume_t vol;
260
261     pa_assert(u);
262     pa_assert(sinp);
263
264     ramp = &sinp->ramp;
265     vol  = ramp->ramps[0].target;
266
267     return vol;
268 }
269
270 static void set_stream_volume_limit(struct userdata *u,
271                                     pa_sink_input   *sinp,
272                                     pa_volume_t      vol,
273                                     long             ramp_time)
274 {
275     pa_sink *sink;
276     pa_cvolume_ramp rampvol;
277
278     pa_assert(u);
279     pa_assert(sinp);
280     pa_assert_se((sink = sinp->sink));
281
282     if (!ramp_time) {
283         pa_cvolume_set(&sinp->volume_factor, sinp->volume.channels, vol);
284
285         if (pa_sink_flat_volume_enabled(sink)) {
286             pa_sink_set_volume(sink, NULL, true, false);
287         }
288         else {
289             pa_sw_cvolume_multiply(&sinp->soft_volume, &sinp->real_ratio,
290                                    &sinp->volume_factor);
291
292             pa_asyncmsgq_send(sink->asyncmsgq, PA_MSGOBJECT(sinp),
293                               PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME,
294                               NULL, 0, NULL);
295         }
296     }
297     else {
298         pa_cvolume_ramp_set(&rampvol,
299                             sinp->volume.channels,
300                             PA_VOLUME_RAMP_TYPE_LINEAR,
301                             ramp_time,
302                             vol);
303
304         pa_sink_input_set_volume_ramp(sinp, &rampvol, true, false);
305     }
306 }
307
308
309 /*
310  * Local Variables:
311  * c-basic-offset: 4
312  * indent-tabs-mode: nil
313  * End:
314  *
315  */