]> code.delx.au - pulseaudio/blob - src/pulsecore/source.c
9bb2d342d8d2ec3efcc39987d9f525f8c81980aa
[pulseaudio] / src / pulsecore / source.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2004-2006 Lennart Poettering
7 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
8
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published
11 by the Free Software Foundation; either version 2 of the License,
12 or (at your option) any later version.
13
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 USA.
23 ***/
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include <pulse/utf8.h>
35 #include <pulse/xmalloc.h>
36
37 #include <pulsecore/source-output.h>
38 #include <pulsecore/namereg.h>
39 #include <pulsecore/core-subscribe.h>
40 #include <pulsecore/log.h>
41 #include <pulsecore/sample-util.h>
42
43 #include "source.h"
44
45 #define CHECK_VALIDITY_RETURN_NULL(condition) \
46 do {\
47 if (!(condition)) \
48 return NULL; \
49 } while (0)
50
51 pa_source* pa_source_new(
52 pa_core *core,
53 const char *driver,
54 const char *name,
55 int fail,
56 const pa_sample_spec *spec,
57 const pa_channel_map *map) {
58
59 pa_source *s;
60 char st[256];
61 int r;
62 pa_channel_map tmap;
63
64 assert(core);
65 assert(name);
66 assert(spec);
67
68 CHECK_VALIDITY_RETURN_NULL(pa_sample_spec_valid(spec));
69
70 if (!map)
71 map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT);
72
73 CHECK_VALIDITY_RETURN_NULL(map && pa_channel_map_valid(map));
74 CHECK_VALIDITY_RETURN_NULL(map->channels == spec->channels);
75 CHECK_VALIDITY_RETURN_NULL(!driver || pa_utf8_valid(driver));
76 CHECK_VALIDITY_RETURN_NULL(pa_utf8_valid(name) && *name);
77
78 s = pa_xnew(pa_source, 1);
79
80 if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) {
81 pa_xfree(s);
82 return NULL;
83 }
84
85 s->ref = 1;
86 s->core = core;
87 s->state = PA_SOURCE_RUNNING;
88 s->name = pa_xstrdup(name);
89 s->description = NULL;
90 s->driver = pa_xstrdup(driver);
91 s->owner = NULL;
92
93 s->sample_spec = *spec;
94 s->channel_map = *map;
95
96 s->outputs = pa_idxset_new(NULL, NULL);
97 s->monitor_of = NULL;
98
99 pa_cvolume_reset(&s->sw_volume, spec->channels);
100 pa_cvolume_reset(&s->hw_volume, spec->channels);
101 s->sw_muted = 0;
102 s->hw_muted = 0;
103
104 s->is_hardware = 0;
105
106 s->get_latency = NULL;
107 s->notify = NULL;
108 s->set_hw_volume = NULL;
109 s->get_hw_volume = NULL;
110 s->set_hw_mute = NULL;
111 s->get_hw_mute = NULL;
112 s->userdata = NULL;
113
114 r = pa_idxset_put(core->sources, s, &s->index);
115 assert(s->index != PA_IDXSET_INVALID && r >= 0);
116
117 pa_sample_spec_snprint(st, sizeof(st), spec);
118 pa_log_info("created %u \"%s\" with sample spec \"%s\"", s->index, s->name, st);
119
120 pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
121
122 return s;
123 }
124
125 void pa_source_disconnect(pa_source *s) {
126 pa_source_output *o, *j = NULL;
127
128 assert(s);
129 assert(s->state == PA_SOURCE_RUNNING);
130
131 s->state = PA_SOURCE_DISCONNECTED;
132 pa_namereg_unregister(s->core, s->name);
133
134 pa_hook_fire(&s->core->hook_source_disconnect, s);
135
136 while ((o = pa_idxset_first(s->outputs, NULL))) {
137 assert(o != j);
138 pa_source_output_kill(o);
139 j = o;
140 }
141
142 pa_idxset_remove_by_data(s->core->sources, s, NULL);
143
144 s->get_latency = NULL;
145 s->notify = NULL;
146 s->get_hw_volume = NULL;
147 s->set_hw_volume = NULL;
148 s->set_hw_mute = NULL;
149 s->get_hw_mute = NULL;
150
151 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
152 }
153
154 static void source_free(pa_source *s) {
155 assert(s);
156 assert(!s->ref);
157
158 if (s->state != PA_SOURCE_DISCONNECTED)
159 pa_source_disconnect(s);
160
161 pa_log_info("freed %u \"%s\"", s->index, s->name);
162
163 pa_idxset_free(s->outputs, NULL, NULL);
164
165 pa_xfree(s->name);
166 pa_xfree(s->description);
167 pa_xfree(s->driver);
168 pa_xfree(s);
169 }
170
171 void pa_source_unref(pa_source *s) {
172 assert(s);
173 assert(s->ref >= 1);
174
175 if (!(--s->ref))
176 source_free(s);
177 }
178
179 pa_source* pa_source_ref(pa_source *s) {
180 assert(s);
181 assert(s->ref >= 1);
182
183 s->ref++;
184 return s;
185 }
186
187 void pa_source_notify(pa_source*s) {
188 assert(s);
189 assert(s->ref >= 1);
190
191 if (s->notify)
192 s->notify(s);
193 }
194
195 static int do_post(void *p, PA_GCC_UNUSED uint32_t idx, PA_GCC_UNUSED int *del, void*userdata) {
196 pa_source_output *o = p;
197 const pa_memchunk *chunk = userdata;
198
199 assert(o);
200 assert(chunk);
201
202 pa_source_output_push(o, chunk);
203 return 0;
204 }
205
206 void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
207 assert(s);
208 assert(s->ref >= 1);
209 assert(chunk);
210
211 pa_source_ref(s);
212
213 if (s->sw_muted || !pa_cvolume_is_norm(&s->sw_volume)) {
214 pa_memchunk vchunk = *chunk;
215
216 pa_memblock_ref(vchunk.memblock);
217 pa_memchunk_make_writable(&vchunk, 0);
218 if (s->sw_muted)
219 pa_silence_memchunk(&vchunk, &s->sample_spec);
220 else
221 pa_volume_memchunk(&vchunk, &s->sample_spec, &s->sw_volume);
222 pa_idxset_foreach(s->outputs, do_post, &vchunk);
223 pa_memblock_unref(vchunk.memblock);
224 } else
225 pa_idxset_foreach(s->outputs, do_post, (void*) chunk);
226
227 pa_source_unref(s);
228 }
229
230 void pa_source_set_owner(pa_source *s, pa_module *m) {
231 assert(s);
232 assert(s->ref >= 1);
233
234 if (m == s->owner)
235 return;
236
237 s->owner = m;
238 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
239 }
240
241 pa_usec_t pa_source_get_latency(pa_source *s) {
242 assert(s);
243 assert(s->ref >= 1);
244
245 if (!s->get_latency)
246 return 0;
247
248 return s->get_latency(s);
249 }
250
251 void pa_source_set_volume(pa_source *s, pa_mixer_t m, const pa_cvolume *volume) {
252 pa_cvolume *v;
253
254 assert(s);
255 assert(s->ref >= 1);
256 assert(volume);
257
258 if (m == PA_MIXER_HARDWARE && s->set_hw_volume)
259 v = &s->hw_volume;
260 else
261 v = &s->sw_volume;
262
263 if (pa_cvolume_equal(v, volume))
264 return;
265
266 *v = *volume;
267
268 if (v == &s->hw_volume)
269 if (s->set_hw_volume(s) < 0)
270 s->sw_volume = *volume;
271
272 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
273 }
274
275 const pa_cvolume *pa_source_get_volume(pa_source *s, pa_mixer_t m) {
276 assert(s);
277 assert(s->ref >= 1);
278
279 if (m == PA_MIXER_HARDWARE && s->set_hw_volume) {
280
281 if (s->get_hw_volume)
282 s->get_hw_volume(s);
283
284 return &s->hw_volume;
285 } else
286 return &s->sw_volume;
287 }
288
289 void pa_source_set_mute(pa_source *s, pa_mixer_t m, int mute) {
290 int *t;
291
292 assert(s);
293 assert(s->ref >= 1);
294
295 if (m == PA_MIXER_HARDWARE && s->set_hw_mute)
296 t = &s->hw_muted;
297 else
298 t = &s->sw_muted;
299
300 if (!!*t == !!mute)
301 return;
302
303 *t = !!mute;
304
305 if (t == &s->hw_muted)
306 if (s->set_hw_mute(s) < 0)
307 s->sw_muted = !!mute;
308
309 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
310 }
311
312 int pa_source_get_mute(pa_source *s, pa_mixer_t m) {
313 assert(s);
314 assert(s->ref >= 1);
315
316 if (m == PA_MIXER_HARDWARE && s->set_hw_mute) {
317
318 if (s->get_hw_mute)
319 s->get_hw_mute(s);
320
321 return s->hw_muted;
322 } else
323 return s->sw_muted;
324 }
325
326 void pa_source_set_description(pa_source *s, const char *description) {
327 assert(s);
328 assert(s->ref >= 1);
329
330 if (!description && !s->description)
331 return;
332
333 if (description && s->description && !strcmp(description, s->description))
334 return;
335
336 pa_xfree(s->description);
337 s->description = pa_xstrdup(description);
338
339 pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
340 }
341
342 unsigned pa_source_used_by(pa_source *s) {
343 assert(s);
344 assert(s->ref >= 1);
345
346 return pa_idxset_size(s->outputs);
347 }