Sun Apr 19 11:27:40 2020 UTC ()
ossaudio: Make SNDCTL_DSP_[GET|SET][PLAY|RECORD]VOL closer to OSSv4

Problems in the previous code include returning values in the 0-255
range NetBSD uses instead of the 0-100 range OSSv4 expects, using
AUDIO_GETBUFINFO (which doesn't even return the mixer bits), and
not encoding channels as specified: "level=(left)|(right << 8)".

In reality, setting the gain in this way (through /dev/audio rather
than /dev/mixer) doesn't seem to work properly, and the mixer-set
value seems to be retained.

However, these changes at least ensure that the return values are
correct and the balance is set correctly.

I've only found one application using this API (audio/audacious), and
OSSv4 support in it is currently disabled precisely because it breaks
when it attempts to set the track volume using it.


(nia)
diff -r1.41 -r1.42 src/lib/libossaudio/ossaudio.c

cvs diff -r1.41 -r1.42 src/lib/libossaudio/Attic/ossaudio.c (expand / switch to unified diff)

--- src/lib/libossaudio/Attic/ossaudio.c 2020/04/15 16:39:06 1.41
+++ src/lib/libossaudio/Attic/ossaudio.c 2020/04/19 11:27:40 1.42
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: ossaudio.c,v 1.41 2020/04/15 16:39:06 nia Exp $ */ 1/* $NetBSD: ossaudio.c,v 1.42 2020/04/19 11:27:40 nia Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 1997 The NetBSD Foundation, Inc. 4 * Copyright (c) 1997 The NetBSD Foundation, Inc.
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
@@ -17,62 +17,66 @@ @@ -17,62 +17,66 @@
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29#include <sys/cdefs.h> 29#include <sys/cdefs.h>
30__RCSID("$NetBSD: ossaudio.c,v 1.41 2020/04/15 16:39:06 nia Exp $"); 30__RCSID("$NetBSD: ossaudio.c,v 1.42 2020/04/19 11:27:40 nia Exp $");
31 31
32/* 32/*
33 * This is an OSS (Linux) sound API emulator. 33 * This is an OSS (Linux) sound API emulator.
34 * It provides the essentials of the API. 34 * It provides the essentials of the API.
35 */ 35 */
36 36
37/* XXX This file is essentially the same as sys/compat/ossaudio.c. 37/* XXX This file is essentially the same as sys/compat/ossaudio.c.
38 * With some preprocessor magic it could be the same file. 38 * With some preprocessor magic it could be the same file.
39 */ 39 */
40 40
41#include <string.h> 41#include <string.h>
42#include <sys/types.h> 42#include <sys/types.h>
43#include <sys/ioctl.h> 43#include <sys/ioctl.h>
44#include <sys/audioio.h> 44#include <sys/audioio.h>
45#include <sys/stat.h> 45#include <sys/stat.h>
46#include <errno.h> 46#include <errno.h>
47#include <fcntl.h> 47#include <fcntl.h>
48#include <stdio.h> 48#include <stdio.h>
49#include <unistd.h> 49#include <unistd.h>
50#include <stdarg.h> 50#include <stdarg.h>
 51#include <stdbool.h>
51 52
52#include "soundcard.h" 53#include "soundcard.h"
53#undef ioctl 54#undef ioctl
54 55
55#define GET_DEV(com) ((com) & 0xff) 56#define GET_DEV(com) ((com) & 0xff)
56 57
57#define TO_OSSVOL(x) (((x) * 100 + 127) / 255) 58#define TO_OSSVOL(x) (((x) * 100 + 127) / 255)
58#define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100) 59#define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100)
59 60
60#define GETPRINFO(info, name) \ 61#define GETPRINFO(info, name) \
61 (((info)->mode == AUMODE_RECORD) \ 62 (((info)->mode == AUMODE_RECORD) \
62 ? (info)->record.name : (info)->play.name) 63 ? (info)->record.name : (info)->play.name)
63 64
64static struct audiodevinfo *getdevinfo(int); 65static struct audiodevinfo *getdevinfo(int);
65 66
 67static int getvol(u_int, u_char);
 68static void setvol(int, int, bool);
 69
66static void setchannels(int, int, int); 70static void setchannels(int, int, int);
67static void setblocksize(int, struct audio_info *); 71static void setblocksize(int, struct audio_info *);
68 72
69static int audio_ioctl(int, unsigned long, void *); 73static int audio_ioctl(int, unsigned long, void *);
70static int mixer_ioctl(int, unsigned long, void *); 74static int mixer_ioctl(int, unsigned long, void *);
71static int opaque_to_enum(struct audiodevinfo *, audio_mixer_name_t *, int); 75static int opaque_to_enum(struct audiodevinfo *, audio_mixer_name_t *, int);
72static int enum_to_ord(struct audiodevinfo *, int); 76static int enum_to_ord(struct audiodevinfo *, int);
73static int enum_to_mask(struct audiodevinfo *, int); 77static int enum_to_mask(struct audiodevinfo *, int);
74 78
75#define INTARG (*(int*)argp) 79#define INTARG (*(int*)argp)
76 80
77int 81int
78_oss_ioctl(int fd, unsigned long com, ...) 82_oss_ioctl(int fd, unsigned long com, ...)
@@ -625,61 +629,43 @@ audio_ioctl(int fd, unsigned long com, v @@ -625,61 +629,43 @@ audio_ioctl(int fd, unsigned long com, v
625 tmpaudioinfo->max_rate = tmpinfo.play.sample_rate; 629 tmpaudioinfo->max_rate = tmpinfo.play.sample_rate;
626 tmpaudioinfo->nrates = 2; 630 tmpaudioinfo->nrates = 2;
627 for (i = 0; i < tmpaudioinfo->nrates; i++) 631 for (i = 0; i < tmpaudioinfo->nrates; i++)
628 tmpaudioinfo->rates[i] = tmpinfo.play.sample_rate; 632 tmpaudioinfo->rates[i] = tmpinfo.play.sample_rate;
629 tmpaudioinfo->min_channels = tmpinfo.play.channels; 633 tmpaudioinfo->min_channels = tmpinfo.play.channels;
630 tmpaudioinfo->max_channels = tmpinfo.play.channels; 634 tmpaudioinfo->max_channels = tmpinfo.play.channels;
631 tmpaudioinfo->binding = -1; 635 tmpaudioinfo->binding = -1;
632 tmpaudioinfo->rate_source = -1; 636 tmpaudioinfo->rate_source = -1;
633 memset(tmpaudioinfo->handle, 0, 16); 637 memset(tmpaudioinfo->handle, 0, 16);
634 tmpaudioinfo->next_play_engine = 0; 638 tmpaudioinfo->next_play_engine = 0;
635 tmpaudioinfo->next_rec_engine = 0; 639 tmpaudioinfo->next_rec_engine = 0;
636 argp = tmpaudioinfo; 640 argp = tmpaudioinfo;
637 break; 641 break;
638 case SNDCTL_DSP_GETPLAYVOL: 
639 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 
640 if (retval < 0) 
641 return retval; 
642 *(uint *)argp = tmpinfo.play.gain; 
643 break; 
644 case SNDCTL_DSP_SETPLAYVOL: 642 case SNDCTL_DSP_SETPLAYVOL:
645 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 643 setvol(fd, INTARG, false);
646 if (retval < 0) 644 /* FALLTHRU */
647 return retval; 645 case SNDCTL_DSP_GETPLAYVOL:
648 if (*(uint *)argp > 255) 646 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
649 tmpinfo.play.gain = 255; 
650 else 
651 tmpinfo.play.gain = *(uint *)argp; 
652 retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); 
653 if (retval < 0) 
654 return retval; 
655 break; 
656 case SNDCTL_DSP_GETRECVOL: 
657 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 
658 if (retval < 0) 647 if (retval < 0)
659 return retval; 648 return retval;
660 *(uint *)argp = tmpinfo.record.gain; 649 INTARG = getvol(tmpinfo.play.gain, tmpinfo.play.balance);
661 break; 650 break;
662 case SNDCTL_DSP_SETRECVOL: 651 case SNDCTL_DSP_SETRECVOL:
663 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 652 setvol(fd, INTARG, true);
664 if (retval < 0) 653 /* FALLTHRU */
665 return retval; 654 case SNDCTL_DSP_GETRECVOL:
666 if (*(uint *)argp > 255) 655 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
667 tmpinfo.record.gain = 255; 
668 else 
669 tmpinfo.record.gain = *(uint *)argp; 
670 retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); 
671 if (retval < 0) 656 if (retval < 0)
672 return retval; 657 return retval;
 658 INTARG = getvol(tmpinfo.record.gain, tmpinfo.record.balance);
673 break; 659 break;
674 case SNDCTL_DSP_SKIP: 660 case SNDCTL_DSP_SKIP:
675 case SNDCTL_DSP_SILENCE: 661 case SNDCTL_DSP_SILENCE:
676 return EINVAL; 662 return EINVAL;
677 case SNDCTL_DSP_SETDUPLEX: 663 case SNDCTL_DSP_SETDUPLEX:
678 idat = 1; 664 idat = 1;
679 retval = ioctl(fd, AUDIO_SETFD, &idat); 665 retval = ioctl(fd, AUDIO_SETFD, &idat);
680 if (retval < 0) 666 if (retval < 0)
681 return retval; 667 return retval;
682 break; 668 break;
683 case SNDCTL_DSP_GETODELAY: 669 case SNDCTL_DSP_GETODELAY:
684 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 670 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
685 if (retval < 0) 671 if (retval < 0)
@@ -1037,26 +1023,73 @@ mixer_ioctl(int fd, unsigned long com, v @@ -1037,26 +1023,73 @@ mixer_ioctl(int fd, unsigned long com, v
1037 if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && 1023 if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com &&
1038 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) 1024 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))
1039 return 0; 1025 return 0;
1040 goto doread; 1026 goto doread;
1041 } else { 1027 } else {
1042 errno = EINVAL; 1028 errno = EINVAL;
1043 return -1; 1029 return -1;
1044 } 1030 }
1045 } 1031 }
1046 INTARG = (int)idat; 1032 INTARG = (int)idat;
1047 return 0; 1033 return 0;
1048} 1034}
1049 1035
 1036static int
 1037getvol(u_int gain, u_char balance)
 1038{
 1039 u_int l, r;
 1040
 1041 if (balance == AUDIO_MID_BALANCE) {
 1042 l = r = gain;
 1043 } else if (balance < AUDIO_MID_BALANCE) {
 1044 l = gain;
 1045 r = (balance * gain) / AUDIO_MID_BALANCE;
 1046 } else {
 1047 r = gain;
 1048 l = ((AUDIO_RIGHT_BALANCE - balance) * gain)
 1049 / AUDIO_MID_BALANCE;
 1050 }
 1051
 1052 return TO_OSSVOL(l) | (TO_OSSVOL(r) << 8);
 1053}
 1054
 1055static void
 1056setvol(int fd, int volume, bool record)
 1057{
 1058 u_int lgain, rgain;
 1059 struct audio_info tmpinfo;
 1060 struct audio_prinfo *prinfo;
 1061
 1062 AUDIO_INITINFO(&tmpinfo);
 1063 prinfo = record ? &tmpinfo.record : &tmpinfo.play;
 1064
 1065 lgain = FROM_OSSVOL((volume >> 0) & 0xff);
 1066 rgain = FROM_OSSVOL((volume >> 8) & 0xff);
 1067
 1068 if (lgain == rgain) {
 1069 prinfo->gain = lgain;
 1070 prinfo->balance = AUDIO_MID_BALANCE;
 1071 } else if (lgain < rgain) {
 1072 prinfo->gain = rgain;
 1073 prinfo->balance = AUDIO_RIGHT_BALANCE -
 1074 (AUDIO_MID_BALANCE * lgain) / rgain;
 1075 } else {
 1076 prinfo->gain = lgain;
 1077 prinfo->balance = (AUDIO_MID_BALANCE * rgain) / lgain;
 1078 }
 1079
 1080 (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo);
 1081}
 1082
1050/* 1083/*
1051 * When AUDIO_SETINFO fails to set a channel count, the application's chosen 1084 * When AUDIO_SETINFO fails to set a channel count, the application's chosen
1052 * number is out of range of what the kernel allows. 1085 * number is out of range of what the kernel allows.
1053 * 1086 *
1054 * When this happens, we use the current hardware settings. This is just in 1087 * When this happens, we use the current hardware settings. This is just in
1055 * case an application is abusing SNDCTL_DSP_CHANNELS - OSSv4 always sets and 1088 * case an application is abusing SNDCTL_DSP_CHANNELS - OSSv4 always sets and
1056 * returns a reasonable value, even if it wasn't what the user requested. 1089 * returns a reasonable value, even if it wasn't what the user requested.
1057 * 1090 *
1058 * XXX: If a device is opened for both playback and recording, and supports 1091 * XXX: If a device is opened for both playback and recording, and supports
1059 * fewer channels for recording than playback, applications that do both will 1092 * fewer channels for recording than playback, applications that do both will
1060 * behave very strangely. OSS doesn't allow for reporting separate channel 1093 * behave very strangely. OSS doesn't allow for reporting separate channel
1061 * counts for recording and playback. This could be worked around by always 1094 * counts for recording and playback. This could be worked around by always
1062 * mixing recorded data up to the same number of channels as is being used 1095 * mixing recorded data up to the same number of channels as is being used