Fix up according to Coding Style
[pulseaudio-mirror.git] / src / modules / module-virtual-source.c
blobb8f2ab063d4e7cd21a3388b5925ef43c39bc1c5d
1 /***
2 This file is part of PulseAudio.
4 Copyright 2010 Intel Corporation
5 Contributor: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <stdio.h>
28 #include <math.h>
30 #include <pulse/xmalloc.h>
31 #include <pulse/i18n.h>
33 #include <pulsecore/macro.h>
34 #include <pulsecore/core-error.h>
35 #include <pulsecore/namereg.h>
36 #include <pulsecore/sink.h>
37 #include <pulsecore/module.h>
38 #include <pulsecore/core-rtclock.h>
39 #include <pulsecore/core-util.h>
40 #include <pulsecore/core-error.h>
41 #include <pulsecore/modargs.h>
42 #include <pulsecore/log.h>
43 #include <pulsecore/thread.h>
44 #include <pulsecore/thread-mq.h>
45 #include <pulsecore/rtpoll.h>
46 #include <pulsecore/sample-util.h>
47 #include <pulsecore/ltdl-helper.h>
49 #include "module-virtual-source-symdef.h"
51 PA_MODULE_AUTHOR("Pierre-Louis Bossart");
52 PA_MODULE_DESCRIPTION("Virtual source");
53 PA_MODULE_VERSION(PACKAGE_VERSION);
54 PA_MODULE_LOAD_ONCE(FALSE);
55 PA_MODULE_USAGE(
56 _("source_name=<name for the source> "
57 "source_properties=<properties for the source> "
58 "master=<name of source to filter> "
59 "uplink_sink=<name> (optional)"
60 "format=<sample format> "
61 "rate=<sample rate> "
62 "channels=<number of channels> "
63 "channel_map=<channel map> "
64 ));
66 #define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
67 #define BLOCK_USEC 1000 /* FIXME */
69 struct userdata {
70 pa_module *module;
72 pa_source *source;
73 pa_source_output *source_output;
75 pa_memblockq *memblockq;
77 pa_bool_t auto_desc;
78 unsigned channels;
80 /* optional fields for uplink sink */
81 pa_sink *sink;
82 pa_usec_t block_usec;
83 pa_memblockq *sink_memblockq;
87 static const char* const valid_modargs[] = {
88 "source_name",
89 "source_properties",
90 "master",
91 "uplink_sink",
92 "format",
93 "rate",
94 "channels",
95 "channel_map",
96 NULL
99 /* Called from I/O thread context */
100 static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
102 switch (code) {
104 case PA_SINK_MESSAGE_GET_LATENCY:
106 /* there's no real latency here */
107 *((pa_usec_t*) data) = 0;
109 return 0;
112 return pa_sink_process_msg(o, code, data, offset, chunk);
115 /* Called from main context */
116 static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
117 struct userdata *u;
119 pa_sink_assert_ref(s);
120 pa_assert_se(u = s->userdata);
122 if (!PA_SINK_IS_LINKED(state)) {
123 return 0;
126 if (state == PA_SINK_RUNNING) {
127 /* need to wake-up source if it was suspended */
128 pa_source_suspend(u->source, FALSE, PA_SUSPEND_ALL);
130 /* FIXME: if there's no client connected, the source will suspend
131 and playback will be stuck. You'd want to prevent the source from
132 sleeping when the uplink sink is active; even if the audio is
133 discarded at least the app isn't stuck */
135 } else {
136 /* nothing to do, if the sink becomes idle or suspended let
137 module-suspend-idle handle the sources later */
140 return 0;
143 static void sink_update_requested_latency_cb(pa_sink *s) {
144 struct userdata *u;
146 pa_sink_assert_ref(s);
147 pa_assert_se(u = s->userdata);
149 /* FIXME: there's no latency support */
154 /* Called from I/O thread context */
155 static void sink_request_rewind_cb(pa_sink *s) {
156 struct userdata *u;
158 pa_sink_assert_ref(s);
159 pa_assert_se(u = s->userdata);
161 /* Do nothing */
162 pa_sink_process_rewind(u->sink, 0);
166 /* Called from I/O thread context */
167 static int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
168 struct userdata *u = PA_SOURCE(o)->userdata;
170 switch (code) {
172 case PA_SOURCE_MESSAGE_GET_LATENCY:
174 /* The source is _put() before the source output is, so let's
175 * make sure we don't access it in that time. Also, the
176 * source output is first shut down, the source second. */
177 if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
178 !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) {
179 *((pa_usec_t*) data) = 0;
180 return 0;
183 *((pa_usec_t*) data) =
185 /* Get the latency of the master source */
186 pa_source_get_latency_within_thread(u->source_output->source) +
188 /* Add the latency internal to our source output on top */
189 /* FIXME, no idea what I am doing here */
190 pa_bytes_to_usec(pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq), &u->source_output->source->sample_spec);
192 return 0;
195 return pa_source_process_msg(o, code, data, offset, chunk);
198 /* Called from main context */
199 static int source_set_state_cb(pa_source *s, pa_source_state_t state) {
200 struct userdata *u;
202 pa_source_assert_ref(s);
203 pa_assert_se(u = s->userdata);
205 if (!PA_SOURCE_IS_LINKED(state) ||
206 !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
207 return 0;
209 pa_source_output_cork(u->source_output, state == PA_SOURCE_SUSPENDED);
210 return 0;
213 /* Called from I/O thread context */
214 static void source_update_requested_latency_cb(pa_source *s) {
215 struct userdata *u;
217 pa_source_assert_ref(s);
218 pa_assert_se(u = s->userdata);
220 if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) ||
221 !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state))
222 return;
224 /* Just hand this one over to the master source */
225 pa_source_output_set_requested_latency_within_thread(
226 u->source_output,
227 pa_source_get_requested_latency_within_thread(s));
230 /* Called from main context */
231 static void source_set_volume_cb(pa_source *s) {
232 struct userdata *u;
234 pa_source_assert_ref(s);
235 pa_assert_se(u = s->userdata);
237 if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
238 !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
239 return;
241 /* FIXME, no volume control in source_output, set volume at the master */
242 pa_source_set_volume(u->source_output->source, &s->volume, TRUE);
245 static void source_get_volume_cb(pa_source *s) {
246 struct userdata *u;
248 pa_source_assert_ref(s);
249 pa_assert_se(u = s->userdata);
251 if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
252 !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
253 return;
255 /* FIXME, no volume control in source_output, get the info from the master */
256 pa_source_get_volume(u->source_output->source, TRUE);
258 if (pa_cvolume_equal(&s->volume,&u->source_output->source->volume))
259 /* no change */
260 return;
262 s->volume = u->source_output->source->volume;
263 pa_source_set_soft_volume(s, NULL);
267 /* Called from main context */
268 static void source_set_mute_cb(pa_source *s) {
269 struct userdata *u;
271 pa_source_assert_ref(s);
272 pa_assert_se(u = s->userdata);
274 if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
275 !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
276 return;
278 /* FIXME, no volume control in source_output, set mute at the master */
279 pa_source_set_mute(u->source_output->source, TRUE, TRUE);
282 /* Called from main context */
283 static void source_get_mute_cb(pa_source *s) {
284 struct userdata *u;
286 pa_source_assert_ref(s);
287 pa_assert_se(u = s->userdata);
289 if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
290 !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
291 return;
293 /* FIXME, no volume control in source_output, get the info from the master */
294 pa_source_get_mute(u->source_output->source, TRUE);
297 /* Called from input thread context */
298 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
299 struct userdata *u;
301 pa_source_output_assert_ref(o);
302 pa_source_output_assert_io_context(o);
303 pa_assert_se(u = o->userdata);
305 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) {
306 pa_log("push when no link?");
307 return;
310 /* PUT YOUR CODE HERE TO DO SOMETHING WITH THE SOURCE DATA */
312 /* if uplink sink exists, pull data from there; simplify by using
313 same length as chunk provided by source */
314 if(u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) {
315 pa_memchunk tchunk;
316 size_t nbytes = chunk->length;
317 pa_mix_info streams[2];
318 pa_memchunk target_chunk;
319 void *target;
320 int ch;
322 /* Hmm, process any rewind request that might be queued up */
323 pa_sink_process_rewind(u->sink, 0);
325 /* get data from the sink */
326 while (pa_memblockq_peek(u->sink_memblockq, &tchunk) < 0) {
327 pa_memchunk nchunk;
329 /* make sure we get nbytes from the sink with render_full,
330 otherwise we cannot mix with the uplink */
331 pa_sink_render_full(u->sink, nbytes, &nchunk);
332 pa_memblockq_push(u->sink_memblockq, &nchunk);
333 pa_memblock_unref(nchunk.memblock);
335 pa_assert(tchunk.length == chunk->length);
337 /* move the read pointer for sink memblockq */
338 pa_memblockq_drop(u->sink_memblockq, tchunk.length);
340 /* allocate target chunk */
341 /* this could probably be done in-place, but having chunk as both
342 the input and output creates issues with reference counts */
343 target_chunk.index = 0;
344 target_chunk.length = chunk->length;
345 pa_assert(target_chunk.length == chunk->length);
347 target_chunk.memblock = pa_memblock_new(o->source->core->mempool,
348 target_chunk.length);
349 pa_assert( target_chunk.memblock );
351 /* get target pointer */
352 target = (void*)((uint8_t*)pa_memblock_acquire(target_chunk.memblock)
353 + target_chunk.index);
355 /* set-up mixing structure
356 volume was taken care of in sink and source already */
357 streams[0].chunk = *chunk;
358 for(ch=0;ch<o->sample_spec.channels;ch++)
359 streams[0].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
360 streams[0].volume.channels = o->sample_spec.channels;
362 streams[1].chunk = tchunk;
363 for(ch=0;ch<o->sample_spec.channels;ch++)
364 streams[1].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
365 streams[1].volume.channels = o->sample_spec.channels;
367 /* do mixing */
368 pa_mix(streams, /* 2 streams to be mixed */
370 target, /* put result in target chunk */
371 chunk->length, /* same length as input */
372 (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */
373 NULL, /* no volume information */
374 FALSE); /* no mute */
376 pa_memblock_release(target_chunk.memblock);
377 pa_memblock_unref(tchunk.memblock); /* clean-up */
379 /* forward the data to the virtual source */
380 pa_source_post(u->source, &target_chunk);
382 pa_memblock_unref(target_chunk.memblock); /* clean-up */
384 } else {
385 /* forward the data to the virtual source */
386 pa_source_post(u->source, chunk);
392 /* Called from input thread context */
393 static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) {
394 struct userdata *u;
396 pa_source_output_assert_ref(o);
397 pa_source_output_assert_io_context(o);
398 pa_assert_se(u = o->userdata);
400 /* FIXME, no idea what I am doing here */
401 #if 0
402 pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL);
403 u->send_counter -= (int64_t) nbytes;
404 #endif
407 /* Called from output thread context */
408 static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
410 /* FIXME, nothing to do here ? */
412 return pa_source_output_process_msg(obj, code, data, offset, chunk);
415 /* Called from output thread context */
416 static void source_output_attach_cb(pa_source_output *o) {
417 struct userdata *u;
419 pa_source_output_assert_ref(o);
420 pa_source_output_assert_io_context(o);
421 pa_assert_se(u = o->userdata);
423 pa_source_set_rtpoll(u->source, o->source->thread_info.rtpoll);
424 pa_source_set_latency_range_within_thread(u->source, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
425 pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency);
426 pa_source_set_max_rewind_within_thread(u->source, pa_source_output_get_max_rewind(o));
428 pa_source_attach_within_thread(u->source);
431 /* Called from output thread context */
432 static void source_output_detach_cb(pa_source_output *o) {
433 struct userdata *u;
435 pa_source_output_assert_ref(o);
436 pa_source_output_assert_io_context(o);
437 pa_assert_se(u = o->userdata);
439 pa_source_detach_within_thread(u->source);
440 pa_source_set_rtpoll(u->source, NULL);
443 /* Called from output thread context */
444 static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) {
445 struct userdata *u;
447 pa_source_output_assert_ref(o);
448 pa_source_output_assert_io_context(o);
449 pa_assert_se(u = o->userdata);
451 /* FIXME */
452 #if 0
453 if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT) {
455 u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source),
456 u->latency),
457 &o->sample_spec);
459 pa_log_info("Skipping %lu bytes", (unsigned long) u->skip);
461 #endif
464 /* Called from main thread */
465 static void source_output_kill_cb(pa_source_output *o) {
466 struct userdata *u;
468 pa_source_output_assert_ref(o);
469 pa_assert_ctl_context();
470 pa_assert_se(u = o->userdata);
472 /* The order here matters! We first kill the source output, followed
473 * by the source. That means the source callbacks must be protected
474 * against an unconnected source output! */
475 pa_source_output_unlink(u->source_output);
476 pa_source_unlink(u->source);
478 pa_source_output_unref(u->source_output);
479 u->source_output = NULL;
481 pa_source_unref(u->source);
482 u->source = NULL;
484 pa_module_unload_request(u->module, TRUE);
487 /* Called from main thread */
488 static pa_bool_t source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) {
489 struct userdata *u;
491 pa_source_output_assert_ref(o);
492 pa_assert_ctl_context();
493 pa_assert_se(u = o->userdata);
495 /* FIXME */
496 //return dest != u->source_input->source->monitor_source;
498 return TRUE;
501 /* Called from main thread */
502 static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
503 struct userdata *u;
505 pa_source_output_assert_ref(o);
506 pa_assert_ctl_context();
507 pa_assert_se(u = o->userdata);
509 if (dest) {
510 pa_source_set_asyncmsgq(u->source, dest->asyncmsgq);
511 pa_source_update_flags(u->source, PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY, dest->flags);
512 } else
513 pa_source_set_asyncmsgq(u->source, NULL);
515 if (u->auto_desc && dest) {
516 const char *z;
517 pa_proplist *pl;
519 pl = pa_proplist_new();
520 z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION);
521 pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Virtual Source %s on %s",
522 pa_proplist_gets(u->source->proplist, "device.vsource.name"), z ? z : dest->name);
524 pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, pl);
525 pa_proplist_free(pl);
530 int pa__init(pa_module*m) {
531 struct userdata *u;
532 pa_sample_spec ss;
533 pa_channel_map map;
534 pa_modargs *ma;
535 pa_source *master=NULL;
536 pa_source_output_new_data source_output_data;
537 pa_source_new_data source_data;
539 /* optional for uplink_sink */
540 pa_sink_new_data sink_data;
541 size_t nbytes;
543 pa_assert(m);
545 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
546 pa_log("Failed to parse module arguments.");
547 goto fail;
550 if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SOURCE))) {
551 pa_log("Master source not found");
552 goto fail;
555 pa_assert(master);
557 ss = master->sample_spec;
558 ss.format = PA_SAMPLE_FLOAT32;
559 map = master->channel_map;
560 if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
561 pa_log("Invalid sample format specification or channel map");
562 goto fail;
566 u = pa_xnew0(struct userdata, 1);
567 if (!u) {
568 pa_log("Failed to alloc userdata");
569 goto fail;
571 u->module = m;
572 m->userdata = u;
573 u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
574 if (!u->memblockq) {
575 pa_log("Failed to create source memblockq.");
576 goto fail;
578 u->channels = ss.channels;
580 /* Create source */
581 pa_source_new_data_init(&source_data);
582 source_data.driver = __FILE__;
583 source_data.module = m;
584 if (!(source_data.name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
585 source_data.name = pa_sprintf_malloc("%s.vsource", master->name);
586 pa_source_new_data_set_sample_spec(&source_data, &ss);
587 pa_source_new_data_set_channel_map(&source_data, &map);
588 pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
589 pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
590 pa_proplist_sets(source_data.proplist, "device.vsource.name", source_data.name);
592 if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) {
593 pa_log("Invalid properties");
594 pa_source_new_data_done(&source_data);
595 goto fail;
598 if ((u->auto_desc = !pa_proplist_contains(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
599 const char *z;
601 z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
602 pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Source %s on %s", source_data.name, z ? z : master->name);
605 u->source = pa_source_new(m->core, &source_data,
606 PA_SOURCE_HW_MUTE_CTRL|PA_SOURCE_HW_VOLUME_CTRL|PA_SOURCE_DECIBEL_VOLUME|
607 (master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY)));
608 pa_source_new_data_done(&source_data);
610 if (!u->source) {
611 pa_log("Failed to create source.");
612 goto fail;
615 u->source->parent.process_msg = source_process_msg_cb;
616 u->source->set_state = source_set_state_cb;
617 u->source->update_requested_latency = source_update_requested_latency_cb;
618 u->source->set_volume = source_set_volume_cb;
619 u->source->set_mute = source_set_mute_cb;
620 u->source->get_volume = source_get_volume_cb;
621 u->source->get_mute = source_get_mute_cb;
622 u->source->userdata = u;
624 pa_source_set_asyncmsgq(u->source, master->asyncmsgq);
626 /* Create source output */
627 pa_source_output_new_data_init(&source_output_data);
628 source_output_data.driver = __FILE__;
629 source_output_data.module = m;
630 source_output_data.source = master;
631 source_output_data.destination_source = u->source;
632 /* FIXME
633 source_output_data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; */
635 pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Source Stream of %s", pa_proplist_gets(u->source->proplist, PA_PROP_DEVICE_DESCRIPTION));
636 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
637 pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
638 pa_source_output_new_data_set_channel_map(&source_output_data, &map);
640 pa_source_output_new(&u->source_output, m->core, &source_output_data);
641 pa_source_output_new_data_done(&source_output_data);
643 if (!u->source_output)
644 goto fail;
646 u->source_output->parent.process_msg = source_output_process_msg_cb;
647 u->source_output->push = source_output_push_cb;
648 u->source_output->process_rewind = source_output_process_rewind_cb;
649 u->source_output->kill = source_output_kill_cb;
650 u->source_output->attach = source_output_attach_cb;
651 u->source_output->detach = source_output_detach_cb;
652 u->source_output->state_change = source_output_state_change_cb;
653 u->source_output->may_move_to = source_output_may_move_to_cb;
654 u->source_output->moving = source_output_moving_cb;
655 u->source_output->userdata = u;
657 u->source->output_from_master = u->source_output;
659 pa_source_put(u->source);
660 pa_source_output_put(u->source_output);
662 /* Create optional uplink sink */
663 pa_sink_new_data_init(&sink_data);
664 sink_data.driver = __FILE__;
665 sink_data.module = m;
666 if ((sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "uplink_sink", NULL)))) {
667 pa_sink_new_data_set_sample_spec(&sink_data, &ss);
668 pa_sink_new_data_set_channel_map(&sink_data, &map);
669 pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
670 pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "uplink sink");
671 pa_proplist_sets(sink_data.proplist, "device.uplink_sink.name", sink_data.name);
673 if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) {
674 const char *z;
676 z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
677 pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Uplink Sink %s on %s", sink_data.name, z ? z : master->name);
680 u->sink_memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
681 if (!u->sink_memblockq) {
682 pa_log("Failed to create sink memblockq.");
683 goto fail;
686 u->sink = pa_sink_new(m->core, &sink_data, 0); /* FIXME, sink has no capabilities */
687 pa_sink_new_data_done(&sink_data);
689 if (!u->sink) {
690 pa_log("Failed to create sink.");
691 goto fail;
694 u->sink->parent.process_msg = sink_process_msg_cb;
695 u->sink->update_requested_latency = sink_update_requested_latency_cb;
696 u->sink->request_rewind = sink_request_rewind_cb;
697 u->sink->set_state = sink_set_state_cb;
698 u->sink->userdata = u;
700 pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
702 /* FIXME: no idea what I am doing here */
703 u->block_usec = BLOCK_USEC;
704 nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
705 pa_sink_set_max_rewind(u->sink, nbytes);
706 pa_sink_set_max_request(u->sink, nbytes);
708 pa_sink_put(u->sink);
709 } else {
710 /* optional uplink sink not enabled */
711 u->sink = NULL;
714 pa_modargs_free(ma);
716 return 0;
718 fail:
719 if (ma)
720 pa_modargs_free(ma);
722 pa__done(m);
724 return -1;
727 int pa__get_n_used(pa_module *m) {
728 struct userdata *u;
730 pa_assert(m);
731 pa_assert_se(u = m->userdata);
733 return pa_source_linked_by(u->source);
736 void pa__done(pa_module*m) {
737 struct userdata *u;
739 pa_assert(m);
741 if (!(u = m->userdata))
742 return;
744 /* See comments in source_output_kill_cb() above regarding
745 * destruction order! */
747 if (u->source_output)
748 pa_source_output_unlink(u->source_output);
750 if (u->source)
751 pa_source_unlink(u->source);
753 if (u->source_output)
754 pa_source_output_unref(u->source_output);
756 if (u->source)
757 pa_source_unref(u->source);
759 if (u->sink)
760 pa_sink_unref(u->sink);
762 if (u->memblockq)
763 pa_memblockq_free(u->memblockq);
765 if (u->sink_memblockq)
766 pa_memblockq_free(u->sink_memblockq);
768 pa_xfree(u);