some more work
[profile/ivi/pulseaudio.git] / src / sink.c
1 #include <stdlib.h>
2 #include <assert.h>
3 #include <string.h>
4 #include <stdio.h>
5
6 #include "sink.h"
7 #include "sinkinput.h"
8 #include "strbuf.h"
9
10 #define MAX_MIX_CHANNELS 32
11
12 struct sink* sink_new(struct core *core, const char *name, const struct sample_spec *spec) {
13     struct sink *s;
14     char *n = NULL;
15     int r;
16     assert(core && spec);
17
18     s = malloc(sizeof(struct sink));
19     assert(s);
20     
21     s->name = name ? strdup(name) : NULL;
22     s->core = core;
23     s->sample_spec = *spec;
24     s->inputs = idxset_new(NULL, NULL);
25
26     if (name) {
27         n = malloc(strlen(name)+9);
28         sprintf(n, "%s_monitor", name);
29     }
30     
31     s->monitor_source = source_new(core, n, spec);
32     free(n);
33     
34     s->volume = 0xFF;
35
36     s->notify = NULL;
37     s->get_latency = NULL;
38     s->userdata = NULL;
39
40     r = idxset_put(core->sinks, s, &s->index);
41     assert(s->index != IDXSET_INVALID && r >= 0);
42     
43     fprintf(stderr, "sink: created %u \"%s\".\n", s->index, s->name);
44     
45     return s;
46 }
47
48 void sink_free(struct sink *s) {
49     struct sink_input *i, *j = NULL;
50     assert(s);
51
52     while ((i = idxset_first(s->inputs, NULL))) {
53         assert(i != j);
54         sink_input_kill(i);
55         j = i;
56     }
57     idxset_free(s->inputs, NULL, NULL);
58
59     source_free(s->monitor_source);
60     idxset_remove_by_data(s->core->sinks, s, NULL);
61
62     fprintf(stderr, "sink: freed %u \"%s\"\n", s->index, s->name);
63     
64     free(s->name);
65     free(s);
66 }
67
68 void sink_notify(struct sink*s) {
69     assert(s);
70
71     if (s->notify)
72         s->notify(s);
73 }
74
75 static unsigned fill_mix_info(struct sink *s, struct mix_info *info, unsigned maxinfo) {
76     uint32_t index = IDXSET_INVALID;
77     struct sink_input *i;
78     unsigned n = 0;
79     
80     assert(s && info);
81
82     for (i = idxset_first(s->inputs, &index); maxinfo > 0 && i; i = idxset_next(s->inputs, &index)) {
83         assert(i->peek);
84         if (i->peek(i, &info->chunk) < 0)
85             continue;
86
87         info->volume = i->volume;
88         
89         assert(info->chunk.memblock && info->chunk.memblock->data && info->chunk.length);
90         info->userdata = i;
91         
92         info++;
93         maxinfo--;
94         n++;
95     }
96
97     return n;
98 }
99
100 static void inputs_drop(struct sink *s, struct mix_info *info, unsigned maxinfo, size_t length) {
101     assert(s && info);
102     
103     for (; maxinfo > 0; maxinfo--, info++) {
104         struct sink_input *i = info->userdata;
105         assert(i && info->chunk.memblock);
106         
107         memblock_unref(info->chunk.memblock);
108         assert(i->drop);
109         i->drop(i, length);
110     }
111 }
112
113 int sink_render(struct sink*s, size_t length, struct memchunk *result) {
114     struct mix_info info[MAX_MIX_CHANNELS];
115     unsigned n;
116     size_t l;
117     assert(s && length && result);
118     
119     n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
120
121     if (n <= 0)
122         return -1;
123
124     if (n == 1) {
125         struct sink_info *i = info[0].userdata;
126         assert(i);
127         *result = info[0].chunk;
128         memblock_ref(result->memblock);
129
130         if (result->length > length)
131             result->length = length;
132
133         l = result->length;
134     } else {
135         result->memblock = memblock_new(length);
136         assert(result->memblock);
137
138         result->length = l = mix_chunks(info, n, result->memblock->data, length, &s->sample_spec, s->volume);
139         result->index = 0;
140         
141         assert(l);
142     }
143
144     inputs_drop(s, info, n, l);
145
146     assert(s->monitor_source);
147     source_post(s->monitor_source, result);
148
149     return 0;
150 }
151
152 int sink_render_into(struct sink*s, struct memchunk *target) {
153     struct mix_info info[MAX_MIX_CHANNELS];
154     unsigned n;
155     size_t l;
156     assert(s && target && target->length && target->memblock && target->memblock->data);
157     
158     n = fill_mix_info(s, info, MAX_MIX_CHANNELS);
159
160     if (n <= 0)
161         return -1;
162
163     if (n == 1) {
164         struct sink_info *i = info[0].userdata;
165         assert(i);
166
167         l = target->length;
168         if (l > info[0].chunk.length)
169             l = info[0].chunk.length;
170         
171         memcpy(target->memblock->data+target->index, info[0].chunk.memblock->data + info[0].chunk.index, l);
172         target->length = l;
173     } else
174         target->length = l = mix_chunks(info, n, target->memblock->data+target->index, target->length, &s->sample_spec, s->volume);
175     
176     assert(l);
177     inputs_drop(s, info, n, l);
178
179     assert(s->monitor_source);
180     source_post(s->monitor_source, target);
181
182     return 0;
183 }
184
185 void sink_render_into_full(struct sink *s, struct memchunk *target) {
186     struct memchunk chunk;
187     size_t l, d;
188     assert(s && target && target->memblock && target->length && target->memblock->data);
189
190     l = target->length;
191     d = 0;
192     while (l > 0) {
193         chunk = *target;
194         chunk.index += d;
195         chunk.length -= d;
196         
197         if (sink_render_into(s, &chunk) < 0)
198             break;
199
200         d += chunk.length;
201         l -= chunk.length;
202     }
203
204     if (l > 0) {
205         chunk = *target;
206         chunk.index += d;
207         chunk.length -= d;
208         silence_memchunk(&chunk, &s->sample_spec);
209     }
210 }
211
212 uint32_t sink_get_latency(struct sink *s) {
213     assert(s);
214
215     if (!s->get_latency)
216         return 0;
217
218     return s->get_latency(s);
219 }
220
221 struct sink* sink_get_default(struct core *c) {
222     struct sink *sink;
223     assert(c);
224
225     if ((sink = idxset_get_by_index(c->sinks, c->default_sink_index)))
226         return sink;
227
228     if (!(sink = idxset_first(c->sinks, &c->default_sink_index)))
229         return NULL;
230
231     fprintf(stderr, "core: default sink vanished, setting to %u.\n", sink->index);
232     return sink;
233 }
234
235 char *sink_list_to_string(struct core *c) {
236     struct strbuf *s;
237     struct sink *sink, *default_sink;
238     uint32_t index = IDXSET_INVALID;
239     assert(c);
240
241     s = strbuf_new();
242     assert(s);
243
244     strbuf_printf(s, "%u sink(s) available.\n", idxset_ncontents(c->sinks));
245
246     default_sink = sink_get_default(c);
247     
248     for (sink = idxset_first(c->sinks, &index); sink; sink = idxset_next(c->sinks, &index)) {
249         assert(sink->monitor_source);
250         strbuf_printf(s, "  %c index: %u, name: <%s>, volume: <0x%02x>, latency: <%u usec>, monitor_source: <%u>\n", sink == default_sink ? '*' : ' ', sink->index, sink->name, (unsigned) sink->volume, sink_get_latency(sink), sink->monitor_source->index);
251     }
252     
253     return strbuf_tostring_free(s);
254 }