1 /* WMix 3.0 -- a mixer using the OSS mixer API.
2 * Copyright (C) 2000, 2001
3 * Daniel Richard G. <skunk@mit.edu>,
4 * timecop <timecop@japan.co.jp>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include <sys/ioctl.h>
31 #include <sys/soundcard.h>
33 #include "include/common.h"
34 #include "include/misc.h"
35 #include "include/mixer-oss.h"
37 #define WMVOLUME_CHANNEL_NAMES \
47 "Recording monitor", \
48 "PCM Wave 2 volume", \
64 #ifdef OSS_CHANNEL_NAMES
65 #define CHANNEL_NAMES SOUND_DEVICE_LABELS
67 #define CHANNEL_NAMES WMVOLUME_CHANNEL_NAMES
71 const char *name
; /* name of channel */
72 const char *sname
; /* short name of the channel */
73 int dev
; /* channel device number */
74 int prev_dev_lr_volume
; /* last known left/right volume
75 * (in device format) */
76 float volume
; /* volume, in [0, 1] */
77 float balance
; /* balance, in [-1, 1] */
78 bool can_record
; /* capable of recording? */
79 bool is_recording
; /* is it recording? */
80 bool is_stereo
; /* capable of stereo? */
81 bool is_muted
; /* is it muted? */
84 static const char *channel_names
[] = { CHANNEL_NAMES
};
85 static const char *short_names
[] = SOUND_DEVICE_LABELS
;
89 static MixerChannel mixer
[SOUND_MIXER_NRDEVICES
];
90 static int n_channels
= 0;
91 static int cur_channel
= 0;
93 static int prev_modify_counter
= -1;
95 static bool get_mixer_state(void)
97 struct mixer_info m_info
;
98 int dev_lr_volume
, dev_left_volume
, dev_right_volume
;
103 /* to really keep track of updates */
104 static MixerChannel oldmixer
[SOUND_MIXER_NRDEVICES
];
106 ioctl(mixer_fd
, SOUND_MIXER_INFO
, &m_info
);
108 if (m_info
.modify_counter
== prev_modify_counter
)
110 * Mixer state has not changed
114 /* Mixer state was changed by another program, so we need
115 * to update. As OSS cannot tell us specifically which
116 * channels changed, we read all of them in.
118 * prev_modify_counter was initialized to -1, so this part
119 * is guaranteed to run the first time this routine is
123 if (ioctl(mixer_fd
, SOUND_MIXER_READ_RECSRC
, &srcmask
) == -1) {
124 fprintf(stderr
, "mixer read failed\n");
129 for (ch
= 0; ch
< n_channels
; ch
++) {
130 if (ioctl(mixer_fd
, MIXER_READ(mixer
[ch
].dev
), &dev_lr_volume
) ==
132 fprintf(stderr
, "mixer read failed\n");
136 if (dev_lr_volume
!= mixer
[ch
].prev_dev_lr_volume
) {
137 dev_left_volume
= dev_lr_volume
& 0xFF;
138 dev_right_volume
= dev_lr_volume
>> 8;
140 if ((dev_left_volume
> 0) || (dev_right_volume
> 0))
141 mixer
[ch
].is_muted
= false;
143 left
= (float) dev_left_volume
/ 100.0;
144 right
= (float) dev_right_volume
/ 100.0;
146 if (!mixer
[ch
].is_muted
) {
147 if (mixer
[ch
].is_stereo
)
149 right
, &mixer
[ch
].volume
, &mixer
[ch
].balance
);
151 mixer
[ch
].volume
= left
;
152 mixer
[ch
].balance
= 0.0;
155 mixer
[ch
].prev_dev_lr_volume
= dev_lr_volume
;
158 mixer
[ch
].is_recording
= ((1 << mixer
[ch
].dev
) & srcmask
) != 0;
160 prev_modify_counter
= m_info
.modify_counter
;
161 /* check if this was due to OSS stupidity or if we really changed */
162 if (!memcmp(&mixer
, &oldmixer
, sizeof(mixer
))) {
163 memcpy(&oldmixer
, &mixer
, sizeof(mixer
));
166 memcpy(&oldmixer
, &mixer
, sizeof(mixer
));
170 static void set_mixer_state(void)
173 int dev_left_volume
, dev_right_volume
, dev_lr_volume
;
175 if (mixer
[cur_channel
].is_muted
) {
179 vb_to_lr(mixer
[cur_channel
].volume
,
180 mixer
[cur_channel
].balance
, &left
, &right
);
182 dev_left_volume
= (int) (100.0 * left
);
183 dev_right_volume
= (int) (100.0 * right
);
184 dev_lr_volume
= (dev_right_volume
<< 8) | dev_left_volume
;
185 ioctl(mixer_fd
, MIXER_WRITE(mixer
[cur_channel
].dev
), &dev_lr_volume
);
188 static void get_record_state(void)
193 if (ioctl(mixer_fd
, SOUND_MIXER_READ_RECSRC
, &srcmask
) == -1) {
194 fprintf(stderr
, "mixer read failed\n");
199 for (ch
= 0; ch
< n_channels
; ch
++) {
200 mixer
[ch
].is_recording
= ((1 << mixer
[ch
].dev
) & srcmask
) != 0;
204 static void set_record_state(void)
208 if (ioctl(mixer_fd
, SOUND_MIXER_READ_RECSRC
, &srcmask
) == -1) {
209 fputs("error: recording source mask ioctl failed\n", stderr
);
213 if (((1 << mixer
[cur_channel
].dev
) & srcmask
) == 0)
214 srcmask
|= (1 << mixer
[cur_channel
].dev
);
216 srcmask
&= ~(1 << mixer
[cur_channel
].dev
);
218 if (ioctl(mixer_fd
, SOUND_MIXER_WRITE_RECSRC
, &srcmask
) == -1) {
219 fputs("error: recording source mask ioctl failed\n", stderr
);
224 static bool is_exclude(const char *short_name
, const char *exclude
[])
229 for (count
= 0; exclude
[count
] != NULL
; count
++) {
232 * Short names may be padded with spaces, because apparently there is a minimum
233 * length requirement of 6 characters for the name, and we do not want to
234 * include this padding in the match
236 len
= strlen(short_name
);
238 if (short_name
[len
- 1] == ' ')
244 if (strncmp(short_name
, exclude
[count
], len
) != 0)
247 if (exclude
[count
][len
] != '\0')
250 /* Check the remaining in short name is only space */
251 while (short_name
[len
] == ' ')
254 if (short_name
[len
] == '\0')
260 void mixer_oss_init(const char *mixer_device
, bool verbose
, const char * exclude
[])
262 int devmask
, srcmask
, recmask
, stmask
;
263 struct mixer_info m_info
;
267 mixer_fd
= open(mixer_device
, O_RDWR
);
269 if (mixer_fd
== -1) {
270 fprintf(stderr
, "error: cannot open mixer device %s\n",
275 if (ioctl(mixer_fd
, SOUND_MIXER_READ_DEVMASK
, &devmask
) == -1) {
276 fputs("error: device mask ioctl failed\n", stderr
);
280 if (ioctl(mixer_fd
, SOUND_MIXER_READ_RECSRC
, &srcmask
) == -1) {
281 fputs("error: recording source mask ioctl failed\n", stderr
);
285 if (ioctl(mixer_fd
, SOUND_MIXER_READ_RECMASK
, &recmask
) == -1) {
286 fputs("error: recording mask ioctl failed\n", stderr
);
290 if (ioctl(mixer_fd
, SOUND_MIXER_READ_STEREODEVS
, &stmask
) == -1) {
291 fputs("error: stereo mask ioctl failed\n", stderr
);
295 if (ioctl(mixer_fd
, SOUND_MIXER_INFO
, &m_info
) == -1) {
296 fputs("error: could not read mixer info\n", stderr
);
301 printf("Sound card: %s (%s)\n", m_info
.name
, m_info
.id
);
302 puts("Supported channels:");
304 for (count
= 0; count
< SOUND_MIXER_NRDEVICES
; count
++) {
306 if (!(mask
& devmask
))
309 if (!is_exclude(short_names
[count
], exclude
)) {
310 mixer
[n_channels
].name
= channel_names
[count
];
311 mixer
[n_channels
].sname
= short_names
[count
];
312 mixer
[n_channels
].dev
= count
;
313 mixer
[n_channels
].prev_dev_lr_volume
= -1;
314 mixer
[n_channels
].can_record
= (mask
& recmask
) != 0;
315 mixer
[n_channels
].is_recording
= (mask
& srcmask
) != 0;
316 mixer
[n_channels
].is_stereo
= (mask
& stmask
) != 0;
317 mixer
[n_channels
].is_muted
= false;
320 printf(" %d: %s \t(%s)\n", n_channels
, channel_names
[count
], short_names
[count
]);
322 printf(" x: %s \t(%s) - disabled\n", channel_names
[count
], short_names
[count
]);
327 bool mixer_oss_is_changed(void)
329 return get_mixer_state();
332 int mixer_oss_get_channel_count(void)
337 int mixer_oss_get_channel(void)
342 const char *mixer_oss_get_channel_name(void)
344 return mixer
[cur_channel
].name
;
347 const char *mixer_oss_get_short_name(void)
349 return mixer
[cur_channel
].sname
;
352 void mixer_oss_set_channel(int channel
)
354 assert((channel
>= 0) && (channel
< n_channels
));
356 cur_channel
= channel
;
360 void mixer_oss_set_channel_rel(int delta_channel
)
362 cur_channel
= (cur_channel
+ delta_channel
) % n_channels
;
364 cur_channel
+= n_channels
;
368 float mixer_oss_get_volume(void)
371 return mixer
[cur_channel
].volume
;
374 void mixer_oss_set_volume(float volume
)
376 assert((volume
>= 0.0) && (volume
<= 1.0));
378 mixer
[cur_channel
].volume
= volume
;
382 void mixer_oss_set_volume_rel(float delta_volume
)
384 mixer
[cur_channel
].volume
+= delta_volume
;
385 mixer
[cur_channel
].volume
= CLAMP(mixer
[cur_channel
].volume
, 0.0, 1.0);
389 float mixer_oss_get_balance(void)
392 return mixer
[cur_channel
].balance
;
395 void mixer_oss_set_balance(float balance
)
397 assert((balance
>= -1.0) && (balance
<= 1.0));
399 if (mixer
[cur_channel
].is_stereo
) {
400 mixer
[cur_channel
].balance
= balance
;
405 void mixer_oss_set_balance_rel(float delta_balance
)
407 if (mixer
[cur_channel
].is_stereo
) {
408 mixer
[cur_channel
].balance
+= delta_balance
;
409 mixer
[cur_channel
].balance
=
410 CLAMP(mixer
[cur_channel
].balance
, -1.0, 1.0);
415 void mixer_oss_toggle_mute(void)
417 mixer
[cur_channel
].is_muted
= !mixer
[cur_channel
].is_muted
;
422 void mixer_oss_toggle_rec(void)
424 if (mixer
[cur_channel
].can_record
) {
425 mixer
[cur_channel
].is_recording
= !mixer
[cur_channel
].is_recording
;
431 bool mixer_oss_is_muted(void)
433 return mixer
[cur_channel
].is_muted
;
436 bool mixer_oss_is_stereo(void)
438 return mixer
[cur_channel
].is_stereo
;
441 bool mixer_oss_is_rec(void)
443 return mixer
[cur_channel
].is_recording
;
446 bool mixer_oss_can_rec(void)
448 return mixer
[cur_channel
].can_record
;