if S16NE is not supported, fall back to S16RE. If FLOAT32NE is not supported, fall...
[profile/ivi/pulseaudio-panda.git] / src / modules / alsa-util.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 <sys/types.h>
27 #include <asoundlib.h>
28
29 #include <polyp/sample.h>
30 #include <polyp/xmalloc.h>
31
32 #include <polypcore/log.h>
33
34 #include "alsa-util.h"
35
36 struct pa_alsa_fdlist {
37     int num_fds;
38     struct pollfd *fds;
39     /* This is a temporary buffer used to avoid lots of mallocs */
40     struct pollfd *work_fds;
41
42     snd_pcm_t *pcm;
43     snd_mixer_t *mixer;
44
45     pa_mainloop_api *m;
46     pa_defer_event *defer;
47     pa_io_event **ios;
48
49     int polled;
50
51     void (*cb)(void *userdata);
52     void *userdata;
53 };
54
55 static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void *userdata) {
56     struct pa_alsa_fdlist *fdl = (struct pa_alsa_fdlist*)userdata;
57     int err, i;
58     unsigned short revents;
59
60     assert(a && fdl && (fdl->pcm || fdl->mixer) && fdl->fds && fdl->work_fds);
61
62     if (fdl->polled)
63         return;
64
65     fdl->polled = 1;
66
67     memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
68
69     for (i = 0;i < fdl->num_fds;i++) {
70         if (e == fdl->ios[i]) {
71             if (events & PA_IO_EVENT_INPUT)
72                 fdl->work_fds[i].revents |= POLLIN;
73             if (events & PA_IO_EVENT_OUTPUT)
74                 fdl->work_fds[i].revents |= POLLOUT;
75             if (events & PA_IO_EVENT_ERROR)
76                 fdl->work_fds[i].revents |= POLLERR;
77             if (events & PA_IO_EVENT_HANGUP)
78                 fdl->work_fds[i].revents |= POLLHUP;
79             break;
80         }
81     }
82
83     assert(i != fdl->num_fds);
84
85     if (fdl->pcm)
86         err = snd_pcm_poll_descriptors_revents(fdl->pcm, fdl->work_fds, fdl->num_fds, &revents);
87     else
88         err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents);
89
90     if (err < 0) {
91         pa_log_error(__FILE__": Unable to get poll revent: %s",
92             snd_strerror(err));
93         return;
94     }
95
96     if (revents) {
97         if (fdl->pcm)
98             fdl->cb(fdl->userdata);
99         else
100             snd_mixer_handle_events(fdl->mixer);
101     }
102
103     a->defer_enable(fdl->defer, 1);
104 }
105
106 static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *userdata) {
107     struct pa_alsa_fdlist *fdl = (struct pa_alsa_fdlist*)userdata;
108     int num_fds, i, err;
109     struct pollfd *temp;
110
111     assert(a && fdl && (fdl->pcm || fdl->mixer));
112
113     a->defer_enable(fdl->defer, 0);
114
115     if (fdl->pcm)
116         num_fds = snd_pcm_poll_descriptors_count(fdl->pcm);
117     else
118         num_fds = snd_mixer_poll_descriptors_count(fdl->mixer);
119     assert(num_fds > 0);
120
121     if (num_fds != fdl->num_fds) {
122         if (fdl->fds)
123             pa_xfree(fdl->fds);
124         if (fdl->work_fds)
125             pa_xfree(fdl->work_fds);
126         fdl->fds = pa_xmalloc0(sizeof(struct pollfd) * num_fds);
127         fdl->work_fds = pa_xmalloc(sizeof(struct pollfd) * num_fds);
128     }
129
130     memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
131
132     if (fdl->pcm)
133         err = snd_pcm_poll_descriptors(fdl->pcm, fdl->work_fds, num_fds);
134     else
135         err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds);
136
137     if (err < 0) {
138         pa_log_error(__FILE__": Unable to get poll descriptors: %s",
139             snd_strerror(err));
140         return;
141     }
142
143     fdl->polled = 0;
144
145     if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
146         return;
147
148     if (fdl->ios) {
149         for (i = 0;i < fdl->num_fds;i++)
150             a->io_free(fdl->ios[i]);
151         if (num_fds != fdl->num_fds) {
152             pa_xfree(fdl->ios);
153             fdl->ios = pa_xmalloc(sizeof(pa_io_event*) * num_fds);
154             assert(fdl->ios);
155         }
156     } else {
157         fdl->ios = pa_xmalloc(sizeof(pa_io_event*) * num_fds);
158         assert(fdl->ios);
159     }
160
161     /* Swap pointers */
162     temp = fdl->work_fds;
163     fdl->work_fds = fdl->fds;
164     fdl->fds = temp;
165
166     fdl->num_fds = num_fds;
167
168     for (i = 0;i < num_fds;i++) {
169         fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
170             ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
171             ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
172             io_cb, fdl);
173         assert(fdl->ios[i]);
174     }
175 }
176
177 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
178     struct pa_alsa_fdlist *fdl;
179
180     fdl = pa_xmalloc(sizeof(struct pa_alsa_fdlist));
181     assert(fdl);
182
183     fdl->num_fds = 0;
184     fdl->fds = NULL;
185     fdl->work_fds = NULL;
186
187     fdl->pcm = NULL;
188     fdl->mixer = NULL;
189
190     fdl->m = NULL;
191     fdl->defer = NULL;
192     fdl->ios = NULL;
193
194     fdl->polled = 0;
195
196     return fdl;
197 }
198
199 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
200     assert(fdl);
201
202     if (fdl->defer) {
203         assert(fdl->m);
204         fdl->m->defer_free(fdl->defer);
205     }
206
207     if (fdl->ios) {
208         int i;
209         assert(fdl->m);
210         for (i = 0;i < fdl->num_fds;i++)
211             fdl->m->io_free(fdl->ios[0]);
212         pa_xfree(fdl->ios);
213     }
214
215     if (fdl->fds)
216         pa_xfree(fdl->fds);
217     if (fdl->work_fds)
218         pa_xfree(fdl->work_fds);
219
220     pa_xfree(fdl);
221 }
222
223 int pa_alsa_fdlist_init_pcm(struct pa_alsa_fdlist *fdl, snd_pcm_t *pcm_handle, pa_mainloop_api* m, void (*cb)(void *userdata), void *userdata) {
224     assert(fdl && pcm_handle && m && !fdl->m && cb);
225
226     fdl->pcm = pcm_handle;
227     fdl->m = m;
228
229     fdl->defer = m->defer_new(m, defer_cb, fdl);
230     assert(fdl->defer);
231
232     fdl->cb = cb;
233     fdl->userdata = userdata;
234
235     return 0;
236 }
237
238 int pa_alsa_fdlist_init_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) {
239     assert(fdl && mixer_handle && m && !fdl->m);
240
241     fdl->mixer = mixer_handle;
242     fdl->m = m;
243
244     fdl->defer = m->defer_new(m, defer_cb, fdl);
245     assert(fdl->defer);
246
247     return 0;
248 }
249
250 static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_sample_format_t *f) {
251
252     static const snd_pcm_format_t format_trans[] = {
253         [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8,
254         [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
255         [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
256         [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
257         [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
258         [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
259         [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
260     };
261
262     static const pa_sample_format_t try_order[] = {
263         PA_SAMPLE_S16NE,
264         PA_SAMPLE_S16RE,
265         PA_SAMPLE_FLOAT32NE,
266         PA_SAMPLE_FLOAT32RE,
267         PA_SAMPLE_ULAW,
268         PA_SAMPLE_ALAW,
269         PA_SAMPLE_U8,
270         PA_SAMPLE_INVALID
271     };
272
273     int i, ret;
274     
275     assert(pcm_handle);
276     assert(f);
277
278     if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
279         return ret;
280
281     if (*f == PA_SAMPLE_FLOAT32BE)
282         *f = PA_SAMPLE_FLOAT32LE;
283     else if (*f == PA_SAMPLE_FLOAT32LE)
284         *f = PA_SAMPLE_FLOAT32BE;
285     else if (*f == PA_SAMPLE_S16BE)
286         *f = PA_SAMPLE_S16LE;
287     else if (*f == PA_SAMPLE_S16LE)
288         *f = PA_SAMPLE_S16BE;
289     else
290         goto try_auto;
291
292     if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
293         return ret;
294         
295 try_auto:
296
297     for (i = 0; try_order[i] != PA_SAMPLE_INVALID; i++) {
298         *f = try_order[i];
299         
300         if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
301             return ret;
302     }
303
304     return -1;
305 }
306
307 /* Set the hardware parameters of the given ALSA device. Returns the
308  * selected fragment settings in *period and *period_size */
309 int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size) {
310     int ret = -1;
311     snd_pcm_uframes_t buffer_size;
312     unsigned int r = ss->rate;
313     unsigned int c = ss->channels;
314     pa_sample_format_t f = ss->format;
315     snd_pcm_hw_params_t *hwparams;
316     
317     assert(pcm_handle);
318     assert(ss);
319     assert(periods);
320     assert(period_size);
321
322     buffer_size = *periods * *period_size;
323     
324     if ((ret = snd_pcm_hw_params_malloc(&hwparams)) < 0 ||
325         (ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0 ||
326         (ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0 ||
327         (ret = set_format(pcm_handle, hwparams, &f)) < 0 ||
328         (ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0 || 
329         (*period_size > 0 && (ret = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, period_size, NULL)) < 0) ||
330         (*periods > 0 && (ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0) ||
331         (ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
332         goto finish;
333
334     if (ss->rate != r) {
335         pa_log_warn(__FILE__": device doesn't support %u Hz, changed to %u Hz.", ss->rate, r);
336
337         /* If the sample rate deviates too much, we need to resample */
338         if (r < ss->rate*.95 || r > ss->rate*1.05)
339             ss->rate = r;
340     }
341
342     if (ss->channels != c) {
343         pa_log_warn(__FILE__": device doesn't support %u channels, changed to %u.", ss->channels, c);
344         ss->channels = c;
345     }
346
347     if (ss->format != f) {
348         pa_log_warn(__FILE__": device doesn't support sample format %s, changed to %s.", pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f));
349         ss->format = f;
350     }
351     
352     if ((ret = snd_pcm_prepare(pcm_handle)) < 0)
353         goto finish;
354
355     if ((ret = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size)) < 0 ||
356         (ret = snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL)) < 0)
357         goto finish;
358     
359     assert(buffer_size > 0);
360     assert(*period_size > 0);
361     *periods = buffer_size / *period_size;
362     assert(*periods > 0);
363     
364     ret = 0;
365     
366 finish:
367     if (hwparams)
368         snd_pcm_hw_params_free(hwparams);
369     
370     return ret;
371 }
372
373 int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
374     int err;
375
376     assert(mixer && dev);
377
378     if ((err = snd_mixer_attach(mixer, dev)) < 0) {
379         pa_log_warn(__FILE__": Unable to attach to mixer %s: %s", dev, snd_strerror(err));
380         return -1;
381     }
382
383     if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
384         pa_log_warn(__FILE__": Unable to register mixer: %s", snd_strerror(err));
385         return -1;
386     }
387
388     if ((err = snd_mixer_load(mixer)) < 0) {
389         pa_log_warn(__FILE__": Unable to load mixer: %s", snd_strerror(err));
390         return -1;
391     }
392
393     return 0;
394 }
395
396 snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback) {
397     snd_mixer_elem_t *elem;
398     snd_mixer_selem_id_t *sid = NULL;
399     snd_mixer_selem_id_alloca(&sid);
400
401     assert(mixer);
402     assert(name);
403
404     snd_mixer_selem_id_set_name(sid, name);
405
406     if (!(elem = snd_mixer_find_selem(mixer, sid))) {
407         pa_log_warn(__FILE__": Cannot find mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
408
409         if (fallback) {
410             snd_mixer_selem_id_set_name(sid, fallback);
411             
412             if (!(elem = snd_mixer_find_selem(mixer, sid)))
413                 pa_log_warn(__FILE__": Cannot find fallback mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
414         }
415     }
416
417     if (elem)
418         pa_log_info(__FILE__": Using mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
419
420     return elem;
421 }