2 This file is part of PulseAudio.
4 Copyright 2009 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
30 #include <pulse/xmalloc.h>
32 #include <pulsecore/sink-input.h>
33 #include <pulsecore/module.h>
34 #include <pulsecore/modargs.h>
35 #include <pulsecore/namereg.h>
36 #include <pulsecore/log.h>
37 #include <pulsecore/core-util.h>
39 #include <pulse/rtclock.h>
40 #include <pulse/timeval.h>
42 #include "module-loopback-symdef.h"
44 PA_MODULE_AUTHOR("Pierre-Louis Bossart");
45 PA_MODULE_DESCRIPTION("Loopback from source to sink");
46 PA_MODULE_VERSION(PACKAGE_VERSION);
47 PA_MODULE_LOAD_ONCE(FALSE);
49 "source=<source to connect to> "
50 "sink=<sink to connect to> "
51 "adjust_time=<how often to readjust rates in s> "
52 "latency_msec=<latency in ms> "
53 "format=<sample format> "
55 "channels=<number of channels> "
56 "channel_map=<channel map>");
58 #define DEFAULT_LATENCY_MSEC 200
60 #define MEMBLOCKQ_MAXLENGTH (1024*1024*16)
62 #define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC)
68 pa_sink_input *sink_input;
69 pa_source_output *source_output;
71 pa_asyncmsgq *asyncmsgq;
72 pa_memblockq *memblockq;
74 pa_rtpoll_item *rtpoll_item_read, *rtpoll_item_write;
76 pa_time_event *time_event;
77 pa_usec_t adjust_time;
86 size_t min_memblockq_length;
90 size_t source_output_buffer;
91 pa_usec_t source_latency;
94 size_t sink_input_buffer;
95 pa_usec_t sink_latency;
97 size_t min_memblockq_length;
102 static const char* const valid_modargs[] = {
114 SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX,
115 SINK_INPUT_MESSAGE_REWIND,
116 SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT,
117 SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED
121 SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
124 /* Called from main context */
125 static void teardown(struct userdata *u) {
127 pa_assert_ctl_context();
130 pa_sink_input_unlink(u->sink_input);
132 if (u->source_output)
133 pa_source_output_unlink(u->source_output);
136 pa_sink_input_unref(u->sink_input);
137 u->sink_input = NULL;
140 if (u->source_output) {
141 pa_source_output_unref(u->source_output);
142 u->source_output = NULL;
146 /* Called from main context */
147 static void adjust_rates(struct userdata *u) {
149 uint32_t old_rate, base_rate, new_rate;
150 pa_usec_t buffer_latency;
153 pa_assert_ctl_context();
155 pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL);
156 pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL);
159 u->latency_snapshot.sink_input_buffer +
160 u->latency_snapshot.source_output_buffer;
162 if (u->latency_snapshot.recv_counter <= u->latency_snapshot.send_counter)
163 buffer += (size_t) (u->latency_snapshot.send_counter - u->latency_snapshot.recv_counter);
165 buffer += PA_CLIP_SUB(buffer, (size_t) (u->latency_snapshot.recv_counter - u->latency_snapshot.send_counter));
167 buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec);
169 pa_log_info("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms",
170 (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC,
171 (double) buffer_latency / PA_USEC_PER_MSEC,
172 (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC,
173 ((double) u->latency_snapshot.sink_latency + buffer_latency + u->latency_snapshot.source_latency) / PA_USEC_PER_MSEC);
175 pa_log_info("Should buffer %zu bytes, buffered at minimum %zu bytes",
176 u->latency_snapshot.max_request*2,
177 u->latency_snapshot.min_memblockq_length);
179 fs = pa_frame_size(&u->sink_input->sample_spec);
180 old_rate = u->sink_input->sample_spec.rate;
181 base_rate = u->source_output->sample_spec.rate;
183 if (u->latency_snapshot.min_memblockq_length < u->latency_snapshot.max_request*2)
184 new_rate = base_rate - (((u->latency_snapshot.max_request*2 - u->latency_snapshot.min_memblockq_length) / fs) *PA_USEC_PER_SEC)/u->adjust_time;
186 new_rate = base_rate + (((u->latency_snapshot.min_memblockq_length - u->latency_snapshot.max_request*2) / fs) *PA_USEC_PER_SEC)/u->adjust_time;
188 pa_log_info("Old rate %lu Hz, new rate %lu Hz", (unsigned long) old_rate, (unsigned long) new_rate);
190 pa_sink_input_set_rate(u->sink_input, new_rate);
192 pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time);
195 /* Called from main context */
196 static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
197 struct userdata *u = userdata;
201 pa_assert(u->time_event == e);
206 /* Called from input thread context */
207 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
211 pa_source_output_assert_ref(o);
212 pa_source_output_assert_io_context(o);
213 pa_assert_se(u = o->userdata);
215 if (u->skip > chunk->length) {
216 u->skip -= chunk->length;
222 copy.index += u->skip;
223 copy.length -= u->skip;
229 pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, chunk, NULL);
230 u->send_counter += (int64_t) chunk->length;
233 /* Called from input thread context */
234 static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) {
237 pa_source_output_assert_ref(o);
238 pa_source_output_assert_io_context(o);
239 pa_assert_se(u = o->userdata);
241 pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL);
242 u->send_counter -= (int64_t) nbytes;
245 /* Called from output thread context */
246 static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
247 struct userdata *u = PA_SOURCE_OUTPUT(obj)->userdata;
251 case SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT: {
254 length = pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq);
256 u->latency_snapshot.send_counter = u->send_counter;
257 u->latency_snapshot.source_output_buffer = u->source_output->thread_info.resampler ? pa_resampler_result(u->source_output->thread_info.resampler, length) : length;
258 u->latency_snapshot.source_latency = pa_source_get_latency_within_thread(u->source_output->source);
264 return pa_source_output_process_msg(obj, code, data, offset, chunk);
267 /* Called from output thread context */
268 static void source_output_attach_cb(pa_source_output *o) {
271 pa_source_output_assert_ref(o);
272 pa_source_output_assert_io_context(o);
273 pa_assert_se(u = o->userdata);
275 u->rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
276 o->source->thread_info.rtpoll,
281 /* Called from output thread context */
282 static void source_output_detach_cb(pa_source_output *o) {
285 pa_source_output_assert_ref(o);
286 pa_source_output_assert_io_context(o);
287 pa_assert_se(u = o->userdata);
289 if (u->rtpoll_item_write) {
290 pa_rtpoll_item_free(u->rtpoll_item_write);
291 u->rtpoll_item_write = NULL;
295 /* Called from output thread context */
296 static void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) {
299 pa_source_output_assert_ref(o);
300 pa_source_output_assert_io_context(o);
301 pa_assert_se(u = o->userdata);
303 if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT) {
305 u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source),
309 pa_log_info("Skipping %lu bytes", (unsigned long) u->skip);
313 /* Called from main thread */
314 static void source_output_kill_cb(pa_source_output *o) {
317 pa_source_output_assert_ref(o);
318 pa_assert_ctl_context();
319 pa_assert_se(u = o->userdata);
322 pa_module_unload_request(u->module, TRUE);
325 /* Called from main thread */
326 static pa_bool_t source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) {
329 pa_source_output_assert_ref(o);
330 pa_assert_ctl_context();
331 pa_assert_se(u = o->userdata);
333 return dest != u->sink_input->sink->monitor_source;
336 /* Called from main thread */
337 static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
342 pa_source_output_assert_ref(o);
343 pa_assert_ctl_context();
344 pa_assert_se(u = o->userdata);
346 p = pa_proplist_new();
347 pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback of %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
349 if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
350 pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
352 pa_sink_input_update_proplist(u->sink_input, PA_UPDATE_REPLACE, p);
356 /* Called from output thread context */
357 static void update_min_memblockq_length(struct userdata *u) {
361 pa_sink_input_assert_io_context(u->sink_input);
363 length = pa_memblockq_get_length(u->memblockq);
365 if (u->min_memblockq_length == (size_t) -1 ||
366 length < u->min_memblockq_length)
367 u->min_memblockq_length = length;
370 /* Called from output thread context */
371 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
374 pa_sink_input_assert_ref(i);
375 pa_sink_input_assert_io_context(i);
376 pa_assert_se(u = i->userdata);
380 while (pa_asyncmsgq_process_one(u->asyncmsgq) > 0)
384 if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
385 pa_log_info("Coud not peek into queue");
389 chunk->length = PA_MIN(chunk->length, nbytes);
390 pa_memblockq_drop(u->memblockq, chunk->length);
392 update_min_memblockq_length(u);
397 /* Called from output thread context */
398 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
401 pa_sink_input_assert_ref(i);
402 pa_sink_input_assert_io_context(i);
403 pa_assert_se(u = i->userdata);
405 pa_memblockq_rewind(u->memblockq, nbytes);
408 /* Called from output thread context */
409 static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
410 struct userdata *u = PA_SINK_INPUT(obj)->userdata;
414 case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
417 pa_sink_input_assert_io_context(u->sink_input);
419 *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec);
421 /* Fall through, the default handler will add in the extra
422 * latency added by the resampler */
426 case SINK_INPUT_MESSAGE_POST:
428 pa_sink_input_assert_io_context(u->sink_input);
430 if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
431 pa_memblockq_push_align(u->memblockq, chunk);
433 pa_memblockq_flush_write(u->memblockq, TRUE);
435 update_min_memblockq_length(u);
437 /* Is this the end of an underrun? Then let's start things
440 u->sink_input->thread_info.underrun_for > 0 &&
441 pa_memblockq_is_readable(u->memblockq)) {
443 pa_log_debug("Requesting rewind due to end of underrun.");
444 pa_sink_input_request_rewind(u->sink_input,
445 (size_t) (u->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : u->sink_input->thread_info.underrun_for),
449 u->recv_counter += (int64_t) chunk->length;
453 case SINK_INPUT_MESSAGE_REWIND:
455 pa_sink_input_assert_io_context(u->sink_input);
457 if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
458 pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, TRUE);
460 pa_memblockq_flush_write(u->memblockq, TRUE);
462 u->recv_counter -= offset;
464 update_min_memblockq_length(u);
468 case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: {
471 update_min_memblockq_length(u);
473 length = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq);
475 u->latency_snapshot.recv_counter = u->recv_counter;
476 u->latency_snapshot.sink_input_buffer =
477 pa_memblockq_get_length(u->memblockq) +
478 (u->sink_input->thread_info.resampler ? pa_resampler_request(u->sink_input->thread_info.resampler, length) : length);
479 u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink);
481 u->latency_snapshot.max_request = pa_sink_input_get_max_request(u->sink_input);
483 u->latency_snapshot.min_memblockq_length = u->min_memblockq_length;
484 u->min_memblockq_length = (size_t) -1;
489 case SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED: {
490 /* This message is sent from the IO thread to the main
491 * thread! So don't be confused. All the user cases above
492 * are executed in thread context, but this one is not! */
494 pa_assert_ctl_context();
496 if (u->adjust_time > 0)
502 return pa_sink_input_process_msg(obj, code, data, offset, chunk);
505 /* Called from output thread context */
506 static void sink_input_attach_cb(pa_sink_input *i) {
509 pa_sink_input_assert_ref(i);
510 pa_sink_input_assert_io_context(i);
511 pa_assert_se(u = i->userdata);
513 u->rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
514 i->sink->thread_info.rtpoll,
518 pa_memblockq_set_prebuf(u->memblockq, pa_sink_input_get_max_request(i)*2);
519 pa_memblockq_set_maxrewind(u->memblockq, pa_sink_input_get_max_rewind(i));
521 u->min_memblockq_length = (size_t) -1;
524 /* Called from output thread context */
525 static void sink_input_detach_cb(pa_sink_input *i) {
528 pa_sink_input_assert_ref(i);
529 pa_sink_input_assert_io_context(i);
530 pa_assert_se(u = i->userdata);
532 if (u->rtpoll_item_read) {
533 pa_rtpoll_item_free(u->rtpoll_item_read);
534 u->rtpoll_item_read = NULL;
538 /* Called from output thread context */
539 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
542 pa_sink_input_assert_ref(i);
543 pa_sink_input_assert_io_context(i);
544 pa_assert_se(u = i->userdata);
546 pa_memblockq_set_maxrewind(u->memblockq, nbytes);
549 /* Called from output thread context */
550 static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
553 pa_sink_input_assert_ref(i);
554 pa_sink_input_assert_io_context(i);
555 pa_assert_se(u = i->userdata);
557 pa_memblockq_set_prebuf(u->memblockq, nbytes*2);
558 pa_log_info("Max request changed");
559 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED, NULL, 0, NULL, NULL);
562 /* Called from main thread */
563 static void sink_input_kill_cb(pa_sink_input *i) {
566 pa_sink_input_assert_ref(i);
567 pa_assert_ctl_context();
568 pa_assert_se(u = i->userdata);
571 pa_module_unload_request(u->module, TRUE);
574 /* Called from main thread */
575 static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
580 pa_sink_input_assert_ref(i);
581 pa_assert_ctl_context();
582 pa_assert_se(u = i->userdata);
584 p = pa_proplist_new();
585 pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
587 if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
588 pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
590 pa_source_output_update_proplist(u->source_output, PA_UPDATE_REPLACE, p);
594 /* Called from main thread */
595 static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
598 pa_sink_input_assert_ref(i);
599 pa_assert_ctl_context();
600 pa_assert_se(u = i->userdata);
602 if (!u->source_output->source->monitor_of)
605 return dest != u->source_output->source->monitor_of;
608 int pa__init(pa_module *m) {
609 pa_modargs *ma = NULL;
612 pa_sink_input_new_data sink_input_data;
614 pa_source_output_new_data source_output_data;
615 uint32_t latency_msec;
619 uint32_t adjust_time_sec;
624 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
625 pa_log("Failed to parse module arguments");
629 if (!(source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) {
630 pa_log("No such source.");
634 if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK))) {
635 pa_log("No such sink.");
639 ss = sink->sample_spec;
640 map = sink->channel_map;
641 if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
642 pa_log("Invalid sample format specification or channel map");
646 latency_msec = DEFAULT_LATENCY_MSEC;
647 if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 2000) {
648 pa_log("Invalid latency specification");
652 m->userdata = u = pa_xnew0(struct userdata, 1);
655 u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC;
657 adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
658 if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
659 pa_log("Failed to parse adjust_time value");
663 if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC)
664 u->adjust_time = adjust_time_sec * PA_USEC_PER_SEC;
666 u->adjust_time = DEFAULT_ADJUST_TIME_USEC;
668 pa_sink_input_new_data_init(&sink_input_data);
669 sink_input_data.driver = __FILE__;
670 sink_input_data.module = m;
671 sink_input_data.sink = sink;
673 pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Loopback of %s",
674 pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
675 if ((n = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)))
676 pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ICON_NAME, n);
677 pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
678 pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
679 pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
680 sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE;
682 pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
683 pa_sink_input_new_data_done(&sink_input_data);
688 u->sink_input->parent.process_msg = sink_input_process_msg_cb;
689 u->sink_input->pop = sink_input_pop_cb;
690 u->sink_input->process_rewind = sink_input_process_rewind_cb;
691 u->sink_input->kill = sink_input_kill_cb;
692 u->sink_input->attach = sink_input_attach_cb;
693 u->sink_input->detach = sink_input_detach_cb;
694 u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
695 u->sink_input->update_max_request = sink_input_update_max_request_cb;
696 u->sink_input->may_move_to = sink_input_may_move_to_cb;
697 u->sink_input->moving = sink_input_moving_cb;
698 u->sink_input->userdata = u;
700 pa_sink_input_set_requested_latency(u->sink_input, u->latency/3);
702 pa_source_output_new_data_init(&source_output_data);
703 source_output_data.driver = __FILE__;
704 source_output_data.module = m;
705 source_output_data.source = source;
706 pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Loopback to %s",
707 pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
708 if ((n = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)))
709 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ICON_NAME, n);
710 pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
711 pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
712 pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
714 pa_source_output_new(&u->source_output, m->core, &source_output_data);
715 pa_source_output_new_data_done(&source_output_data);
717 if (!u->source_output)
720 u->source_output->parent.process_msg = source_output_process_msg_cb;
721 u->source_output->push = source_output_push_cb;
722 u->source_output->process_rewind = source_output_process_rewind_cb;
723 u->source_output->kill = source_output_kill_cb;
724 u->source_output->attach = source_output_attach_cb;
725 u->source_output->detach = source_output_detach_cb;
726 u->source_output->state_change = source_output_state_change_cb;
727 u->source_output->may_move_to = source_output_may_move_to_cb;
728 u->source_output->moving = source_output_moving_cb;
729 u->source_output->userdata = u;
731 pa_source_output_set_requested_latency(u->source_output, u->latency/3);
733 pa_sink_input_get_silence(u->sink_input, &silence);
734 u->memblockq = pa_memblockq_new(
736 MEMBLOCKQ_MAXLENGTH, /* maxlength */
737 MEMBLOCKQ_MAXLENGTH, /* tlength */
738 pa_frame_size(&ss), /* base */
742 &silence); /* silence frame */
743 pa_memblock_unref(silence.memblock);
745 u->asyncmsgq = pa_asyncmsgq_new(0);
747 pa_sink_input_put(u->sink_input);
748 pa_source_output_put(u->source_output);
750 if (u->adjust_time > 0)
751 u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time, time_callback, u);
765 void pa__done(pa_module*m) {
770 if (!(u = m->userdata))
776 pa_memblockq_free(u->memblockq);
779 pa_asyncmsgq_unref(u->asyncmsgq);
782 u->core->mainloop->time_free(u->time_event);