]> code.delx.au - pulseaudio/blob - src/module-alsa-sink.c
add alsa sink
[pulseaudio] / src / module-alsa-sink.c
1 #include <assert.h>
2 #include <stdio.h>
3 #include <sys/poll.h>
4
5 #include <asoundlib.h>
6
7 #include "module.h"
8 #include "core.h"
9 #include "memchunk.h"
10 #include "sink.h"
11 #include "modargs.h"
12 #include "util.h"
13 #include "sample-util.h"
14
15 struct userdata {
16 snd_pcm_t *pcm_handle;
17 struct pa_sink *sink;
18 void **io_sources;
19 unsigned n_io_sources;
20
21 size_t frame_size, fragment_size;
22 struct pa_memchunk memchunk, silence;
23 };
24
25 static const char* const valid_modargs[] = {
26 "device",
27 "sink_name",
28 NULL
29 };
30
31 #define DEFAULT_SINK_NAME "alsa_output"
32 #define DEFAULT_DEVICE "plughw:0,0"
33
34 static void xrun_recovery(struct userdata *u) {
35 assert(u);
36
37 fprintf(stderr, "*** XRUN ***\n");
38
39 if (snd_pcm_prepare(u->pcm_handle) < 0)
40 fprintf(stderr, "snd_pcm_prepare() failed\n");
41 }
42
43 static void do_write(struct userdata *u) {
44 assert(u);
45
46 for (;;) {
47 struct pa_memchunk *memchunk = NULL;
48 snd_pcm_sframes_t frames;
49
50 if (u->memchunk.memblock)
51 memchunk = &u->memchunk;
52 else {
53 if (pa_sink_render(u->sink, u->fragment_size, &u->memchunk) < 0)
54 memchunk = &u->silence;
55 else
56 memchunk = &u->memchunk;
57 }
58
59 assert(memchunk->memblock && memchunk->memblock->data && memchunk->length && memchunk->memblock->length && (memchunk->length % u->frame_size) == 0);
60
61 assert(u->pcm_handle);
62 assert(memchunk->memblock);
63 assert(memchunk->memblock->data);
64 assert(memchunk->length);
65 assert(u->frame_size);
66
67 if ((frames = snd_pcm_writei(u->pcm_handle, memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) {
68 if (frames == -EPIPE) {
69 xrun_recovery(u);
70 continue;
71 }
72
73 fprintf(stderr, "snd_pcm_writei() failed\n");
74 return;
75 }
76
77 if (memchunk == &u->memchunk) {
78 memchunk->index += frames * u->frame_size;
79 memchunk->length -= frames * u->frame_size;
80
81 if (memchunk->length == 0) {
82 pa_memblock_unref(memchunk->memblock);
83 memchunk->memblock = NULL;
84 memchunk->index = memchunk->length = 0;
85 }
86 }
87
88 break;
89 }
90 }
91
92 static void io_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
93 struct userdata *u = userdata;
94 assert(u && a && id);
95
96 if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN)
97 xrun_recovery(u);
98
99 do_write(u);
100 }
101
102 static uint32_t sink_get_latency_cb(struct pa_sink *s) {
103 struct userdata *u = s->userdata;
104 snd_pcm_sframes_t frames;
105 assert(s && u && u->sink);
106
107 if (snd_pcm_delay(u->pcm_handle, &frames) < 0) {
108 fprintf(stderr, __FILE__": failed to get delay\n");
109 s->get_latency = NULL;
110 return 0;
111 }
112
113 if (frames < 0)
114 frames = 0;
115
116 return pa_samples_usec(frames * u->frame_size, &s->sample_spec);
117 }
118
119 int pa_module_init(struct pa_core *c, struct pa_module*m) {
120 struct pa_modargs *ma = NULL;
121 int ret = -1;
122 struct userdata *u = NULL;
123 snd_pcm_hw_params_t *hwparams;
124 const char *dev;
125 struct pollfd *pfds, *ppfd;
126 struct pa_sample_spec ss;
127 unsigned i, periods;
128 snd_pcm_uframes_t buffer_size;
129 void ** ios;
130
131 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
132 fprintf(stderr, __FILE__": failed to parse module arguments\n");
133 goto fail;
134 }
135
136 u = malloc(sizeof(struct userdata));
137 assert(u);
138 memset(u, 0, sizeof(struct userdata));
139 m->userdata = u;
140
141 if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) {
142 fprintf(stderr, "Error opening PCM device %s\n", dev);
143 goto fail;
144 }
145
146 ss.format = PA_SAMPLE_S16LE;
147 ss.rate = 44100;
148 ss.channels = 2;
149
150 periods = 12;
151 buffer_size = periods*256;
152
153 snd_pcm_hw_params_alloca(&hwparams);
154 if (snd_pcm_hw_params_any(u->pcm_handle, hwparams) < 0 ||
155 snd_pcm_hw_params_set_access(u->pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 ||
156 snd_pcm_hw_params_set_format(u->pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0 ||
157 snd_pcm_hw_params_set_rate_near(u->pcm_handle, hwparams, &ss.rate, NULL) < 0 ||
158 snd_pcm_hw_params_set_channels(u->pcm_handle, hwparams, ss.channels) < 0 ||
159 snd_pcm_hw_params_set_periods_near(u->pcm_handle, hwparams, &periods, NULL) < 0 ||
160 snd_pcm_hw_params_set_buffer_size_near(u->pcm_handle, hwparams, &buffer_size) < 0 ||
161 snd_pcm_hw_params(u->pcm_handle, hwparams) < 0) {
162 fprintf(stderr, "Error setting HW params.\n");
163 goto fail;
164 }
165
166 u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss);
167 assert(u->sink);
168
169 u->sink->get_latency = sink_get_latency_cb;
170 u->sink->userdata = u;
171 pa_sink_set_owner(u->sink, m);
172 u->sink->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev);
173
174 u->n_io_sources = snd_pcm_poll_descriptors_count(u->pcm_handle);
175
176 pfds = malloc(sizeof(struct pollfd) * u->n_io_sources);
177 assert(pfds);
178 if (snd_pcm_poll_descriptors(u->pcm_handle, pfds, u->n_io_sources) < 0) {
179 printf("Unable to obtain poll descriptors for playback.\n");
180 free(pfds);
181 goto fail;
182 }
183
184 u->io_sources = malloc(sizeof(void*) * u->n_io_sources);
185 assert(u->io_sources);
186
187 for (i = 0, ios = u->io_sources, ppfd = pfds; i < u->n_io_sources; i++, ios++, ppfd++) {
188 *ios = c->mainloop->source_io(c->mainloop, ppfd->fd,
189 ((ppfd->events & POLLIN) ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) |
190 ((ppfd->events & POLLOUT) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), io_callback, u);
191 assert(*ios);
192 }
193
194 free(pfds);
195
196 u->frame_size = pa_sample_size(&ss);
197 u->fragment_size = buffer_size*u->frame_size/periods;
198
199 fprintf(stderr, __FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size);
200
201 u->silence.memblock = pa_memblock_new(u->silence.length = u->fragment_size);
202 assert(u->silence.memblock);
203 pa_silence_memblock(u->silence.memblock, &ss);
204 u->silence.index = 0;
205
206 u->memchunk.memblock = NULL;
207 u->memchunk.index = u->memchunk.length = 0;
208
209 ret = 0;
210
211 finish:
212 if (ma)
213 pa_modargs_free(ma);
214
215 return ret;
216
217 fail:
218
219 if (u)
220 pa_module_done(c, m);
221
222 goto finish;
223 }
224
225 void pa_module_done(struct pa_core *c, struct pa_module*m) {
226 struct userdata *u;
227 assert(c && m);
228
229 if ((u = m->userdata)) {
230 unsigned i;
231 void **ios;
232
233 pa_sink_free(u->sink);
234
235 for (ios = u->io_sources, i = 0; i < u->n_io_sources; i++, ios++)
236 c->mainloop->cancel_io(c->mainloop, *ios);
237 free(u->io_sources);
238
239 if (u->pcm_handle) {
240 snd_pcm_drop(u->pcm_handle);
241 snd_pcm_close(u->pcm_handle);
242 }
243
244 if (u->memchunk.memblock)
245 pa_memblock_unref(u->memchunk.memblock);
246 if (u->silence.memblock)
247 pa_memblock_unref(u->silence.memblock);
248
249 free(u);
250 }
251 }
252