help_view: one in all patch
[cmus.git] / mixer_alsa.c
blob74a7a7345905439c947d5d28a31acc27d116bef9
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_set_volume(int l, int r)
125 if (mixer_elem == NULL) {
126 return -1;
128 l += mixer_vol_min;
129 r += mixer_vol_min;
130 if (l > mixer_vol_max)
131 d_print("error: left volume too high (%d > %ld)\n",
132 l, mixer_vol_max);
133 if (r > mixer_vol_max)
134 d_print("error: right volume too high (%d > %ld)\n",
135 r, mixer_vol_max);
136 snd_mixer_selem_set_playback_volume(mixer_elem, SND_MIXER_SCHN_FRONT_LEFT, l);
137 snd_mixer_selem_set_playback_volume(mixer_elem, SND_MIXER_SCHN_FRONT_RIGHT, r);
138 return 0;
141 static int alsa_mixer_get_volume(int *l, int *r)
143 long lv, rv;
145 if (mixer_elem == NULL)
146 return -1;
147 snd_mixer_handle_events(alsa_mixer_handle);
148 snd_mixer_selem_get_playback_volume(mixer_elem, SND_MIXER_SCHN_FRONT_LEFT, &lv);
149 snd_mixer_selem_get_playback_volume(mixer_elem, SND_MIXER_SCHN_FRONT_RIGHT, &rv);
150 *l = lv - mixer_vol_min;
151 *r = rv - mixer_vol_min;
152 return 0;
155 static int alsa_mixer_set_option(int key, const char *val)
157 switch (key) {
158 case 0:
159 free(alsa_mixer_element);
160 alsa_mixer_element = xstrdup(val);
161 break;
162 case 1:
163 free(alsa_mixer_device);
164 alsa_mixer_device = xstrdup(val);
165 break;
166 default:
167 return -OP_ERROR_NOT_OPTION;
169 return 0;
172 static int alsa_mixer_get_option(int key, char **val)
174 switch (key) {
175 case 0:
176 if (alsa_mixer_element)
177 *val = xstrdup(alsa_mixer_element);
178 break;
179 case 1:
180 if (alsa_mixer_device)
181 *val = xstrdup(alsa_mixer_device);
182 break;
183 default:
184 return -OP_ERROR_NOT_OPTION;
186 return 0;
189 const struct mixer_plugin_ops op_mixer_ops = {
190 .init = alsa_mixer_init,
191 .exit = alsa_mixer_exit,
192 .open = alsa_mixer_open,
193 .close = alsa_mixer_close,
194 .set_volume = alsa_mixer_set_volume,
195 .get_volume = alsa_mixer_get_volume,
196 .set_option = alsa_mixer_set_option,
197 .get_option = alsa_mixer_get_option
200 const char * const op_mixer_options[] = {
201 "channel",
202 "device",
203 NULL