2 This file is part of PulseAudio.
4 Copyright 2013 bct electronic GmbH
5 Contributor: Stefan Huber <s.huber@bct-electronic.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
29 #include <pulse/xmalloc.h>
31 #include <pulsecore/i18n.h>
32 #include <pulsecore/macro.h>
33 #include <pulsecore/namereg.h>
34 #include <pulsecore/module.h>
35 #include <pulsecore/core-util.h>
36 #include <pulsecore/modargs.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/rtpoll.h>
39 #include <pulsecore/sample-util.h>
40 #include <pulsecore/ltdl-helper.h>
41 #include <pulsecore/mix.h>
43 #include "module-remap-source-symdef.h"
45 PA_MODULE_AUTHOR("Stefan Huber");
46 PA_MODULE_DESCRIPTION("Virtual channel remapping source");
47 PA_MODULE_VERSION(PACKAGE_VERSION
);
48 PA_MODULE_LOAD_ONCE(false);
50 "source_name=<name for the source> "
51 "source_properties=<properties for the source> "
52 "master=<name of source to filter> "
53 "master_channel_map=<channel map> "
54 "format=<sample format> "
56 "channels=<number of channels> "
57 "channel_map=<channel map> "
58 "remix=<remix channels?>");
64 pa_source_output
*source_output
;
69 static const char* const valid_modargs
[] = {
82 /* Called from I/O thread context */
83 static int source_process_msg_cb(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
84 struct userdata
*u
= PA_SOURCE(o
)->userdata
;
88 case PA_SOURCE_MESSAGE_GET_LATENCY
:
90 /* The source is _put() before the source output is, so let's
91 * make sure we don't access it in that time. Also, the
92 * source output is first shut down, the source second. */
93 if (!PA_SOURCE_IS_LINKED(u
->source
->thread_info
.state
) ||
94 !PA_SOURCE_OUTPUT_IS_LINKED(u
->source_output
->thread_info
.state
)) {
95 *((pa_usec_t
*) data
) = 0;
99 *((pa_usec_t
*) data
) =
101 /* Get the latency of the master source */
102 pa_source_get_latency_within_thread(u
->source_output
->source
) +
103 /* Add the latency internal to our source output on top */
104 pa_bytes_to_usec(pa_memblockq_get_length(u
->source_output
->thread_info
.delay_memblockq
), &u
->source_output
->source
->sample_spec
);
109 return pa_source_process_msg(o
, code
, data
, offset
, chunk
);
112 /* Called from main context */
113 static int source_set_state_cb(pa_source
*s
, pa_source_state_t state
) {
116 pa_source_assert_ref(s
);
117 pa_assert_se(u
= s
->userdata
);
119 if (!PA_SOURCE_IS_LINKED(state
) ||
120 !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u
->source_output
)))
123 pa_source_output_cork(u
->source_output
, state
== PA_SOURCE_SUSPENDED
);
127 /* Called from I/O thread context */
128 static void source_update_requested_latency_cb(pa_source
*s
) {
131 pa_source_assert_ref(s
);
132 pa_assert_se(u
= s
->userdata
);
134 if (!PA_SOURCE_IS_LINKED(u
->source
->thread_info
.state
) ||
135 !PA_SOURCE_OUTPUT_IS_LINKED(u
->source_output
->thread_info
.state
))
138 pa_log_debug("Source update requested latency.");
140 /* Just hand this one over to the master source */
141 pa_source_output_set_requested_latency_within_thread(
143 pa_source_get_requested_latency_within_thread(s
));
146 /* Called from output thread context */
147 static void source_output_push_cb(pa_source_output
*o
, const pa_memchunk
*chunk
) {
150 pa_source_output_assert_ref(o
);
151 pa_source_output_assert_io_context(o
);
152 pa_assert_se(u
= o
->userdata
);
154 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u
->source_output
))) {
155 pa_log("push when no link?");
159 pa_source_post(u
->source
, chunk
);
162 /* Called from output thread context */
163 static void source_output_process_rewind_cb(pa_source_output
*o
, size_t nbytes
) {
166 pa_source_output_assert_ref(o
);
167 pa_source_output_assert_io_context(o
);
168 pa_assert_se(u
= o
->userdata
);
170 pa_source_process_rewind(u
->source
, nbytes
);
173 /* Called from output thread context */
174 static void source_output_detach_cb(pa_source_output
*o
) {
177 pa_source_output_assert_ref(o
);
178 pa_source_output_assert_io_context(o
);
179 pa_assert_se(u
= o
->userdata
);
181 pa_source_detach_within_thread(u
->source
);
183 pa_source_set_rtpoll(u
->source
, NULL
);
186 /* Called from output thread context */
187 static void source_output_attach_cb(pa_source_output
*o
) {
190 pa_source_output_assert_ref(o
);
191 pa_source_output_assert_io_context(o
);
192 pa_assert_se(u
= o
->userdata
);
194 pa_source_set_rtpoll(u
->source
, o
->source
->thread_info
.rtpoll
);
195 pa_source_set_latency_range_within_thread(u
->source
, o
->source
->thread_info
.min_latency
, o
->source
->thread_info
.max_latency
);
196 pa_source_set_fixed_latency_within_thread(u
->source
, o
->source
->thread_info
.fixed_latency
);
197 pa_source_set_max_rewind_within_thread(u
->source
, pa_source_output_get_max_rewind(o
));
199 pa_source_attach_within_thread(u
->source
);
202 /* Called from main thread */
203 static void source_output_kill_cb(pa_source_output
*o
) {
206 pa_source_output_assert_ref(o
);
207 pa_assert_ctl_context();
208 pa_assert_se(u
= o
->userdata
);
210 /* The order here matters! We first kill the source output, followed
211 * by the source. That means the source callbacks must be protected
212 * against an unconnected source output! */
213 pa_source_output_unlink(u
->source_output
);
214 pa_source_unlink(u
->source
);
216 pa_source_output_unref(u
->source_output
);
217 u
->source_output
= NULL
;
219 pa_source_unref(u
->source
);
222 pa_module_unload_request(u
->module
, true);
225 /* Called from output thread context */
226 static void source_output_state_change_cb(pa_source_output
*o
, pa_source_output_state_t state
) {
229 pa_source_output_assert_ref(o
);
230 pa_source_output_assert_io_context(o
);
231 pa_assert_se(u
= o
->userdata
);
233 pa_log_debug("Source output %d state %d.", o
->index
, state
);
236 /* Called from main thread */
237 static void source_output_moving_cb(pa_source_output
*o
, pa_source
*dest
) {
240 pa_source_output_assert_ref(o
);
241 pa_assert_ctl_context();
242 pa_assert_se(u
= o
->userdata
);
245 pa_source_set_asyncmsgq(u
->source
, dest
->asyncmsgq
);
246 pa_source_update_flags(u
->source
, PA_SOURCE_LATENCY
|PA_SOURCE_DYNAMIC_LATENCY
, dest
->flags
);
248 pa_source_set_asyncmsgq(u
->source
, NULL
);
250 if (u
->auto_desc
&& dest
) {
254 pl
= pa_proplist_new();
255 k
= pa_proplist_gets(dest
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
256 pa_proplist_setf(pl
, PA_PROP_DEVICE_DESCRIPTION
, "Remapped %s", k
? k
: dest
->name
);
258 pa_source_update_proplist(u
->source
, PA_UPDATE_REPLACE
, pl
);
259 pa_proplist_free(pl
);
263 int pa__init(pa_module
*m
) {
266 pa_channel_map source_map
, stream_map
;
269 pa_source_output_new_data source_output_data
;
270 pa_source_new_data source_data
;
275 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
276 pa_log("Failed to parse module arguments.");
280 if (!(master
= pa_namereg_get(m
->core
, pa_modargs_get_value(ma
, "master", NULL
), PA_NAMEREG_SOURCE
))) {
281 pa_log("Master source not found.");
285 ss
= master
->sample_spec
;
286 source_map
= master
->channel_map
;
287 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &source_map
, PA_CHANNEL_MAP_DEFAULT
) < 0) {
288 pa_log("Invalid sample format specification or channel map.");
292 stream_map
= source_map
;
293 if (pa_modargs_get_channel_map(ma
, "master_channel_map", &stream_map
) < 0) {
294 pa_log("Invalid master channel map.");
298 if (stream_map
.channels
!= ss
.channels
) {
299 pa_log("Number of channels doesn't match.");
303 if (pa_channel_map_equal(&stream_map
, &master
->channel_map
))
304 pa_log_warn("No remapping configured, proceeding nonetheless!");
306 if (pa_modargs_get_value_boolean(ma
, "remix", &remix
) < 0) {
307 pa_log("Invalid boolean remix parameter.");
311 u
= pa_xnew0(struct userdata
, 1);
316 pa_source_new_data_init(&source_data
);
317 source_data
.driver
= __FILE__
;
318 source_data
.module
= m
;
319 if (!(source_data
.name
= pa_xstrdup(pa_modargs_get_value(ma
, "source_name", NULL
))))
320 source_data
.name
= pa_sprintf_malloc("%s.remapped", master
->name
);
321 pa_source_new_data_set_sample_spec(&source_data
, &ss
);
322 pa_source_new_data_set_channel_map(&source_data
, &source_map
);
323 pa_proplist_sets(source_data
.proplist
, PA_PROP_DEVICE_MASTER_DEVICE
, master
->name
);
324 pa_proplist_sets(source_data
.proplist
, PA_PROP_DEVICE_CLASS
, "filter");
326 if (pa_modargs_get_proplist(ma
, "source_properties", source_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
327 pa_log("Invalid properties.");
328 pa_source_new_data_done(&source_data
);
332 if ((u
->auto_desc
= !pa_proplist_contains(source_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
))) {
335 k
= pa_proplist_gets(master
->proplist
, PA_PROP_DEVICE_DESCRIPTION
);
336 pa_proplist_setf(source_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, "Remapped %s", k
? k
: master
->name
);
339 u
->source
= pa_source_new(m
->core
, &source_data
, master
->flags
& (PA_SOURCE_LATENCY
|PA_SOURCE_DYNAMIC_LATENCY
));
340 pa_source_new_data_done(&source_data
);
343 pa_log("Failed to create source.");
347 u
->source
->parent
.process_msg
= source_process_msg_cb
;
348 u
->source
->set_state
= source_set_state_cb
;
349 u
->source
->update_requested_latency
= source_update_requested_latency_cb
;
351 u
->source
->userdata
= u
;
353 pa_source_set_asyncmsgq(u
->source
, master
->asyncmsgq
);
355 /* Create source output */
356 pa_source_output_new_data_init(&source_output_data
);
357 source_output_data
.driver
= __FILE__
;
358 source_output_data
.module
= m
;
359 pa_source_output_new_data_set_source(&source_output_data
, master
, false);
360 source_output_data
.destination_source
= u
->source
;
362 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_NAME
, "Remapped Stream");
363 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_ROLE
, "filter");
364 pa_source_output_new_data_set_sample_spec(&source_output_data
, &ss
);
365 pa_source_output_new_data_set_channel_map(&source_output_data
, &stream_map
);
366 source_output_data
.flags
= remix
? 0 : PA_SOURCE_OUTPUT_NO_REMIX
;
368 pa_source_output_new(&u
->source_output
, m
->core
, &source_output_data
);
369 pa_source_output_new_data_done(&source_output_data
);
371 if (!u
->source_output
)
374 u
->source_output
->push
= source_output_push_cb
;
375 u
->source_output
->process_rewind
= source_output_process_rewind_cb
;
376 u
->source_output
->kill
= source_output_kill_cb
;
377 u
->source_output
->attach
= source_output_attach_cb
;
378 u
->source_output
->detach
= source_output_detach_cb
;
379 u
->source_output
->state_change
= source_output_state_change_cb
;
380 u
->source_output
->moving
= source_output_moving_cb
;
381 u
->source_output
->userdata
= u
;
383 u
->source
->output_from_master
= u
->source_output
;
385 pa_source_put(u
->source
);
386 pa_source_output_put(u
->source_output
);
401 int pa__get_n_used(pa_module
*m
) {
405 pa_assert_se(u
= m
->userdata
);
407 return pa_source_linked_by(u
->source
);
410 void pa__done(pa_module
*m
) {
415 if (!(u
= m
->userdata
))
418 /* See comments in source_output_kill_cb() above regarding
419 * destruction order! */
421 if (u
->source_output
)
422 pa_source_output_unlink(u
->source_output
);
425 pa_source_unlink(u
->source
);
427 if (u
->source_output
)
428 pa_source_output_unref(u
->source_output
);
431 pa_source_unref(u
->source
);