routing: add loopback support to nodes
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / multiplex.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 <strings.h>
23
24 #include <pulsecore/pulsecore-config.h>
25
26 #include <pulse/def.h>
27 #include <pulsecore/thread.h>
28 #include <pulsecore/strlist.h>
29 #include <pulsecore/time-smoother.h>
30 #include <pulsecore/sink.h>
31 #include <pulsecore/sink-input.h>
32
33 #include <combine/userdata.h>
34
35 #include "multiplex.h"
36 #include "utils.h"
37
38 #ifndef DEFAULT_RESAMPLER
39 #define DEFAULT_RESAMPLER "speex-float-3"
40 #endif
41
42
43 pa_multiplex *pa_multiplex_init(void)
44 {
45     pa_multiplex *multiplex = pa_xnew0(pa_multiplex, 1);
46
47     return multiplex;
48 }
49
50
51 void pa_multiplex_done(pa_multiplex *multiplex, pa_core *core)
52 {
53     pa_muxnode *mux, *n;
54
55     PA_LLIST_FOREACH_SAFE(mux,n, multiplex->muxnodes) {
56         pa_module_unload_by_index(core, mux->module_index, FALSE);
57     }
58 }
59
60
61
62 pa_muxnode *pa_multiplex_create(pa_multiplex   *multiplex,
63                                 pa_core        *core,
64                                 uint32_t        sink_index,
65                                 pa_channel_map *chmap,
66                                 const char     *resampler,
67                                 int             type)
68 {
69     static char *modnam = "module-combine-sink";
70
71     struct userdata *u;         /* combine's userdata! */
72     struct output   *o;
73     pa_muxnode      *mux;
74     pa_sink         *sink;
75     pa_sink_input   *sinp;
76     pa_module       *module;
77     char             args[512];
78     uint32_t         idx;
79
80     pa_assert(core);
81
82     if (!resampler)
83         resampler = DEFAULT_RESAMPLER;
84
85     if (!(sink = pa_idxset_get_by_index(core->sinks, sink_index))) {
86         pa_log_debug("can't find the primary sink (index %u) for multiplexer",
87                      sink_index);
88         return NULL;
89     }
90
91     snprintf(args, sizeof(args), "slaves=\"%s\" resample_method=\"%s\" "
92              "channels=%u", sink->name, resampler, chmap->channels);
93
94     if (!(module = pa_module_load(core, modnam, args))) {
95         pa_log("failed to load module '%s %s'. can't multiplex", modnam, args);
96         return NULL;
97     }
98
99     pa_assert_se((u = module->userdata));
100     pa_assert(u->sink);
101
102     u->no_reattach = TRUE;
103
104     mux = pa_xnew0(pa_muxnode, 1);
105     mux->module_index = module->index;
106     mux->sink_index = u->sink->index;
107     mux->defstream_index = PA_IDXSET_INVALID;
108
109     PA_LLIST_PREPEND(pa_muxnode, multiplex->muxnodes, mux);
110
111     if (!(o = pa_idxset_first(u->outputs, &idx)))
112         pa_log("can't find default multiplexer stream");
113     else {
114         if ((sinp = o->sink_input)) {
115             pa_utils_set_stream_routing_properties(sinp->proplist, type, NULL);
116             mux->defstream_index = sinp->index;
117         }
118     }
119
120     pa_log_debug("multiplexer succesfully loaded");
121
122     return mux;
123 }
124
125
126 void pa_multiplex_destroy(pa_multiplex *multiplex,
127                           pa_core      *core,
128                           pa_muxnode   *mux)
129 {
130     pa_assert(multiplex);
131     pa_assert(core);
132
133     if (mux) {
134         PA_LLIST_REMOVE(pa_muxnode, multiplex->muxnodes, mux);
135         pa_module_unload_by_index(core, mux->module_index, FALSE);
136         pa_xfree(mux);
137     }
138 }
139
140
141 pa_muxnode *pa_multiplex_find(pa_multiplex *multiplex, uint32_t sink_index)
142 {
143     pa_muxnode *mux;
144
145     PA_LLIST_FOREACH(mux, multiplex->muxnodes) {
146         if (sink_index == mux->sink_index) {
147             pa_log_debug("muxnode found for sink %u", sink_index);
148             return mux;
149         }
150     }
151
152     pa_log_debug("can't find muxnode for sink %u", sink_index);
153
154     return NULL;
155 }
156
157
158 pa_bool_t pa_multiplex_add_default_route(pa_core    *core,
159                                          pa_muxnode *mux,
160                                          pa_sink    *sink,
161                                          int         type)
162 {
163     pa_module *module;
164     pa_sink_input *sinp;
165     struct userdata *u;         /* combine's userdata! */
166
167     pa_assert(core);
168     pa_assert(mux);
169     pa_assert(sink);
170
171     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
172         pa_log("module %u is gone", mux->module_index);
173     else {
174         pa_assert_se((u = module->userdata));
175
176         if (sink == u->sink) {
177             pa_log("%s: mux %d refuses to make a loopback to itself",
178                    __FILE__, mux->module_index);
179         }
180         else {
181             pa_log_debug("adding default route to mux %u", mux->module_index);
182
183             if (!(sinp = u->add_slave(u, sink))) {
184                 pa_log("failed to add new slave to mux %u", mux->module_index);
185                 return FALSE;
186             }
187
188             pa_utils_set_stream_routing_properties(sinp->proplist, type, NULL);
189             mux->defstream_index = sinp->index;
190
191             return TRUE;
192         }
193     }
194
195     return FALSE;
196 }
197
198 pa_bool_t pa_multiplex_remove_default_route(pa_core *core,
199                                             pa_muxnode *mux,
200                                             pa_bool_t transfer_to_explicit)
201 {
202     pa_module       *module;
203     pa_sink_input   *sinp;
204     uint32_t         idx;
205     struct userdata *u;         /* combine's userdata! */
206
207     pa_assert(core);
208     pa_assert(mux);
209
210     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
211         pa_log("module %u is gone", mux->module_index);
212     else if ((idx = mux->defstream_index) == PA_IDXSET_INVALID)
213         pa_log_debug("mux %u do not have default stream", mux->module_index);
214     else if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, idx)))
215         pa_log("can't remove default route: sink-input %u is gone", idx);
216     else {
217         pa_assert_se((u = module->userdata));
218         mux->defstream_index = PA_IDXSET_INVALID;
219
220         if (transfer_to_explicit) {
221             pa_log_debug("converting default route sink-input.%u -> sink.%u "
222                          "to explicit", sinp->index, sinp->sink->index);
223             pa_utils_set_stream_routing_method_property(sinp->proplist, TRUE);
224             return TRUE;
225         }
226         else {
227             u->remove_slave(u, sinp, NULL);
228         }
229     }
230
231     return FALSE;
232 }
233
234 pa_bool_t pa_multiplex_change_default_route(pa_core    *core,
235                                             pa_muxnode *mux,
236                                             pa_sink    *sink)
237 {
238     pa_module       *module;
239     pa_sink_input   *sinp;
240     uint32_t         idx;
241     struct userdata *u;         /* combine's userdata! */
242
243     pa_assert(core);
244     pa_assert(mux);
245     pa_assert(sink);
246
247     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
248         pa_log("module %u is gone", mux->module_index);
249     else if ((idx = mux->defstream_index) == PA_IDXSET_INVALID)
250         pa_log_debug("mux %u do not have default stream", mux->module_index);
251     else if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, idx)))
252         pa_log("can't remove default route: sink-input %u is gone", idx);
253     else {
254         pa_assert_se((u = module->userdata));
255         if (!u->move_slave(u, sinp, sink))
256             pa_log_debug("failed to move default stream on mux %u", mux->module_index);
257         else {
258             pa_log_debug("default stream was successfully moved on mux %u",
259                          mux->module_index);
260             return TRUE;
261         }
262     }
263
264     return FALSE;
265 }
266
267
268 pa_bool_t pa_multiplex_add_explicit_route(pa_core    *core,
269                                           pa_muxnode *mux,
270                                           pa_sink    *sink,
271                                           int         type)
272 {
273     pa_module *module;
274     pa_sink_input *sinp;
275     struct userdata *u;         /* combine's userdata! */
276
277     pa_assert(core);
278     pa_assert(mux);
279     pa_assert(sink);
280
281     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
282         pa_log("module %u is gone", mux->module_index);
283     else {
284         pa_assert_se((u = module->userdata));
285
286         if (sink == u->sink) {
287             pa_log("%s: mux %d refuses to make a loopback to itself",
288                    __FILE__, mux->module_index);
289         }
290         else {
291             pa_log_debug("adding explicit route to mux %u", mux->module_index);
292
293             if (!(sinp = u->add_slave(u, sink))) {
294                 pa_log("failed to add new slave to mux %u", mux->module_index);
295                 return FALSE;
296             }
297
298             pa_utils_set_stream_routing_properties(sinp->proplist, type, sink);
299
300             return TRUE;
301         }
302     }
303
304     return FALSE;
305 }
306
307
308 pa_bool_t pa_multiplex_remove_explicit_route(pa_core    *core,
309                                              pa_muxnode *mux,
310                                              pa_sink    *sink)
311 {
312     pa_module *module;
313     struct userdata *u;         /* combine's userdata! */
314
315     pa_assert(core);
316     pa_assert(mux);
317     pa_assert(sink);
318
319     if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
320         pa_log("module %u is gone", mux->module_index);
321     else {
322         pa_assert_se((u = module->userdata));
323
324         u->remove_slave(u, NULL, sink);
325
326         pa_log_debug("link to sink.%u removed", sink->index);
327
328         return TRUE;
329     }
330
331     return FALSE;
332 }
333
334
335 pa_bool_t pa_multiplex_duplicate_route(pa_core       *core,
336                                        pa_muxnode    *mux,
337                                        pa_sink_input *sinp,
338                                        pa_sink       *sink)
339 {
340     pa_module       *module;
341     struct userdata *u;   /* combine's userdata! */
342     struct output   *o;
343     uint32_t         idx;
344     pa_sink_input   *i;
345
346     pa_assert(core);
347     pa_assert(mux);
348     pa_assert(sink);
349
350     pa_log_debug("check for duplicate route on mux %u",
351                  mux->module_index);
352
353     if (!(module = pa_idxset_get_by_index(core->modules,mux->module_index)))
354         pa_log("module %u is gone", mux->module_index);
355     else {
356         pa_assert_se((u = module->userdata));
357
358         PA_IDXSET_FOREACH(o, u->outputs, idx) {
359             if (!(i = o->sink_input))
360                 continue;
361             if (sinp && i == sinp)
362                 continue;
363             if (i->sink == sink) {
364                 pa_log_debug("route sink-input.%u -> sink.%u is a duplicate",
365                              i->index, sink->index);
366                 return TRUE;
367             }
368         }
369
370         if (!sinp)
371             pa_log_debug("no duplicate route found to sink.%u", sink->index);
372         else {
373             pa_log_debug("no duplicate found for route sink-input.%u -> "
374                          "sink.%u", sinp->index, sink->index);
375         }
376     }
377         
378     return FALSE;
379 }
380
381 int pa_multiplex_no_of_routes(pa_core *core, pa_muxnode *mux)
382 {
383     pa_module       *module;
384     struct userdata *u;   /* combine's userdata! */
385
386     pa_assert(core);
387     pa_assert(mux);
388
389     if (!(module = pa_idxset_get_by_index(core->modules,mux->module_index))) {
390         pa_log("module %u is gone", mux->module_index);
391         return -1;
392     }
393
394     pa_assert_se((u = module->userdata));
395
396     return (int)pa_idxset_size(u->outputs);
397 }
398
399
400 int pa_multiplex_print(pa_muxnode *mux, char *buf, int len)
401 {
402     char *p, *e;
403
404     pa_assert(buf);
405     pa_assert(len > 0);
406
407     e = (p = buf) + len;
408
409     if (!mux)
410         p += snprintf(p, e-p, "<not set>");
411     else {
412         p += snprintf(p, e-p, "module %u, sink %u, default stream %u",
413                       mux->module_index, mux->sink_index,mux->defstream_index);
414     }
415     
416     return p - buf;
417 }
418
419
420 /*
421  * Local Variables:
422  * c-basic-offset: 4
423  * indent-tabs-mode: nil
424  * End:
425  *
426  */
427