]> code.delx.au - pulseaudio/blob - polyp/module-alsa-source.c
introduce pa_xmalloc() and friends
[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 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 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 #include <sys/poll.h>
29
30 #include <asoundlib.h>
31
32 #include "module.h"
33 #include "core.h"
34 #include "memchunk.h"
35 #include "sink.h"
36 #include "modargs.h"
37 #include "util.h"
38 #include "sample-util.h"
39 #include "alsa-util.h"
40 #include "xmalloc.h"
41
42 struct userdata {
43 snd_pcm_t *pcm_handle;
44 struct pa_source *source;
45 void **io_sources;
46 unsigned n_io_sources;
47
48 size_t frame_size, fragment_size;
49 struct pa_memchunk memchunk;
50 struct pa_module *module;
51 };
52
53 static const char* const valid_modargs[] = {
54 "device",
55 "source_name",
56 "format",
57 "channels",
58 "rate",
59 "fragments",
60 "fragment_size",
61 NULL
62 };
63
64 #define DEFAULT_SOURCE_NAME "alsa_input"
65 #define DEFAULT_DEVICE "hw:0,0"
66
67 static void update_usage(struct userdata *u) {
68 pa_module_set_used(u->module,
69 (u->source ? pa_idxset_ncontents(u->source->outputs) : 0));
70 }
71
72 static void xrun_recovery(struct userdata *u) {
73 assert(u);
74
75 fprintf(stderr, "*** ALSA-XRUN (capture) ***\n");
76
77 if (snd_pcm_prepare(u->pcm_handle) < 0)
78 fprintf(stderr, "snd_pcm_prepare() failed\n");
79 }
80
81 static void do_read(struct userdata *u) {
82 assert(u);
83
84 update_usage(u);
85
86 for (;;) {
87 struct pa_memchunk post_memchunk;
88 snd_pcm_sframes_t frames;
89 size_t l;
90
91 if (!u->memchunk.memblock) {
92 u->memchunk.memblock = pa_memblock_new(u->memchunk.length = u->fragment_size);
93 u->memchunk.index = 0;
94 }
95
96 assert(u->memchunk.memblock && u->memchunk.memblock->data && u->memchunk.length && u->memchunk.memblock->length && (u->memchunk.length % u->frame_size) == 0);
97
98 if ((frames = snd_pcm_readi(u->pcm_handle, u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) {
99 if (frames == -EAGAIN)
100 return;
101
102 if (frames == -EPIPE) {
103 xrun_recovery(u);
104 continue;
105 }
106
107 fprintf(stderr, "snd_pcm_readi() failed: %s\n", strerror(-frames));
108 return;
109 }
110
111 l = frames * u->frame_size;
112
113 post_memchunk = u->memchunk;
114 post_memchunk.length = l;
115
116 pa_source_post(u->source, &post_memchunk);
117
118 u->memchunk.index += l;
119 u->memchunk.length -= l;
120
121 if (u->memchunk.length == 0) {
122 pa_memblock_unref(u->memchunk.memblock);
123 u->memchunk.memblock = NULL;
124 u->memchunk.index = u->memchunk.length = 0;
125 }
126
127 break;
128 }
129 }
130
131 static void io_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
132 struct userdata *u = userdata;
133 assert(u && a && id);
134
135 if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
136 xrun_recovery(u);
137
138 do_read(u);
139 }
140
141 int pa_module_init(struct pa_core *c, struct pa_module*m) {
142 struct pa_modargs *ma = NULL;
143 int ret = -1;
144 struct userdata *u = NULL;
145 const char *dev;
146 struct pa_sample_spec ss;
147 unsigned periods, fragsize;
148 snd_pcm_uframes_t buffer_size;
149 size_t frame_size;
150
151 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
152 fprintf(stderr, __FILE__": failed to parse module arguments\n");
153 goto fail;
154 }
155
156 ss = c->default_sample_spec;
157 if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
158 fprintf(stderr, __FILE__": failed to parse sample specification\n");
159 goto fail;
160 }
161 frame_size = pa_frame_size(&ss);
162
163 periods = 12;
164 fragsize = 1024;
165 if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) {
166 fprintf(stderr, __FILE__": failed to parse buffer metrics\n");
167 goto fail;
168 }
169 buffer_size = fragsize/frame_size*periods;
170
171 u = pa_xmalloc0(sizeof(struct userdata));
172 m->userdata = u;
173 u->module = m;
174
175 if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0) {
176 fprintf(stderr, __FILE__": Error opening PCM device %s\n", dev);
177 goto fail;
178 }
179
180 if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &buffer_size) < 0) {
181 fprintf(stderr, __FILE__": Failed to set hardware parameters\n");
182 goto fail;
183 }
184
185 u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss);
186 assert(u->source);
187
188 u->source->userdata = u;
189 pa_source_set_owner(u->source, m);
190 u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev);
191
192 if (pa_create_io_sources(u->pcm_handle, c->mainloop, &u->io_sources, &u->n_io_sources, io_callback, u) < 0) {
193 fprintf(stderr, __FILE__": failed to obtain file descriptors\n");
194 goto fail;
195 }
196
197 u->frame_size = frame_size;
198 u->fragment_size = buffer_size*u->frame_size/periods;
199
200 fprintf(stderr, __FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size);
201
202 u->memchunk.memblock = NULL;
203 u->memchunk.index = u->memchunk.length = 0;
204
205 snd_pcm_start(u->pcm_handle);
206
207 ret = 0;
208
209 finish:
210 if (ma)
211 pa_modargs_free(ma);
212
213 return ret;
214
215 fail:
216
217 if (u)
218 pa_module_done(c, m);
219
220 goto finish;
221 }
222
223 void pa_module_done(struct pa_core *c, struct pa_module*m) {
224 struct userdata *u;
225 assert(c && m);
226
227 if (!(u = m->userdata))
228 return;
229
230 if (u->source)
231 pa_source_free(u->source);
232
233 if (u->io_sources)
234 pa_free_io_sources(c->mainloop, u->io_sources, u->n_io_sources);
235
236 if (u->pcm_handle) {
237 snd_pcm_drop(u->pcm_handle);
238 snd_pcm_close(u->pcm_handle);
239 }
240
241 if (u->memchunk.memblock)
242 pa_memblock_unref(u->memchunk.memblock);
243
244 pa_xfree(u);
245 }
246