Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/netbsd-9]: src Pull up following revision(s) (requested by nia in ticket...



details:   https://anonhg.NetBSD.org/src/rev/14f7ae76b07e
branches:  netbsd-9
changeset: 931359:14f7ae76b07e
user:      martin <martin%NetBSD.org@localhost>
date:      Mon Apr 27 14:32:34 2020 +0000

description:
Pull up following revision(s) (requested by nia in ticket #855):

        lib/libossaudio/ossaudio.c: revision 1.41
        lib/libossaudio/ossaudio.c: revision 1.42
        lib/libossaudio/ossaudio.c: revision 1.43
        sys/compat/ossaudio/ossaudio.c: revision 1.80
        sys/compat/ossaudio/ossaudio.c: revision 1.81
        sys/compat/ossaudio/ossaudio.c: revision 1.82
        lib/libossaudio/ossaudio.c: revision 1.39
        sys/compat/ossaudio/ossaudio.c: revision 1.79
        lib/libossaudio/ossaudio.c: revision 1.40

ossaudio: Make SNDCTL_DSP_SPEED more robust when using invalid rates.

>From the perspective of reading the OSSv4 specification, NetBSD's
behaviour when an invalid sample rate is set makes no sense at all:
AUDIO_SETINFO simply returns an error code, and then we immediately
fall through to getting the sample rate, which is still set to the
legacy default of 8000 Hz.

Instead, what OSS applications generally expect is that they will be
able to receive the actual hardware sample rate. This is very, very
unlikely to be 8000 Hz on a modern machine.

No functional change when setting a sample rate between the supported
rates of 1000 and 192000 Hz. When a rate outside this range is requested,
the hardware rate is returned (on modern hardware, generally always 48000
Hz or a multiple of 48000 Hz).

ossaudio: Make SNDCTL_DSP_SETFMT conform with OSSv4.

The OSSv4 spec says we shouldn't really error if an invalid format is
chosen by an application. Things are especially likely to be confused
if we return MULAW, since in OSSv4 terms that means that's the native
hardware format. Instead, set and return the current hardware format
if an invalid format is chosen.

For the 24-bit sample formats, note that the NetBSD kernel currently
can't handle them in its default configuration, and will return an error
code if you attempt to use them. So, if an applicaton requests 24-bit PCM,
promote it to 32-bit PCM. According to the spec, this is valid and
applications should be checking the return value anyway.

In the Linux compat layer, we just use S16LE as a fallback. The OSSv3
headers that are still being shipped with Linux don't contain definitions
for fancier formats and we can reasonably expect all applications to
support S16LE.

ossaudio: If the user's channel count is rejected, use the hardware count

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.

ossaudio: Implement SNDCTL_DSP_(SET|GET)TRIGGER.

diffstat:

 lib/libossaudio/ossaudio.c     |  276 +++++++++++++++++++++++++++++-----------
 sys/compat/ossaudio/ossaudio.c |  193 +++++++++++++++++++---------
 2 files changed, 329 insertions(+), 140 deletions(-)

diffs (truncated from 666 to 300 lines):

diff -r 80cead163a42 -r 14f7ae76b07e lib/libossaudio/ossaudio.c
--- a/lib/libossaudio/ossaudio.c        Sat Apr 25 10:53:48 2020 +0000
+++ b/lib/libossaudio/ossaudio.c        Mon Apr 27 14:32:34 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ossaudio.c,v 1.36.2.1 2019/11/19 11:01:27 martin Exp $ */
+/*     $NetBSD: ossaudio.c,v 1.36.2.2 2020/04/27 14:32:34 martin Exp $ */
 
 /*-
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: ossaudio.c,v 1.36.2.1 2019/11/19 11:01:27 martin Exp $");
+__RCSID("$NetBSD: ossaudio.c,v 1.36.2.2 2020/04/27 14:32:34 martin Exp $");
 
 /*
  * This is an OSS (Linux) sound API emulator.
@@ -48,6 +48,7 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <stdarg.h>
+#include <stdbool.h>
 
 #include "soundcard.h"
 #undef ioctl
@@ -63,6 +64,10 @@
 
 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 *);
 
 static int audio_ioctl(int, unsigned long, void *);
@@ -95,7 +100,7 @@
 audio_ioctl(int fd, unsigned long com, void *argp)
 {
 
-       struct audio_info tmpinfo;
+       struct audio_info tmpinfo, hwfmt;
        struct audio_offset tmpoffs;
        struct audio_buf_info bufinfo;
        struct count_info cntinfo;
@@ -134,7 +139,35 @@
                AUDIO_INITINFO(&tmpinfo);
                tmpinfo.play.sample_rate =
                tmpinfo.record.sample_rate = INTARG;
-               (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
+               /*
+                * The default NetBSD behavior if an unsupported sample rate
+                * is set is to return an error code and keep the rate at the
+                * default of 8000 Hz.
+                * 
+                * However, OSS specifies that a sample rate supported by the
+                * hardware is returned if the exact rate could not be set.
+                * 
+                * So, if the chosen sample rate is invalid, set and return
+                * the current hardware rate.
+                */
+               if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) {
+                       /* Don't care that SETINFO failed the first time... */
+                       errno = 0;
+                       retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt);
+                       if (retval < 0)
+                               return retval;
+                       retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
+                       if (retval < 0)
+                               return retval;
+                       tmpinfo.play.sample_rate =
+                       tmpinfo.record.sample_rate =
+                           (tmpinfo.mode == AUMODE_RECORD) ?
+                           hwfmt.record.sample_rate :
+                           hwfmt.play.sample_rate;
+                       retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo);
+                       if (retval < 0)
+                               return retval;
+               }
                /* FALLTHRU */
        case SOUND_PCM_READ_RATE:
                retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
@@ -210,24 +243,19 @@
                        tmpinfo.play.encoding =
                        tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE;
                        break;
+               /*
+                * XXX: When the kernel supports 24-bit LPCM by default,
+                * the 24-bit formats should be handled properly instead
+                * of falling back to 32 bits.
+                */
                case AFMT_S24_LE:
-                       tmpinfo.play.precision =
-                       tmpinfo.record.precision = 24;
-                       tmpinfo.play.encoding =
-                       tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE;
-                       break;
-               case AFMT_S24_BE:
-                       tmpinfo.play.precision =
-                       tmpinfo.record.precision = 24;
-                       tmpinfo.play.encoding =
-                       tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE;
-                       break;
                case AFMT_S32_LE:
                        tmpinfo.play.precision =
                        tmpinfo.record.precision = 32;
                        tmpinfo.play.encoding =
                        tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE;
                        break;
+               case AFMT_S24_BE:
                case AFMT_S32_BE:
                        tmpinfo.play.precision =
                        tmpinfo.record.precision = 32;
@@ -241,9 +269,36 @@
                        tmpinfo.record.encoding = AUDIO_ENCODING_AC3;
                        break;
                default:
-                       return EINVAL;
+                       /*
+                        * OSSv4 specifies that if an invalid format is chosen
+                        * by an application then a sensible format supported
+                        * by the hardware is returned.
+                        *
+                        * In this case, we pick the current hardware format.
+                        */
+                       retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt);
+                       if (retval < 0)
+                               return retval;
+                       retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
+                       if (retval < 0)
+                               return retval;
+                       tmpinfo.play.encoding =
+                       tmpinfo.record.encoding =
+                           (tmpinfo.mode == AUMODE_RECORD) ?
+                           hwfmt.record.encoding : hwfmt.play.encoding;
+                       tmpinfo.play.precision =
+                       tmpinfo.record.precision =
+                           (tmpinfo.mode == AUMODE_RECORD) ?
+                           hwfmt.record.precision : hwfmt.play.precision ;
+                       break;
                }
-               (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
+               /*
+                * In the post-kernel-mixer world, assume that any error means
+                * it's fatal rather than an unsupported format being selected.
+                */
+               retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo);
+               if (retval < 0)
+                       return retval;
                /* FALLTHRU */
        case SOUND_PCM_READ_BITS:
                retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
@@ -300,12 +355,10 @@
                INTARG = idat;
                break;
        case SNDCTL_DSP_CHANNELS:
-               AUDIO_INITINFO(&tmpinfo);
-               tmpinfo.play.channels = INTARG;
-               (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
-               AUDIO_INITINFO(&tmpinfo);
-               tmpinfo.record.channels = INTARG;
-               (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
+               retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
+               if (retval < 0)
+                       return retval;
+               setchannels(fd, tmpinfo.mode, INTARG);
                /* FALLTHRU */
        case SOUND_PCM_READ_CHANNELS:
                retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
@@ -448,43 +501,35 @@
                retval = ioctl(fd, AUDIO_GETPROPS, &idata);
                if (retval < 0)
                        return retval;
-               idat = DSP_CAP_TRIGGER; /* pretend we have trigger */
+               idat = DSP_CAP_TRIGGER;
                if (idata & AUDIO_PROP_FULLDUPLEX)
                        idat |= DSP_CAP_DUPLEX;
                if (idata & AUDIO_PROP_MMAP)
                        idat |= DSP_CAP_MMAP;
                INTARG = idat;
                break;
-#if 0
+       case SNDCTL_DSP_SETTRIGGER:
+               retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
+               if (retval < 0)
+                       return retval;
+               AUDIO_INITINFO(&tmpinfo);
+               if (tmpinfo.mode & AUMODE_PLAY)
+                       tmpinfo.play.pause = (INTARG & PCM_ENABLE_OUTPUT) == 0;
+               if (tmpinfo.mode & AUMODE_RECORD)
+                       tmpinfo.record.pause = (INTARG & PCM_ENABLE_INPUT) == 0;
+               (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo);
+               /* FALLTHRU */
        case SNDCTL_DSP_GETTRIGGER:
                retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
                if (retval < 0)
                        return retval;
-               idat = (tmpinfo.play.pause ? 0 : PCM_ENABLE_OUTPUT) |
-                      (tmpinfo.record.pause ? 0 : PCM_ENABLE_INPUT);
-               retval = copyout(&idat, SCARG(uap, data), sizeof idat);
-               if (retval < 0)
-                       return retval;
+               idat = 0;
+               if ((tmpinfo.mode & AUMODE_PLAY) && !tmpinfo.play.pause)
+                       idat |= PCM_ENABLE_OUTPUT;
+               if ((tmpinfo.mode & AUMODE_RECORD) && !tmpinfo.record.pause)
+                       idat |= PCM_ENABLE_INPUT;
+               INTARG = idat;
                break;
-       case SNDCTL_DSP_SETTRIGGER:
-               AUDIO_INITINFO(&tmpinfo);
-               retval = copyin(SCARG(uap, data), &idat, sizeof idat);
-               if (retval < 0)
-                       return retval;
-               tmpinfo.play.pause = (idat & PCM_ENABLE_OUTPUT) == 0;
-               tmpinfo.record.pause = (idat & PCM_ENABLE_INPUT) == 0;
-               (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
-               retval = copyout(&idat, SCARG(uap, data), sizeof idat);
-               if (retval < 0)
-                       return retval;
-               break;
-#else
-       case SNDCTL_DSP_GETTRIGGER:
-       case SNDCTL_DSP_SETTRIGGER:
-               /* XXX Do nothing for now. */
-               INTARG = PCM_ENABLE_OUTPUT;
-               break;
-#endif
        case SNDCTL_DSP_GETIPTR:
                retval = ioctl(fd, AUDIO_GETIOFFS, &tmpoffs);
                if (retval < 0)
@@ -586,41 +631,23 @@
                tmpaudioinfo->next_rec_engine = 0;
                argp = tmpaudioinfo;
                break;
+       case SNDCTL_DSP_SETPLAYVOL:
+               setvol(fd, INTARG, false);
+               /* FALLTHRU */
        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);
+               retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
                if (retval < 0)
                        return retval;
-               break;
-       case SNDCTL_DSP_GETRECVOL:
-               retval = ioctl(fd, AUDIO_GETBUFINFO, &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);
+               setvol(fd, INTARG, true);
+               /* FALLTHRU */
+       case SNDCTL_DSP_GETRECVOL:
+               retval = ioctl(fd, AUDIO_GETINFO, &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);
-               if (retval < 0)
-                       return retval;
+               INTARG = getvol(tmpinfo.record.gain, tmpinfo.record.balance);
                break;
        case SNDCTL_DSP_SKIP:
        case SNDCTL_DSP_SILENCE:
@@ -998,6 +1025,101 @@
        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;



Home | Main Index | Thread Index | Old Index