Handle streams separately in tree_add_track()
[cmus.git] / mixer_oss.c
blob79241b15ecc818cf52f19d9b1f4e3b6b44a3d5f6
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 "utils.h"
23 #include "xmalloc.h"
24 #include "debug.h"
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/ioctl.h>
29 #if defined(__OpenBSD__)
30 #include <soundcard.h>
31 #else
32 #include <sys/soundcard.h>
33 #endif
35 enum {
36 OSS_MIXER_CHANNEL_VOLUME,
37 OSS_MIXER_CHANNEL_BASS,
38 OSS_MIXER_CHANNEL_TREBLE,
39 OSS_MIXER_CHANNEL_SYNTH,
40 OSS_MIXER_CHANNEL_PCM,
41 OSS_MIXER_CHANNEL_SPEAKER,
42 OSS_MIXER_CHANNEL_LINE,
43 OSS_MIXER_CHANNEL_MIC,
44 OSS_MIXER_CHANNEL_CD,
45 OSS_MIXER_CHANNEL_IMIX,
46 OSS_MIXER_CHANNEL_ALTPCM,
47 OSS_MIXER_CHANNEL_RECLEV,
48 OSS_MIXER_CHANNEL_IGAIN,
49 OSS_MIXER_CHANNEL_OGAIN,
50 OSS_MIXER_CHANNEL_LINE1,
51 OSS_MIXER_CHANNEL_LINE2,
52 OSS_MIXER_CHANNEL_LINE3,
53 OSS_MIXER_CHANNEL_DIGITAL1,
54 OSS_MIXER_CHANNEL_DIGITAL2,
55 OSS_MIXER_CHANNEL_DIGITAL3,
56 OSS_MIXER_CHANNEL_PHONEIN,
57 OSS_MIXER_CHANNEL_PHONEOUT,
58 OSS_MIXER_CHANNEL_VIDEO,
59 OSS_MIXER_CHANNEL_RADIO,
60 OSS_MIXER_CHANNEL_MONITOR,
61 OSS_MIXER_CHANNEL_MAX
64 static int mixer_fd = -1;
65 static int mixer_devmask;
66 /* static int mixer_recmask; */
67 /* static int mixer_recsrc; */
68 /* static int mixer_stereodevs; */
69 static char mixer_channels[OSS_MIXER_CHANNEL_MAX];
71 /* configuration */
72 static char *oss_mixer_device = NULL;
73 static int oss_volume_controls_pcm = 1;
75 static int mixer_open(const char *device)
77 int i;
79 mixer_fd = open(device, O_RDWR);
80 if (mixer_fd == -1)
81 return -1;
82 ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &mixer_devmask);
83 /* ioctl(mixer_fd, SOUND_MIXER_READ_RECMASK, &mixer_recmask); */
84 /* ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &mixer_recsrc); */
85 /* ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &mixer_stereodevs); */
86 i = 0;
87 while (i < min(SOUND_MIXER_NRDEVICES, OSS_MIXER_CHANNEL_MAX)) {
88 mixer_channels[i] = (mixer_devmask >> i) & 1;
89 i++;
91 while (i < OSS_MIXER_CHANNEL_MAX)
92 mixer_channels[i++] = 0;
93 return 0;
96 static int mixer_set_level(int channel, int l, int r)
98 int tmp;
100 tmp = (l & 0x7f) + ((r & 0x7f) << 8);
101 if (ioctl(mixer_fd, MIXER_WRITE(channel), &tmp) == -1)
102 return -1;
103 return 0;
106 static int mixer_get_level(int channel, int *l, int *r)
108 int tmp;
110 if (ioctl(mixer_fd, MIXER_READ(channel), &tmp) == -1)
111 return -1;
112 *l = tmp & 0x7f;
113 *r = (tmp >> 8) & 0x7f;
114 return 0;
117 static int oss_device_exists(const char *device)
119 struct stat s;
121 if (stat(device, &s) == 0) {
122 d_print("device %s exists\n", device);
123 return 1;
125 d_print("device %s does not exist\n", device);
126 return 0;
129 static int oss_mixer_init(void)
131 const char *new_mixer_dev = "/dev/sound/mixer";
132 const char *mixer_dev = "/dev/mixer";
134 if (oss_mixer_device) {
135 if (oss_device_exists(oss_mixer_device))
136 return 0;
137 free(oss_mixer_device);
138 oss_mixer_device = NULL;
139 return -1;
141 if (oss_device_exists(new_mixer_dev)) {
142 oss_mixer_device = xstrdup(new_mixer_dev);
143 return 0;
145 if (oss_device_exists(mixer_dev)) {
146 oss_mixer_device = xstrdup(mixer_dev);
147 return 0;
149 return -1;
152 static int oss_mixer_exit(void)
154 if (oss_mixer_device) {
155 free(oss_mixer_device);
156 oss_mixer_device = NULL;
158 return 0;
161 static int oss_mixer_open(int *volume_max)
163 *volume_max = 100;
164 if (mixer_open(oss_mixer_device) == 0)
165 return 0;
166 return -1;
169 static int oss_mixer_close(void)
171 close(mixer_fd);
172 mixer_fd = -1;
173 return 0;
176 static int oss_mixer_set_volume(int l, int r)
178 if (oss_volume_controls_pcm) {
179 return mixer_set_level(OSS_MIXER_CHANNEL_PCM, l, r);
180 } else {
181 return mixer_set_level(OSS_MIXER_CHANNEL_VOLUME, l, r);
185 static int oss_mixer_get_volume(int *l, int *r)
187 if (oss_volume_controls_pcm) {
188 return mixer_get_level(OSS_MIXER_CHANNEL_PCM, l, r);
189 } else {
190 return mixer_get_level(OSS_MIXER_CHANNEL_VOLUME, l, r);
194 static int oss_mixer_set_option(int key, const char *val)
196 switch (key) {
197 case 0:
198 if (strcasecmp(val, "pcm") == 0) {
199 oss_volume_controls_pcm = 1;
200 } else if (strcasecmp(val, "master") == 0) {
201 oss_volume_controls_pcm = 0;
202 } else {
203 errno = EINVAL;
204 return -OP_ERROR_ERRNO;
206 break;
207 case 1:
208 free(oss_mixer_device);
209 oss_mixer_device = xstrdup(val);
210 break;
211 default:
212 return -OP_ERROR_NOT_OPTION;
214 return 0;
217 static int oss_mixer_get_option(int key, char **val)
219 switch (key) {
220 case 0:
221 if (oss_volume_controls_pcm) {
222 *val = xstrdup("PCM");
223 } else {
224 *val = xstrdup("Master");
226 break;
227 case 1:
228 if (oss_mixer_device)
229 *val = xstrdup(oss_mixer_device);
230 break;
231 default:
232 return -OP_ERROR_NOT_OPTION;
234 return 0;
237 const struct mixer_plugin_ops op_mixer_ops = {
238 .init = oss_mixer_init,
239 .exit = oss_mixer_exit,
240 .open = oss_mixer_open,
241 .close = oss_mixer_close,
242 .set_volume = oss_mixer_set_volume,
243 .get_volume = oss_mixer_get_volume,
244 .set_option = oss_mixer_set_option,
245 .get_option = oss_mixer_get_option
248 const char * const op_mixer_options[] = {
249 "channel",
250 "device",
251 NULL