6931d396d2fca2920abc97691e8d6a68a258a4d1
[profile/ivi/pulseaudio.git] / src / polypcore / sink.c
1 /* $Id$ */
2
3 /***
4   This file is part of polypaudio.
5  
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.
10  
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.
15  
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
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <assert.h>
28 #include <string.h>
29 #include <stdio.h>
30
31 #include <polyp/introspect.h>
32 #include <polyp/utf8.h>
33 #include <polyp/xmalloc.h>
34
35 #include <polypcore/sink-input.h>
36 #include <polypcore/namereg.h>
37 #include <polypcore/util.h>
38 #include <polypcore/sample-util.h>
39 #include <polypcore/core-subscribe.h>
40 #include <polypcore/log.h>
41
42 #include "sink.h"
43
44 #define MAX_MIX_CHANNELS 32
45
46 #define CHECK_VALIDITY_RETURN_NULL(condition) \
47 do {\
48 if (!(condition)) \
49     return NULL; \
50 } while (0)
51
52 pa_sink* pa_sink_new(
53         pa_core *core,
54         const char *driver,
55         const char *name,
56         int fail,
57         const pa_sample_spec *spec,
58         const pa_channel_map *map) {
59     
60     pa_sink *s;
61     char *n = NULL;
62     char st[256];
63     int r;
64     pa_channel_map tmap;
65
66     assert(core);
67     assert(name);
68     assert(spec);
69
70     CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec));
71     
72     if (!map)
73         map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT);
74
75     CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map));
76     CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels);
77     CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver));
78     CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name);
79     
80     s = pa_xnew(pa_sink, 1);
81
82     if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) {
83         pa_xfree(s);
84         return NULL;
85     }
86
87     s->ref = 1;
88     s->core = core;
89     s->state = PA_SINK_RUNNING;
90     s->name = pa_xstrdup(name);
91     s->description = NULL;
92     s->driver = pa_xstrdup(driver);
93     s->owner = NULL;
94
95     s->sample_spec = *spec;
96     s->channel_map = *map;
97     
98     s->inputs = pa_idxset_new(NULL, NULL);
99
100     pa_cvolume_reset(&s->sw_volume, spec->channels);
101     pa_cvolume_reset(&s->hw_volume, spec->channels);
102     s->sw_muted = 0;
103     s->hw_muted = 0;
104
105     s->get_latency = NULL;
106     s->notify = NULL;
107     s->set_hw_volume = NULL;
108     s->get_hw_volume = NULL;
109     s->set_hw_mute = NULL;
110     s->get_hw_mute = NULL;
111     s->userdata = NULL;
112
113     r = pa_idxset_put(core->sinks, s, &s->index);
114     assert(s->index != PA_IDXSET_INVALID && r >= 0);
115     
116     pa_sample_spec_snprint(st, sizeof(st), spec);
117     pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
118
119     n = pa_sprintf_malloc("%s_monitor", name);
120     s->monitor_source = pa_source_new(core, driver, n, 0, spec, map);
121     assert(s->monitor_source);
122     pa_xfree(n);
123     s->monitor_source->monitor_of = s;
124     s->monitor_source->description = pa_sprintf_malloc("Monitor source of sink '%s'", s->name);
125     
126     pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
127     
128     return s;
129 }
130
131 void pa_sink_disconnect(pa_sink* s) {
132     pa_sink_input *i, *j = NULL;
133     
134     assert(s);
135     assert(s->state == PA_SINK_RUNNING);
136
137     pa_namereg_unregister(s->core, s->name);
138     
139     while ((i = pa_idxset_first(s->inputs, NULL))) {
140         assert(i != j);
141         pa_sink_input_kill(i);
142         j = i;
143     }
144
145     pa_source_disconnect(s->monitor_source);
146
147     pa_idxset_remove_by_data(s->core->sinks, s, NULL);
148
149     s->get_latency = NULL;
150     s->notify = NULL;
151     s->get_hw_volume = NULL;
152     s->set_hw_volume = NULL;
153     s->set_hw_mute = NULL;
154     s->get_hw_mute = NULL;
155     
156     s->state = PA_SINK_DISCONNECTED;
157     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
158 }
159
160 static void sink_free(pa_sink *s) {
161     assert(s);
162     assert(!s->ref);
163     
164     if (s->state != PA_SINK_DISCONNECTED)
165         pa_sink_disconnect(s);
166
167     pa_log_info(__FILE__": freed %u \"%s\"", s->index, s->name); 
168
169     pa_source_unref(s->monitor_source);
170     s->monitor_source = NULL;
171     
172     pa_idxset_free(s->inputs, NULL, NULL);
173
174     pa_xfree(s->name);
175     pa_xfree(s->description);
176     pa_xfree(s->driver);
177     pa_xfree(s);
178 }
179
180 void pa_sink_unref(pa_sink*s) {
181     assert(s);
182     assert(s->ref >= 1);
183
184     if (!(--s->ref))
185         sink_free(s);
186 }
187
188 pa_sink* pa_sink_ref(pa_sink *s) {
189     assert(s);
190     assert(s->ref >= 1);
191     
192     s->ref++;
193     return s;
194 }
195
196 void pa_sink_notify(pa_sink*s) {
197     assert(s);
198     assert(s->ref >= 1);
199
200     if (s->notify)
201         s->notify(s);
202 }
203
204 static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) {
205     uint32_t idx = PA_IDXSET_INVALID;
206     pa_sink_input *i;
207     unsigned n = 0;
208     
209     assert(s);
210     assert(s->ref >= 1);
211     assert(info);
212
213     for (i = pa_idxset_first(s->inputs, &idx); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &idx)) {
214         /* Increase ref counter, to make sure that this input doesn't
215          * vanish while we still need it */
216         pa_sink_input_ref(i);
217
218         if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0) {
219             pa_sink_input_unref(i);
220             continue;
221         }
222
223         info->userdata = i;
224         
225         assert(info->chunk.memblock);
226         assert(info->chunk.memblock->data);
227         assert(info->chunk.length);
228         
229         info++;
230         maxinfo--;
231         n++;
232     }
233
234     return n;
235 }
236
237 static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) {
238     assert(s);
239     assert(s->ref >= 1);
240     assert(info);
241
242     for (; maxinfo > 0; maxinfo--, info++) {
243         pa_sink_input *i = info->userdata;
244         
245         assert(i);
246         assert(info->chunk.memblock);
247
248         /* Drop read data */
249         pa_sink_input_drop(i, &info->chunk, length);
250         pa_memblock_unref(info->chunk.memblock);
251
252         /* Decrease ref counter */
253         pa_sink_input_unref(i);
254         info->userdata = NULL;
255     }
256 }
257         
258 int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
259     pa_mix_info info[MAX_MIX_CHANNELS];
260     unsigned n;
261     int r = -1;
262     
263     assert(s);
264     assert(s->ref >= 1);
265     assert(length);
266     assert(result);
267
268     pa_sink_ref(s);
269     
270     n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
271
272     if (n <= 0)
273         goto finish;
274
275     if (n == 1) {
276         pa_cvolume volume;
277
278         *result = info[0].chunk;
279         pa_memblock_ref(result->memblock);
280
281         if (result->length > length)
282             result->length = length;
283
284         pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume);
285         
286         if (s->sw_muted || !pa_cvolume_is_norm(&volume)) {
287             pa_memchunk_make_writable(result, s->core->memblock_stat, 0);
288             if (s->sw_muted)
289                 pa_silence_memchunk(result, &s->sample_spec);
290             else
291                 pa_volume_memchunk(result, &s->sample_spec, &volume);
292         }
293     } else {
294         result->memblock = pa_memblock_new(length, s->core->memblock_stat);
295         assert(result->memblock);
296
297 /*          pa_log("mixing %i", n);  */
298
299         result->length = pa_mix(info, n, result->memblock->data, length,
300             &s->sample_spec, &s->sw_volume, s->sw_muted);
301         result->index = 0;
302     }
303
304     inputs_drop(s, info, n, result->length);
305     pa_source_post(s->monitor_source, result);
306
307     r = 0;
308
309 finish:
310     pa_sink_unref(s);
311
312     return r;
313 }
314
315 int pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
316     pa_mix_info info[MAX_MIX_CHANNELS];
317     unsigned n;
318     int r = -1;
319     
320     assert(s);
321     assert(s->ref >= 1);
322     assert(target);
323     assert(target->memblock);
324     assert(target->length);
325     assert(target->memblock->data);
326
327     pa_sink_ref(s);
328     
329     n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
330
331     if (n <= 0)
332         goto finish;
333
334     if (n == 1) {
335         pa_cvolume volume;
336
337         if (target->length > info[0].chunk.length)
338             target->length = info[0].chunk.length;
339         
340         memcpy((uint8_t*) target->memblock->data + target->index,
341                (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index,
342                target->length);
343
344         pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume);
345
346         if (s->sw_muted)
347             pa_silence_memchunk(target, &s->sample_spec);        
348         else if (!pa_cvolume_is_norm(&volume))
349             pa_volume_memchunk(target, &s->sample_spec, &volume);
350     } else
351         target->length = pa_mix(info, n,
352                                 (uint8_t*) target->memblock->data + target->index,
353                                 target->length,
354                                 &s->sample_spec,
355                                 &s->sw_volume,
356                                 s->sw_muted);
357     
358     inputs_drop(s, info, n, target->length);
359     pa_source_post(s->monitor_source, target);
360
361     r = 0;
362
363 finish:
364     pa_sink_unref(s);
365     
366     return r;
367 }
368
369 void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
370     pa_memchunk chunk;
371     size_t l, d;
372     
373     assert(s);
374     assert(s->ref >= 1);
375     assert(target);
376     assert(target->memblock);
377     assert(target->length);
378     assert(target->memblock->data);
379
380     pa_sink_ref(s);
381     
382     l = target->length;
383     d = 0;
384     while (l > 0) {
385         chunk = *target;
386         chunk.index += d;
387         chunk.length -= d;
388         
389         if (pa_sink_render_into(s, &chunk) < 0)
390             break;
391
392         d += chunk.length;
393         l -= chunk.length;
394     }
395
396     if (l > 0) {
397         chunk = *target;
398         chunk.index += d;
399         chunk.length -= d;
400         pa_silence_memchunk(&chunk, &s->sample_spec);
401     }
402
403     pa_sink_unref(s);
404 }
405
406 void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
407     assert(s);
408     assert(s->ref >= 1);
409     assert(length);
410     assert(result);
411
412     /*** This needs optimization ***/
413     
414     result->memblock = pa_memblock_new(result->length = length, s->core->memblock_stat);
415     result->index = 0;
416
417     pa_sink_render_into_full(s, result);
418 }
419
420 pa_usec_t pa_sink_get_latency(pa_sink *s) {
421     assert(s);
422     assert(s->ref >= 1);
423
424     if (!s->get_latency)
425         return 0;
426
427     return s->get_latency(s);
428 }
429
430 void pa_sink_set_owner(pa_sink *s, pa_module *m) {
431     assert(s);
432     assert(s->ref >= 1);
433            
434     s->owner = m;
435
436     if (s->monitor_source)
437         pa_source_set_owner(s->monitor_source, m);
438 }
439
440 void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) {
441     pa_cvolume *v;
442     
443     assert(s);
444     assert(s->ref >= 1);
445     assert(volume);
446
447     if (m == PA_MIXER_HARDWARE && s->set_hw_volume) 
448         v = &s->hw_volume;
449     else
450         v = &s->sw_volume;
451
452     if (pa_cvolume_equal(v, volume))
453         return;
454         
455     *v = *volume;
456
457     if (v == &s->hw_volume)
458         if (s->set_hw_volume(s) < 0)
459             s->sw_volume =  *volume;
460
461     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
462 }
463
464 const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) {
465     assert(s);
466     assert(s->ref >= 1);
467
468     if (m == PA_MIXER_HARDWARE && s->set_hw_volume) {
469
470         if (s->get_hw_volume)
471             s->get_hw_volume(s);
472         
473         return &s->hw_volume;
474     } else
475         return &s->sw_volume;
476 }
477
478 void pa_sink_set_mute(pa_sink *s, pa_mixer_t m, int mute) {
479     int *t;
480     
481     assert(s);
482     assert(s->ref >= 1);
483
484     if (m == PA_MIXER_HARDWARE && s->set_hw_mute) 
485         t = &s->hw_muted;
486     else
487         t = &s->sw_muted;
488
489     if (!!*t == !!mute)
490         return;
491         
492     *t = !!mute;
493
494     if (t == &s->hw_muted)
495         if (s->set_hw_mute(s) < 0)
496             s->sw_muted = !!mute;
497
498     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
499 }
500
501 int pa_sink_get_mute(pa_sink *s, pa_mixer_t m) {
502     assert(s);
503     assert(s->ref >= 1);
504
505     if (m == PA_MIXER_HARDWARE && s->set_hw_mute) {
506
507         if (s->get_hw_mute)
508             s->get_hw_mute(s);
509         
510         return s->hw_muted;
511     } else
512         return s->sw_muted;
513 }