5c5aea7e791375ad4791efde7939d1d516708522
[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     uint32_t fade_out;
40     uint32_t 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, uint32_t);
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_atou(fade_out_str, &fader->transit.fade_out) < 0)
56         fader->transit.fade_out = 100;
57
58     if (!fade_in_str || pa_atou(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 %u ms, in %u 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;
88     pa_cvolume_ramp_int  *ramp;
89     mir_node        *node;
90     double           dB;
91     pa_volume_t      newvol;
92     pa_volume_t      oldvol;
93     uint32_t         time;
94     uint32_t         i,j;
95     int              class;
96     bool        rampit;
97
98     pa_assert(u);
99     pa_assert_se(u->fader);
100     pa_assert_se((core = u->core));
101
102     transit = &u->fader->transit;
103     rampit  = transit->fade_in > 0 &&  transit->fade_out > 0;
104
105     pa_log_debug("applying volume limits ...");
106
107     PA_IDXSET_FOREACH(sink, core->sinks, i) {
108         if ((node = pa_discover_find_node_by_ptr(u, sink))) {
109             pa_log_debug("   node '%s'", node->amname);
110             
111             PA_IDXSET_FOREACH(sinp, sink->inputs, j) {
112                 class = pa_utils_get_stream_class(sinp->proplist);
113
114                 pa_log_debug("     stream %u (class %u)", sinp->index, class);
115
116                 if (!class) {
117                     if (!(sinp->flags & PA_SINK_INPUT_START_RAMP_MUTED))
118                         pa_log_debug("        skipping");
119                     else {
120                         sinp->flags &= ~PA_SINK_INPUT_START_RAMP_MUTED;
121                         time = transit->fade_in;
122                         
123                         pa_log_debug("        attenuation 0 dB "
124                                      "transition time %u ms", time);
125                         set_stream_volume_limit(u, sinp, PA_VOLUME_NORM, time);
126                     }
127                 }
128                 else {
129                     dB = mir_volume_apply_limits(u, node, class, stamp);
130                     newvol = pa_sw_volume_from_dB(dB);
131
132                     if (rampit) {
133                         ramp   = &sinp->ramp;
134                         oldvol = ramp->ramps[0].target;
135                         
136                         if (oldvol > newvol)
137                             time = transit->fade_out;
138                         else if (oldvol < newvol)
139                             time = transit->fade_in;
140                         else
141                             time = 0;
142                     }
143                     else {
144                         oldvol = sinp->volume_factor.values[0];
145                         time = 0;
146                     }
147                     
148                     if (oldvol == newvol)
149                         pa_log_debug("         attenuation %.2lf dB",dB);
150                     else {
151                         pa_log_debug("         attenuation %.2lf dB "
152                                      "transition time %u ms", dB, time);
153                         set_stream_volume_limit(u, sinp, newvol, time);
154                     }
155                 }
156             } /* PA_IDXSET_FOREACH sinp */
157         }
158     } /* PA_IDXSET_FOREACH sink */
159 }
160
161 void pa_fader_ramp_volume(struct userdata *u,
162                           pa_sink_input *sinp,
163                           pa_volume_t newvol)
164 {
165     transition_time *transit;
166     bool             rampit;
167     pa_volume_t      oldvol;
168     pa_cvolume_ramp_int  *ramp;
169     uint32_t         time;
170     pa_cvolume_ramp  rampvol;
171
172     pa_assert(u);
173     pa_assert(u->fader);
174     pa_assert(sinp);
175
176     transit = &u->fader->transit;
177     rampit  = transit->fade_in > 0 &&  transit->fade_out > 0;
178     ramp    = &sinp->ramp;
179     oldvol  = ramp->ramps[0].target;
180
181     if (rampit && oldvol != newvol) {
182         time = (oldvol > newvol) ? transit->fade_out : transit->fade_in;
183
184         pa_cvolume_ramp_set(&rampvol,
185                             sinp->volume.channels,
186                             PA_VOLUME_RAMP_TYPE_LINEAR,
187                             time, newvol);
188
189         pa_sink_input_set_volume_ramp(sinp, &rampvol, true, false);
190     }
191 }
192
193
194 void pa_fader_set_volume(struct userdata *u,
195                           pa_sink_input *sinp,
196                           pa_volume_t newvol)
197 {
198     pa_volume_t oldvol;
199     pa_cvolume_ramp_int *ramp;
200     pa_cvolume_ramp  rampvol;
201
202     pa_assert(u);
203     pa_assert(sinp);
204
205     ramp   = &sinp->ramp;
206     oldvol = ramp->ramps[0].target;
207
208     if (oldvol != newvol) {
209         pa_cvolume_ramp_set(&rampvol,
210                             sinp->volume.channels,
211                             PA_VOLUME_RAMP_TYPE_LINEAR,
212                             0, newvol);
213
214         pa_sink_input_set_volume_ramp(sinp, &rampvol, true, false);
215     }
216 }
217
218 pa_volume_t pa_fader_get_volume(struct userdata *u, pa_sink_input *sinp)
219 {
220     pa_cvolume_ramp_int *ramp;
221     pa_volume_t vol;
222
223     pa_assert(u);
224     pa_assert(sinp);
225
226     ramp = &sinp->ramp;
227     vol  = ramp->ramps[0].target;
228
229     return vol;
230 }
231
232 static void set_stream_volume_limit(struct userdata *u,
233                                     pa_sink_input   *sinp,
234                                     pa_volume_t      vol,
235                                     uint32_t         ramp_time)
236 {
237     pa_sink *sink;
238     pa_cvolume_ramp rampvol;
239
240     pa_assert(u);
241     pa_assert(sinp);
242     pa_assert_se((sink = sinp->sink));
243
244     if (!ramp_time) {
245         pa_cvolume_set(&sinp->volume_factor, sinp->volume.channels, vol);
246
247         if (pa_sink_flat_volume_enabled(sink)) {
248             pa_sink_set_volume(sink, NULL, true, false);
249         }
250         else {
251             pa_sw_cvolume_multiply(&sinp->soft_volume, &sinp->real_ratio,
252                                    &sinp->volume_factor);
253
254             pa_asyncmsgq_send(sink->asyncmsgq, PA_MSGOBJECT(sinp),
255                               PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME,
256                               NULL, 0, NULL);
257         }
258     }
259     else {
260         pa_cvolume_ramp_set(&rampvol,
261                             sinp->volume.channels,
262                             PA_VOLUME_RAMP_TYPE_LINEAR,
263                             ramp_time,
264                             vol);
265
266         pa_sink_input_set_volume_ramp(sinp, &rampvol, true, false);
267     }
268 }
269
270
271 /*
272  * Local Variables:
273  * c-basic-offset: 4
274  * indent-tabs-mode: nil
275  * End:
276  *
277  */