]> code.delx.au - pulseaudio/blob - polyp/module-alsa-source.c
add simple hardware auto detection module
[pulseaudio] / polyp / module-alsa-source.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 <assert.h>
27 #include <stdio.h>
28
29 #ifdef HAVE_SYS_POLL_H
30 #include <sys/poll.h>
31 #else
32 #include "poll.h"
33 #endif
34
35 #include <asoundlib.h>
36
37 #include "module.h"
38 #include "core.h"
39 #include "memchunk.h"
40 #include "sink.h"
41 #include "modargs.h"
42 #include "util.h"
43 #include "sample-util.h"
44 #include "alsa-util.h"
45 #include "xmalloc.h"
46 #include "log.h"
47 #include "module-alsa-source-symdef.h"
48
49 PA_MODULE_AUTHOR("Lennart Poettering")
50 PA_MODULE_DESCRIPTION("ALSA Source")
51 PA_MODULE_VERSION(PACKAGE_VERSION)
52 PA_MODULE_USAGE("source_name=<name for the source> device=<ALSA device> format=<sample format> channels=<number of channels> rate=<sample rate> fragments=<number of fragments> fragment_size=<fragment size>")
53
54 struct userdata {
55 snd_pcm_t *pcm_handle;
56 pa_source *source;
57 pa_io_event **io_events;
58 unsigned n_io_events;
59
60 size_t frame_size, fragment_size;
61 pa_memchunk memchunk;
62 pa_module *module;
63 };
64
65 static const char* const valid_modargs[] = {
66 "device",
67 "source_name",
68 "channels",
69 "rate",
70 "format",
71 "fragments",
72 "fragment_size",
73 NULL
74 };
75
76 #define DEFAULT_SOURCE_NAME "alsa_input"
77 #define DEFAULT_DEVICE "hw:0,0"
78
79 static void update_usage(struct userdata *u) {
80 pa_module_set_used(u->module,
81 (u->source ? pa_idxset_size(u->source->outputs) : 0));
82 }
83
84 static void xrun_recovery(struct userdata *u) {
85 assert(u);
86
87 pa_log(__FILE__": *** ALSA-XRUN (capture) ***\n");
88
89 if (snd_pcm_prepare(u->pcm_handle) < 0)
90 pa_log(__FILE__": snd_pcm_prepare() failed\n");
91 }
92
93 static void do_read(struct userdata *u) {
94 assert(u);
95
96 update_usage(u);
97
98 for (;;) {
99 pa_memchunk post_memchunk;
100 snd_pcm_sframes_t frames;
101 size_t l;
102
103 if (!u->memchunk.memblock) {
104 u->memchunk.memblock = pa_memblock_new(u->memchunk.length = u->fragment_size, u->source->core->memblock_stat);
105 u->memchunk.index = 0;
106 }
107
108 assert(u->memchunk.memblock && u->memchunk.memblock->data && u->memchunk.length && u->memchunk.memblock->length && (u->memchunk.length % u->frame_size) == 0);
109
110 if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) {
111 if (frames == -EAGAIN)
112 return;
113
114 if (frames == -EPIPE) {
115 xrun_recovery(u);
116 continue;
117 }
118
119 pa_log(__FILE__": snd_pcm_readi() failed: %s\n", strerror(-frames));
120 return;
121 }
122
123 l = frames * u->frame_size;
124
125 post_memchunk = u->memchunk;
126 post_memchunk.length = l;
127
128 pa_source_post(u->source, &post_memchunk);
129
130 u->memchunk.index += l;
131 u->memchunk.length -= l;
132
133 if (u->memchunk.length == 0) {
134 pa_memblock_unref(u->memchunk.memblock);
135 u->memchunk.memblock = NULL;
136 u->memchunk.index = u->memchunk.length = 0;
137 }
138
139 break;
140 }
141 }
142
143 static void io_callback(pa_mainloop_api*a, pa_io_event *e, PA_GCC_UNUSED int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
144 struct userdata *u = userdata;
145 assert(u && a && e);
146
147 if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
148 xrun_recovery(u);
149
150 do_read(u);
151 }
152
153 static pa_usec_t source_get_latency_cb(pa_source *s) {
154 struct userdata *u = s->userdata;
155 snd_pcm_sframes_t frames;
156 assert(s && u && u->source);
157
158 if (snd_pcm_delay(u->pcm_handle, &frames) < 0) {
159 pa_log(__FILE__": failed to get delay\n");
160 s->get_latency = NULL;
161 return 0;
162 }
163
164 return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec);
165 }
166
167 int pa__init(pa_core *c, pa_module*m) {
168 pa_modargs *ma = NULL;
169 int ret = -1;
170 struct userdata *u = NULL;
171 const char *dev;
172 pa_sample_spec ss;
173 unsigned periods, fragsize;
174 snd_pcm_uframes_t period_size;
175 size_t frame_size;
176 int err;
177
178 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
179 pa_log(__FILE__": failed to parse module arguments\n");
180 goto fail;
181 }
182
183 ss = c->default_sample_spec;
184 if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
185 pa_log(__FILE__": failed to parse sample specification\n");
186 goto fail;
187 }
188 frame_size = pa_frame_size(&ss);
189
190 periods = 12;
191 fragsize = 1024;
192 if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) {
193 pa_log(__FILE__": failed to parse buffer metrics\n");
194 goto fail;
195 }
196 period_size = fragsize;
197
198 u = pa_xmalloc0(sizeof(struct userdata));
199 m->userdata = u;
200 u->module = m;
201
202 snd_config_update_free_global();
203 if ((err = snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
204 pa_log(__FILE__": Error opening PCM device %s: %s\n", dev, snd_strerror(err));
205 goto fail;
206 }
207
208 if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size)) < 0) {
209 pa_log(__FILE__": Failed to set hardware parameters: %s\n", snd_strerror(err));
210 goto fail;
211 }
212
213 u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL);
214 assert(u->source);
215
216 u->source->userdata = u;
217 u->source->get_latency = source_get_latency_cb;
218 pa_source_set_owner(u->source, m);
219 u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev);
220
221 if (pa_create_io_events(u->pcm_handle, c->mainloop, &u->io_events, &u->n_io_events, io_callback, u) < 0) {
222 pa_log(__FILE__": failed to obtain file descriptors\n");
223 goto fail;
224 }
225
226 u->frame_size = frame_size;
227 u->fragment_size = period_size;
228
229 pa_log(__FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size);
230
231 u->memchunk.memblock = NULL;
232 u->memchunk.index = u->memchunk.length = 0;
233
234 snd_pcm_start(u->pcm_handle);
235
236 ret = 0;
237
238 finish:
239 if (ma)
240 pa_modargs_free(ma);
241
242 return ret;
243
244 fail:
245
246 if (u)
247 pa__done(c, m);
248
249 goto finish;
250 }
251
252 void pa__done(pa_core *c, pa_module*m) {
253 struct userdata *u;
254 assert(c && m);
255
256 if (!(u = m->userdata))
257 return;
258
259 if (u->source) {
260 pa_source_disconnect(u->source);
261 pa_source_unref(u->source);
262 }
263
264 if (u->io_events)
265 pa_free_io_events(c->mainloop, u->io_events, u->n_io_events);
266
267 if (u->pcm_handle) {
268 snd_pcm_drop(u->pcm_handle);
269 snd_pcm_close(u->pcm_handle);
270 }
271
272 if (u->memchunk.memblock)
273 pa_memblock_unref(u->memchunk.memblock);
274
275 pa_xfree(u);
276 }
277