4 This file is part of polypaudio.
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.
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.
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
26 #include <sys/types.h>
27 #include <asoundlib.h>
29 #include <polyp/sample.h>
30 #include <polyp/xmalloc.h>
32 #include <polypcore/log.h>
34 #include "alsa-util.h"
36 struct pa_alsa_fdlist
{
39 /* This is a temporary buffer used to avoid lots of mallocs */
40 struct pollfd
*work_fds
;
46 pa_defer_event
*defer
;
51 void (*cb
)(void *userdata
);
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
;
58 unsigned short revents
;
60 assert(a
&& fdl
&& (fdl
->pcm
|| fdl
->mixer
) && fdl
->fds
&& fdl
->work_fds
);
67 memcpy(fdl
->work_fds
, fdl
->fds
, sizeof(struct pollfd
) * fdl
->num_fds
);
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
;
83 assert(i
!= fdl
->num_fds
);
86 err
= snd_pcm_poll_descriptors_revents(fdl
->pcm
, fdl
->work_fds
, fdl
->num_fds
, &revents
);
88 err
= snd_mixer_poll_descriptors_revents(fdl
->mixer
, fdl
->work_fds
, fdl
->num_fds
, &revents
);
91 pa_log_error(__FILE__
": Unable to get poll revent: %s",
98 fdl
->cb(fdl
->userdata
);
100 snd_mixer_handle_events(fdl
->mixer
);
103 a
->defer_enable(fdl
->defer
, 1);
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
;
111 assert(a
&& fdl
&& (fdl
->pcm
|| fdl
->mixer
));
113 a
->defer_enable(fdl
->defer
, 0);
116 num_fds
= snd_pcm_poll_descriptors_count(fdl
->pcm
);
118 num_fds
= snd_mixer_poll_descriptors_count(fdl
->mixer
);
121 if (num_fds
!= fdl
->num_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
);
130 memset(fdl
->work_fds
, 0, sizeof(struct pollfd
) * num_fds
);
133 err
= snd_pcm_poll_descriptors(fdl
->pcm
, fdl
->work_fds
, num_fds
);
135 err
= snd_mixer_poll_descriptors(fdl
->mixer
, fdl
->work_fds
, num_fds
);
138 pa_log_error(__FILE__
": Unable to get poll descriptors: %s",
145 if (memcmp(fdl
->fds
, fdl
->work_fds
, sizeof(struct pollfd
) * num_fds
) == 0)
149 for (i
= 0;i
< fdl
->num_fds
;i
++)
150 a
->io_free(fdl
->ios
[i
]);
151 if (num_fds
!= fdl
->num_fds
) {
153 fdl
->ios
= pa_xmalloc(sizeof(pa_io_event
*) * num_fds
);
157 fdl
->ios
= pa_xmalloc(sizeof(pa_io_event
*) * num_fds
);
162 temp
= fdl
->work_fds
;
163 fdl
->work_fds
= fdl
->fds
;
166 fdl
->num_fds
= num_fds
;
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),
177 struct pa_alsa_fdlist
*pa_alsa_fdlist_new(void) {
178 struct pa_alsa_fdlist
*fdl
;
180 fdl
= pa_xmalloc(sizeof(struct pa_alsa_fdlist
));
185 fdl
->work_fds
= NULL
;
199 void pa_alsa_fdlist_free(struct pa_alsa_fdlist
*fdl
) {
204 fdl
->m
->defer_free(fdl
->defer
);
210 for (i
= 0;i
< fdl
->num_fds
;i
++)
211 fdl
->m
->io_free(fdl
->ios
[0]);
218 pa_xfree(fdl
->work_fds
);
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
);
226 fdl
->pcm
= pcm_handle
;
229 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
233 fdl
->userdata
= userdata
;
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
);
241 fdl
->mixer
= mixer_handle
;
244 fdl
->defer
= m
->defer_new(m
, defer_cb
, fdl
);
250 /* Set the hardware parameters of the given ALSA device. Returns the
251 * selected fragment settings in *period and *period_size */
252 int pa_alsa_set_hw_params(snd_pcm_t
*pcm_handle
, pa_sample_spec
*ss
, uint32_t *periods
, snd_pcm_uframes_t
*period_size
) {
254 snd_pcm_uframes_t buffer_size
;
255 snd_pcm_hw_params_t
*hwparams
= NULL
;
256 unsigned int r
= ss
->rate
;
257 unsigned int c
= ss
->channels
;
259 static const snd_pcm_format_t format_trans
[] = {
260 [PA_SAMPLE_U8
] = SND_PCM_FORMAT_U8
,
261 [PA_SAMPLE_ALAW
] = SND_PCM_FORMAT_A_LAW
,
262 [PA_SAMPLE_ULAW
] = SND_PCM_FORMAT_MU_LAW
,
263 [PA_SAMPLE_S16LE
] = SND_PCM_FORMAT_S16_LE
,
264 [PA_SAMPLE_S16BE
] = SND_PCM_FORMAT_S16_BE
,
265 [PA_SAMPLE_FLOAT32LE
] = SND_PCM_FORMAT_FLOAT_LE
,
266 [PA_SAMPLE_FLOAT32BE
] = SND_PCM_FORMAT_FLOAT_BE
,
268 assert(pcm_handle
&& ss
&& periods
&& period_size
);
270 buffer_size
= *periods
* *period_size
;
272 if ((ret
= snd_pcm_hw_params_malloc(&hwparams
)) < 0 ||
273 (ret
= snd_pcm_hw_params_any(pcm_handle
, hwparams
)) < 0 ||
274 (ret
= snd_pcm_hw_params_set_access(pcm_handle
, hwparams
, SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0 ||
275 (ret
= snd_pcm_hw_params_set_format(pcm_handle
, hwparams
, format_trans
[ss
->format
])) < 0 ||
276 (ret
= snd_pcm_hw_params_set_rate_near(pcm_handle
, hwparams
, &r
, NULL
)) < 0 ||
277 (ret
= snd_pcm_hw_params_set_channels_near(pcm_handle
, hwparams
, &c
)) < 0 ||
278 (*period_size
> 0 && (ret
= snd_pcm_hw_params_set_period_size_near(pcm_handle
, hwparams
, period_size
, NULL
)) < 0) ||
279 (*periods
> 0 && (ret
= snd_pcm_hw_params_set_buffer_size_near(pcm_handle
, hwparams
, &buffer_size
)) < 0) ||
280 (ret
= snd_pcm_hw_params(pcm_handle
, hwparams
)) < 0)
284 pa_log_info(__FILE__
": device doesn't support %u Hz, changed to %u Hz.", ss
->rate
, r
);
286 /* If the sample rate deviates too much, we need to resample */
287 if (r
< ss
->rate
*.9 || r
> ss
->rate
*1.1)
291 if (ss
->channels
!= c
) {
292 pa_log_info(__FILE__
": device doesn't support %u channels, changed to %u.", ss
->channels
, c
);
296 if ((ret
= snd_pcm_prepare(pcm_handle
)) < 0)
299 if ((ret
= snd_pcm_hw_params_get_buffer_size(hwparams
, &buffer_size
)) < 0 ||
300 (ret
= snd_pcm_hw_params_get_period_size(hwparams
, period_size
, NULL
)) < 0)
303 assert(buffer_size
> 0);
304 assert(*period_size
> 0);
305 *periods
= buffer_size
/ *period_size
;
306 assert(*periods
> 0);
312 snd_pcm_hw_params_free(hwparams
);
317 int pa_alsa_prepare_mixer(snd_mixer_t
*mixer
, const char *dev
) {
320 assert(mixer
&& dev
);
322 if ((err
= snd_mixer_attach(mixer
, dev
)) < 0) {
323 pa_log_warn(__FILE__
": Unable to attach to mixer %s: %s", dev
, snd_strerror(err
));
327 if ((err
= snd_mixer_selem_register(mixer
, NULL
, NULL
)) < 0) {
328 pa_log_warn(__FILE__
": Unable to register mixer: %s", snd_strerror(err
));
332 if ((err
= snd_mixer_load(mixer
)) < 0) {
333 pa_log_warn(__FILE__
": Unable to load mixer: %s", snd_strerror(err
));
340 snd_mixer_elem_t
*pa_alsa_find_elem(snd_mixer_t
*mixer
, const char *name
, const char *fallback
) {
341 snd_mixer_elem_t
*elem
;
342 snd_mixer_selem_id_t
*sid
= NULL
;
343 snd_mixer_selem_id_alloca(&sid
);
348 snd_mixer_selem_id_set_name(sid
, name
);
350 if (!(elem
= snd_mixer_find_selem(mixer
, sid
))) {
351 pa_log_warn(__FILE__
": Cannot find mixer control \"%s\".", snd_mixer_selem_id_get_name(sid
));
354 snd_mixer_selem_id_set_name(sid
, fallback
);
356 if (!(elem
= snd_mixer_find_selem(mixer
, sid
)))
357 pa_log_warn(__FILE__
": Cannot find fallback mixer control \"%s\".", snd_mixer_selem_id_get_name(sid
));
362 pa_log_info(__FILE__
": Using mixer control \"%s\".", snd_mixer_selem_id_get_name(sid
));