flac: Saner EOF handling
[cmus.git] / mixer_alsa.c
blobf19327f30c8c52eda33ef4810f756bfecbd240f6
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include "mixer.h"
21 #include "op.h"
22 #include "xmalloc.h"
23 #include "debug.h"
25 #define ALSA_PCM_NEW_HW_PARAMS_API
26 #define ALSA_PCM_NEW_SW_PARAMS_API
28 #include <alsa/asoundlib.h>
30 static snd_mixer_t *alsa_mixer_handle;
31 static snd_mixer_elem_t *mixer_elem = NULL;
32 static long mixer_vol_min, mixer_vol_max;
34 /* configuration */
35 static char *alsa_mixer_device = NULL;
36 static char *alsa_mixer_element = NULL;
38 static int alsa_mixer_init(void)
40 if (alsa_mixer_device == NULL)
41 alsa_mixer_device = xstrdup("default");
42 if (alsa_mixer_element == NULL)
43 alsa_mixer_element = xstrdup("PCM");
44 /* FIXME: check device */
45 return 0;
48 static int alsa_mixer_exit(void)
50 free(alsa_mixer_device);
51 alsa_mixer_device = NULL;
52 free(alsa_mixer_element);
53 alsa_mixer_element = NULL;
54 return 0;
57 static int alsa_mixer_open(int *volume_max)
59 snd_mixer_selem_id_t *sid;
60 snd_mixer_elem_t *elem;
61 int count;
62 int rc;
64 snd_mixer_selem_id_alloca(&sid);
66 rc = snd_mixer_open(&alsa_mixer_handle, 0);
67 if (rc < 0)
68 goto error;
69 rc = snd_mixer_attach(alsa_mixer_handle, alsa_mixer_device);
70 if (rc < 0)
71 goto error;
72 rc = snd_mixer_selem_register(alsa_mixer_handle, NULL, NULL);
73 if (rc < 0)
74 goto error;
75 rc = snd_mixer_load(alsa_mixer_handle);
76 if (rc < 0)
77 goto error;
78 count = snd_mixer_get_count(alsa_mixer_handle);
79 if (count == 0) {
80 d_print("error: mixer does not have elements\n");
81 return -2;
83 elem = snd_mixer_first_elem(alsa_mixer_handle);
84 while (elem) {
85 const char *name;
86 int has_vol, has_switch;
88 snd_mixer_selem_get_id(elem, sid);
89 name = snd_mixer_selem_id_get_name(sid);
90 d_print("name = %s\n", name);
91 d_print("has playback volume = %d\n", snd_mixer_selem_has_playback_volume(elem));
92 d_print("has playback switch = %d\n", snd_mixer_selem_has_playback_switch(elem));
93 if (strcasecmp(name, alsa_mixer_element)) {
94 elem = snd_mixer_elem_next(elem);
95 continue;
97 has_vol = snd_mixer_selem_has_playback_volume(elem);
98 if (!has_vol) {
99 d_print("mixer element `%s' does not have playback volume\n", name);
100 return -2;
102 snd_mixer_selem_get_playback_volume_range(elem,
103 &mixer_vol_min, &mixer_vol_max);
104 has_switch = snd_mixer_selem_has_playback_switch(elem);
105 /* FIXME: get number of channels */
106 mixer_elem = elem;
107 *volume_max = mixer_vol_max - mixer_vol_min;
108 return 0;
110 d_print("error: mixer element `%s' not found\n", alsa_mixer_element);
111 return -2;
112 error:
113 d_print("error: %s\n", snd_strerror(rc));
114 return -1;
117 static int alsa_mixer_close(void)
119 snd_mixer_close(alsa_mixer_handle);
120 return 0;
123 static int alsa_mixer_get_fds(int *fds)
125 struct pollfd pfd[NR_MIXER_FDS];
126 int count, i;
128 count = snd_mixer_poll_descriptors(alsa_mixer_handle, pfd, NR_MIXER_FDS);
129 for (i = 0; i < count; i++)
130 fds[i] = pfd[i].fd;
131 return count;
134 static int alsa_mixer_set_volume(int l, int r)
136 if (mixer_elem == NULL) {
137 return -1;
139 l += mixer_vol_min;
140 r += mixer_vol_min;
141 if (l > mixer_vol_max)
142 d_print("error: left volume too high (%d > %ld)\n",
143 l, mixer_vol_max);
144 if (r > mixer_vol_max)
145 d_print("error: right volume too high (%d > %ld)\n",
146 r, mixer_vol_max);
147 snd_mixer_selem_set_playback_volume(mixer_elem, SND_MIXER_SCHN_FRONT_LEFT, l);
148 snd_mixer_selem_set_playback_volume(mixer_elem, SND_MIXER_SCHN_FRONT_RIGHT, r);
149 return 0;
152 static int alsa_mixer_get_volume(int *l, int *r)
154 long lv, rv;
156 if (mixer_elem == NULL)
157 return -1;
158 snd_mixer_handle_events(alsa_mixer_handle);
159 snd_mixer_selem_get_playback_volume(mixer_elem, SND_MIXER_SCHN_FRONT_LEFT, &lv);
160 snd_mixer_selem_get_playback_volume(mixer_elem, SND_MIXER_SCHN_FRONT_RIGHT, &rv);
161 *l = lv - mixer_vol_min;
162 *r = rv - mixer_vol_min;
163 return 0;
166 static int alsa_mixer_set_option(int key, const char *val)
168 switch (key) {
169 case 0:
170 free(alsa_mixer_element);
171 alsa_mixer_element = xstrdup(val);
172 break;
173 case 1:
174 free(alsa_mixer_device);
175 alsa_mixer_device = xstrdup(val);
176 break;
177 default:
178 return -OP_ERROR_NOT_OPTION;
180 return 0;
183 static int alsa_mixer_get_option(int key, char **val)
185 switch (key) {
186 case 0:
187 if (alsa_mixer_element)
188 *val = xstrdup(alsa_mixer_element);
189 break;
190 case 1:
191 if (alsa_mixer_device)
192 *val = xstrdup(alsa_mixer_device);
193 break;
194 default:
195 return -OP_ERROR_NOT_OPTION;
197 return 0;
200 const struct mixer_plugin_ops op_mixer_ops = {
201 .init = alsa_mixer_init,
202 .exit = alsa_mixer_exit,
203 .open = alsa_mixer_open,
204 .close = alsa_mixer_close,
205 .get_fds = alsa_mixer_get_fds,
206 .set_volume = alsa_mixer_set_volume,
207 .get_volume = alsa_mixer_get_volume,
208 .set_option = alsa_mixer_set_option,
209 .get_option = alsa_mixer_get_option
212 const char * const op_mixer_options[] = {
213 "channel",
214 "device",
215 NULL