4 This file is part of polypaudio.
6 polypaudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
11 polypaudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
32 #include "sink-input.h"
35 #include "sample-util.h"
37 #include "subscribe.h"
39 #include <polyp/polyplib-introspect.h>
41 #define MAX_MIX_CHANNELS 32
48 const pa_sample_spec
*spec
,
49 const pa_channel_map
*map
) {
61 s
= pa_xnew(pa_sink
, 1);
63 if (!(name
= pa_namereg_register(core
, name
, PA_NAMEREG_SINK
, s
, fail
))) {
70 s
->state
= PA_SINK_RUNNING
;
71 s
->name
= pa_xstrdup(name
);
72 s
->description
= NULL
;
73 s
->driver
= pa_xstrdup(driver
);
76 s
->sample_spec
= *spec
;
78 s
->channel_map
= *map
;
80 pa_channel_map_init_auto(&s
->channel_map
, spec
->channels
);
82 s
->inputs
= pa_idxset_new(NULL
, NULL
);
84 pa_cvolume_reset(&s
->sw_volume
, spec
->channels
);
85 pa_cvolume_reset(&s
->hw_volume
, spec
->channels
);
87 s
->get_latency
= NULL
;
89 s
->set_hw_volume
= NULL
;
90 s
->get_hw_volume
= NULL
;
93 r
= pa_idxset_put(core
->sinks
, s
, &s
->index
);
94 assert(s
->index
!= PA_IDXSET_INVALID
&& r
>= 0);
96 pa_sample_spec_snprint(st
, sizeof(st
), spec
);
97 pa_log_info(__FILE__
": created %u \"%s\" with sample spec \"%s\"\n", s
->index
, s
->name
, st
);
99 n
= pa_sprintf_malloc("%s_monitor", name
);
100 s
->monitor_source
= pa_source_new(core
, driver
, n
, 0, spec
, map
);
101 assert(s
->monitor_source
);
103 s
->monitor_source
->monitor_of
= s
;
104 s
->monitor_source
->description
= pa_sprintf_malloc("Monitor source of sink '%s'", s
->name
);
106 pa_subscription_post(core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_NEW
, s
->index
);
111 void pa_sink_disconnect(pa_sink
* s
) {
112 pa_sink_input
*i
, *j
= NULL
;
115 assert(s
->state
== PA_SINK_RUNNING
);
117 pa_namereg_unregister(s
->core
, s
->name
);
119 while ((i
= pa_idxset_first(s
->inputs
, NULL
))) {
121 pa_sink_input_kill(i
);
125 pa_source_disconnect(s
->monitor_source
);
127 pa_idxset_remove_by_data(s
->core
->sinks
, s
, NULL
);
129 s
->get_latency
= NULL
;
131 s
->get_hw_volume
= NULL
;
132 s
->set_hw_volume
= NULL
;
134 s
->state
= PA_SINK_DISCONNECTED
;
135 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
| PA_SUBSCRIPTION_EVENT_REMOVE
, s
->index
);
138 static void sink_free(pa_sink
*s
) {
142 if (s
->state
!= PA_SINK_DISCONNECTED
)
143 pa_sink_disconnect(s
);
145 pa_log_info(__FILE__
": freed %u \"%s\"\n", s
->index
, s
->name
);
147 pa_source_unref(s
->monitor_source
);
148 s
->monitor_source
= NULL
;
150 pa_idxset_free(s
->inputs
, NULL
, NULL
);
153 pa_xfree(s
->description
);
158 void pa_sink_unref(pa_sink
*s
) {
166 pa_sink
* pa_sink_ref(pa_sink
*s
) {
174 void pa_sink_notify(pa_sink
*s
) {
182 static unsigned fill_mix_info(pa_sink
*s
, pa_mix_info
*info
, unsigned maxinfo
) {
183 uint32_t idx
= PA_IDXSET_INVALID
;
191 for (i
= pa_idxset_first(s
->inputs
, &idx
); maxinfo
> 0 && i
; i
= pa_idxset_next(s
->inputs
, &idx
)) {
192 /* Increase ref counter, to make sure that this input doesn't
193 * vanish while we still need it */
194 pa_sink_input_ref(i
);
196 if (pa_sink_input_peek(i
, &info
->chunk
, &info
->volume
) < 0) {
197 pa_sink_input_unref(i
);
203 assert(info
->chunk
.memblock
);
204 assert(info
->chunk
.memblock
->data
);
205 assert(info
->chunk
.length
);
215 static void inputs_drop(pa_sink
*s
, pa_mix_info
*info
, unsigned maxinfo
, size_t length
) {
220 for (; maxinfo
> 0; maxinfo
--, info
++) {
221 pa_sink_input
*i
= info
->userdata
;
224 assert(info
->chunk
.memblock
);
227 pa_sink_input_drop(i
, &info
->chunk
, length
);
228 pa_memblock_unref(info
->chunk
.memblock
);
230 /* Decrease ref counter */
231 pa_sink_input_unref(i
);
232 info
->userdata
= NULL
;
236 int pa_sink_render(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
237 pa_mix_info info
[MAX_MIX_CHANNELS
];
248 n
= fill_mix_info(s
, info
, MAX_MIX_CHANNELS
);
256 *result
= info
[0].chunk
;
257 pa_memblock_ref(result
->memblock
);
259 if (result
->length
> length
)
260 result
->length
= length
;
262 pa_sw_cvolume_multiply(&volume
, &s
->sw_volume
, &info
[0].volume
);
264 if (!pa_cvolume_is_norm(&volume
)) {
265 pa_memchunk_make_writable(result
, s
->core
->memblock_stat
, 0);
266 pa_volume_memchunk(result
, &s
->sample_spec
, &volume
);
269 result
->memblock
= pa_memblock_new(length
, s
->core
->memblock_stat
);
270 assert(result
->memblock
);
272 result
->length
= pa_mix(info
, n
, result
->memblock
->data
, length
, &s
->sample_spec
, &s
->sw_volume
);
276 inputs_drop(s
, info
, n
, result
->length
);
277 pa_source_post(s
->monitor_source
, result
);
287 int pa_sink_render_into(pa_sink
*s
, pa_memchunk
*target
) {
288 pa_mix_info info
[MAX_MIX_CHANNELS
];
295 assert(target
->memblock
);
296 assert(target
->length
);
297 assert(target
->memblock
->data
);
301 n
= fill_mix_info(s
, info
, MAX_MIX_CHANNELS
);
309 if (target
->length
> info
[0].chunk
.length
)
310 target
->length
= info
[0].chunk
.length
;
312 memcpy((uint8_t*) target
->memblock
->data
+ target
->index
,
313 (uint8_t*) info
[0].chunk
.memblock
->data
+ info
[0].chunk
.index
,
316 pa_sw_cvolume_multiply(&volume
, &s
->sw_volume
, &info
[0].volume
);
318 if (!pa_cvolume_is_norm(&volume
))
319 pa_volume_memchunk(target
, &s
->sample_spec
, &volume
);
321 target
->length
= pa_mix(info
, n
,
322 (uint8_t*) target
->memblock
->data
+ target
->index
,
327 inputs_drop(s
, info
, n
, target
->length
);
328 pa_source_post(s
->monitor_source
, target
);
338 void pa_sink_render_into_full(pa_sink
*s
, pa_memchunk
*target
) {
345 assert(target
->memblock
);
346 assert(target
->length
);
347 assert(target
->memblock
->data
);
358 if (pa_sink_render_into(s
, &chunk
) < 0)
369 pa_silence_memchunk(&chunk
, &s
->sample_spec
);
375 void pa_sink_render_full(pa_sink
*s
, size_t length
, pa_memchunk
*result
) {
381 /*** This needs optimization ***/
383 result
->memblock
= pa_memblock_new(result
->length
= length
, s
->core
->memblock_stat
);
386 pa_sink_render_into_full(s
, result
);
389 pa_usec_t
pa_sink_get_latency(pa_sink
*s
) {
396 return s
->get_latency(s
);
399 void pa_sink_set_owner(pa_sink
*s
, pa_module
*m
) {
405 if (s
->monitor_source
)
406 pa_source_set_owner(s
->monitor_source
, m
);
409 void pa_sink_set_volume(pa_sink
*s
, pa_mixer_t m
, const pa_cvolume
*volume
) {
416 if (m
== PA_MIXER_HARDWARE
&& s
->set_hw_volume
)
421 if (pa_cvolume_equal(v
, volume
))
426 if (v
== &s
->hw_volume
)
427 if (s
->set_hw_volume(s
) < 0)
428 s
->sw_volume
= *volume
;
430 pa_subscription_post(s
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
, s
->index
);
433 const pa_cvolume
*pa_sink_get_volume(pa_sink
*s
, pa_mixer_t m
) {
437 if (m
== PA_MIXER_HARDWARE
&& s
->set_hw_volume
) {
439 if (s
->get_hw_volume
)
442 return &s
->hw_volume
;
444 return &s
->sw_volume
;