Module Name:    src
Committed By:   nia
Date:           Sun Apr 19 11:27:40 UTC 2020

Modified Files:
        src/lib/libossaudio: ossaudio.c

Log Message:
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.


To generate a diff of this commit:
cvs rdiff -u -r1.41 -r1.42 src/lib/libossaudio/ossaudio.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/lib/libossaudio/ossaudio.c
diff -u src/lib/libossaudio/ossaudio.c:1.41 src/lib/libossaudio/ossaudio.c:1.42
--- src/lib/libossaudio/ossaudio.c:1.41	Wed Apr 15 16:39:06 2020
+++ src/lib/libossaudio/ossaudio.c	Sun Apr 19 11:27:40 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: ossaudio.c,v 1.41 2020/04/15 16:39:06 nia Exp $	*/
+/*	$NetBSD: ossaudio.c,v 1.42 2020/04/19 11:27:40 nia Exp $	*/
 
 /*-
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: ossaudio.c,v 1.41 2020/04/15 16:39:06 nia Exp $");
+__RCSID("$NetBSD: ossaudio.c,v 1.42 2020/04/19 11:27:40 nia Exp $");
 
 /*
  * This is an OSS (Linux) sound API emulator.
@@ -48,6 +48,7 @@ __RCSID("$NetBSD: ossaudio.c,v 1.41 2020
 #include <stdio.h>
 #include <unistd.h>
 #include <stdarg.h>
+#include <stdbool.h>
 
 #include "soundcard.h"
 #undef ioctl
@@ -63,6 +64,9 @@ __RCSID("$NetBSD: ossaudio.c,v 1.41 2020
 
 static struct audiodevinfo *getdevinfo(int);
 
+static int getvol(u_int, u_char);
+static void setvol(int, int, bool);
+
 static void setchannels(int, int, int);
 static void setblocksize(int, struct audio_info *);
 
@@ -635,41 +639,23 @@ audio_ioctl(int fd, unsigned long com, v
 		tmpaudioinfo->next_rec_engine = 0;
 		argp = tmpaudioinfo;
 		break;
-	case SNDCTL_DSP_GETPLAYVOL:
-		retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
-		if (retval < 0)
-			return retval;
-		*(uint *)argp = tmpinfo.play.gain;
-		break;
 	case SNDCTL_DSP_SETPLAYVOL:
-		retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
-		if (retval < 0)
-			return retval;
-		if (*(uint *)argp > 255)
-			tmpinfo.play.gain = 255;
-		else
-			tmpinfo.play.gain = *(uint *)argp;
-		retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo);
-		if (retval < 0)
-			return retval;
-		break;
-	case SNDCTL_DSP_GETRECVOL:
-		retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
+		setvol(fd, INTARG, false);
+		/* FALLTHRU */
+	case SNDCTL_DSP_GETPLAYVOL:
+		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
 		if (retval < 0)
 			return retval;
-		*(uint *)argp = tmpinfo.record.gain;
+		INTARG = getvol(tmpinfo.play.gain, tmpinfo.play.balance);
 		break;
 	case SNDCTL_DSP_SETRECVOL:
-		retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
-		if (retval < 0)
-			return retval;
-		if (*(uint *)argp > 255)
-			tmpinfo.record.gain = 255;
-		else
-			tmpinfo.record.gain = *(uint *)argp;
-		retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo);
+		setvol(fd, INTARG, true);
+		/* FALLTHRU */
+	case SNDCTL_DSP_GETRECVOL:
+		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
 		if (retval < 0)
 			return retval;
+		INTARG = getvol(tmpinfo.record.gain, tmpinfo.record.balance);
 		break;
 	case SNDCTL_DSP_SKIP:
 	case SNDCTL_DSP_SILENCE:
@@ -1047,6 +1033,53 @@ mixer_ioctl(int fd, unsigned long com, v
 	return 0;
 }
 
+static int
+getvol(u_int gain, u_char balance)
+{
+	u_int l, r;
+
+	if (balance == AUDIO_MID_BALANCE) {
+		l = r = gain;
+	} else if (balance < AUDIO_MID_BALANCE) {
+		l = gain;
+		r = (balance * gain) / AUDIO_MID_BALANCE;
+	} else {
+		r = gain;
+		l = ((AUDIO_RIGHT_BALANCE - balance) * gain)
+		    / AUDIO_MID_BALANCE;
+	}
+
+	return TO_OSSVOL(l) | (TO_OSSVOL(r) << 8);
+}
+
+static void
+setvol(int fd, int volume, bool record)
+{
+	u_int lgain, rgain;
+	struct audio_info tmpinfo;
+	struct audio_prinfo *prinfo;
+
+	AUDIO_INITINFO(&tmpinfo);
+	prinfo = record ? &tmpinfo.record : &tmpinfo.play;
+
+	lgain = FROM_OSSVOL((volume >> 0) & 0xff);
+	rgain = FROM_OSSVOL((volume >> 8) & 0xff);
+
+	if (lgain == rgain) {
+		prinfo->gain = lgain;
+		prinfo->balance = AUDIO_MID_BALANCE;
+	} else if (lgain < rgain) {
+		prinfo->gain = rgain;
+		prinfo->balance = AUDIO_RIGHT_BALANCE -
+		    (AUDIO_MID_BALANCE * lgain) / rgain;
+	} else {
+		prinfo->gain = lgain;
+		prinfo->balance = (AUDIO_MID_BALANCE * rgain) / lgain;
+	}
+
+	(void)ioctl(fd, AUDIO_SETINFO, &tmpinfo);
+}
+
 /*
  * When AUDIO_SETINFO fails to set a channel count, the application's chosen
  * number is out of range of what the kernel allows.

Reply via email to