[MPlayer-dev-eng] [PATCH] SGI IRIX audio format fixes

dega deganews at quickclic.net
Thu Sep 1 05:18:29 CEST 2005


The attached patch fixes the following problems with the current IRIX
audio driver (ao_sgi):

- plays noise when handed unsigned audio streams
- plays twice/half as fast when handed 8bit/32bit audio streams
- plays noise when handed 24-bit audio streams
- accepts little endian audio streams, then plays noise
- float audio streams are not handled correctly
- always sets the sample rate on the first analog output
  channel, even when the default output channel is
  something else (i.e. digital output, analog out 2),
  potentially resulting in incorrect sample rate
- does not set ao_data.bps
- does not update ao_data.samplerate when requested rate cannot
  be set
  - esp. important when using digital out, which cannot go
    below 32KHz
- returns inaccurate value in get_delay()

A side effect of the channel selection fix is that it is now possible to
override the default audio device from the command line:

-ao sgi:<device_name>

On Indy/Indigo/Indigo2/Onyx, one can say, for example, sgi:"Digital Out".
Use "audiopanel -print" to get the list of audio device names.

I've tested the fix on Indigo2, Onyx (with ASO) and O2.

Thanks,
dega


Index: libao2/ao_sgi.c
===================================================================
RCS file: /cvsroot/mplayer/main/libao2/ao_sgi.c,v
retrieving revision 1.12
diff -u -r1.12 ao_sgi.c
--- libao2/ao_sgi.c	27 Feb 2005 23:06:32 -0000	1.12
+++ libao2/ao_sgi.c	1 Sep 2005 02:24:44 -0000
@@ -7,12 +7,15 @@

 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
 #include <dmedia/audio.h>

 #include "audio_out.h"
 #include "audio_out_internal.h"
 #include "mp_msg.h"
 #include "help_mp.h"
+#include "libaf/af_format.h"

 static ao_info_t info =
 {
@@ -29,32 +32,130 @@
 static ALport	ao_port;
 static int sample_rate;
 static int queue_size;
+static int bytes_per_frame;
+static uint64_t us2si_mask;
+
+/**
+ * \param   [in/out]  format
+ * \param   [out]     width
+ * \param   [out]     us2si_x unsigned-to-signed XOR mask
+ *
+ * \return  the closest matching SGI AL sample format
+ *
+ * \note    width is set to required per-channel sample width
+ *          format is updated to match the SGI AL sample format
+ *          us2si_x is 0 if no conversion is necessary
+ */
+static int fmt2sgial(int *format, int *width, uint64_t *us2si_x) {
+  int smpfmt = -1;
+  uint64_t mask = 0UL;
+
+  /* SGI AL only supports float and signed integers in native
+   * endianess. If this is something else, we must rely on the audio
+   * filter to convert it to a compatible format.
+   * The exceptions are unsigned integer formats. We say we support
+   * them natively, and let the play function do the conversion
+   * on the fly. */
+
+  /* 24-bit audio is supported, but only with 32-bit alignment.
+   * mplayer's 24-bit format is packed, unfortunately.
+   * So we must upgrade 24-bit requests to 32 bits. Then we drop the
+   * lowest 8 bits during playback. */
+
+  *us2si_x = 0UL;
+  *width = 0;
+
+  switch(*format & (AF_FORMAT_POINT_MASK | AF_FORMAT_SPECIAL_MASK)) {
+  case AF_FORMAT_I:
+    switch(af_fmt2bits(*format)) {
+    case 8:
+      *width = AL_SAMPLE_8;
+      mask = 0x8080808080808080UL;
+      break;
+    case 16:
+      *width = AL_SAMPLE_16;
+      mask = 0x8000800080008000UL;
+      break;
+    case 24:
+    case 32:
+      *width = AL_SAMPLE_24;
+      mask = 0x8000000080000000UL;
+      *format &= ~AF_FORMAT_BITS_MASK;
+      *format |= AF_FORMAT_32BIT;
+      break;
+    default:
+      *width = AL_SAMPLE_16;
+      mask = 0x8000800080008000UL;
+      *format &= ~AF_FORMAT_BITS_MASK;
+      *format |= AF_FORMAT_16BIT;
+    }
+
+    if ((*format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_US) {
+      *us2si_x = mask;
+    }
+
+    smpfmt = AL_SAMPFMT_TWOSCOMP;
+
+    break;
+
+  case AF_FORMAT_F:
+    *width = 4;
+    smpfmt = AL_SAMPFMT_FLOAT;
+
+    break;
+
+  default:
+    /* No special encodings are natively supported.
+     * Fall back to default format. */
+    *format = AF_FORMAT_S16_NE;
+    *width = AL_SAMPLE_16;
+    smpfmt = AL_SAMPFMT_TWOSCOMP;
+  }
+
+  /* Only support native endianess */
+  *format &= ~AF_FORMAT_END_MASK;
+  *format |= AF_FORMAT_NE;
+
+  return smpfmt;
+}

 // to set/get/query special features/parameters
 static int control(int cmd, void *arg){

   mp_msg(MSGT_AO, MSGL_INFO, MSGTR_AO_SGI_INFO);

-  return -1;
+  switch(cmd) {
+  case AOCONTROL_QUERY_FORMAT:
+    /* Do not reject any format: return the closest matching
+     * format if the request is not supported natively. */
+    return CONTROL_TRUE;
+  }
+
+  return CONTROL_UNKNOWN;
 }

 // open & setup audio device
 // return: 1=success 0=fail
 static int init(int rate, int channels, int format, int flags) {

+  int smpwidth, smpfmt;
+  int rv = AL_DEFAULT_OUTPUT;
+
+  smpfmt = fmt2sgial(&format, &smpwidth, &us2si_mask);
+
   mp_msg(MSGT_AO, MSGL_INFO, MSGTR_AO_SGI_InitInfo, rate, (channels > 1) ? "Stereo" : "Mono", af_fmt2str_short(format));

   { /* from /usr/share/src/dmedia/audio/setrate.c */

-    int fd;
-    int rv;
-    double frate;
+    double frate, realrate;
     ALpv x[2];

-    rv = alGetResourceByName(AL_SYSTEM, "out.analog", AL_DEVICE_TYPE);
-    if (!rv) {
-      mp_msg(MSGT_AO, MSGL_ERR, MSGTR_AO_SGI_InvalidDevice);
-      return 0;
+    if(ao_subdevice) {
+      rv = alGetResourceByName(AL_SYSTEM, ao_subdevice, AL_OUTPUT_DEVICE_TYPE);
+      if (!rv) {
+	mp_msg(MSGT_AO, MSGL_ERR, MSGTR_AO_SGI_InvalidDevice);
+	return 0;
+      }
     }

     frate = rate;
@@ -76,15 +177,21 @@
       mp_msg(MSGT_AO, MSGL_WARN, MSGTR_AO_SGI_CantGetParms, alGetErrorString(oserror()));
     }

-    if (frate != alFixedToDouble(x[0].value.ll)) {
-      mp_msg(MSGT_AO, MSGL_INFO, MSGTR_AO_SGI_SampleRateInfo, alFixedToDouble(x[0].value.ll), frate);
+    realrate = alFixedToDouble(x[0].value.ll);
+    if (frate != realrate) {
+      mp_msg(MSGT_AO, MSGL_INFO, MSGTR_AO_SGI_SampleRateInfo, realrate, frate);
     }
-    sample_rate = (int)frate;
+    sample_rate = (int)realrate;
   }

+  bytes_per_frame = channels * smpwidth;
+
+  ao_data.samplerate = sample_rate;
+  ao_data.channels = channels;
+  ao_data.format = format;
+  ao_data.bps = sample_rate * bytes_per_frame;
   ao_data.buffersize=131072;
   ao_data.outburst = ao_data.buffersize/16;
-  ao_data.channels = channels;

   ao_config = alNewConfig();

@@ -93,14 +200,11 @@
     return 0;
   }

-  if(channels == 2) alSetChannels(ao_config, AL_STEREO);
-  else alSetChannels(ao_config, AL_MONO);
-
-  alSetWidth(ao_config, AL_SAMPLE_16);
-  alSetSampFmt(ao_config, AL_SAMPFMT_TWOSCOMP);
-  alSetQueueSize(ao_config, 48000);
-
-  if (alSetDevice(ao_config, AL_DEFAULT_OUTPUT) < 0) {
+  if(alSetChannels(ao_config, channels) < 0 ||
+     alSetWidth(ao_config, smpwidth) < 0 ||
+     alSetSampFmt(ao_config, smpfmt) < 0 ||
+     alSetQueueSize(ao_config, sample_rate) < 0 ||
+     alSetDevice(ao_config, rv) < 0) {
     mp_msg(MSGT_AO, MSGL_ERR, MSGTR_AO_SGI_InitConfigError, alGetErrorString(oserror()));
     return 0;
   }
@@ -125,11 +229,16 @@

   mp_msg(MSGT_AO, MSGL_INFO, MSGTR_AO_SGI_Uninit);

+  if (ao_config) {
+    alFreeConfig(ao_config);
+    ao_config = NULL;
+  }
+
   if (ao_port) {
     if (!immed)
     while(alGetFilled(ao_port) > 0) sginap(1);
     alClosePort(ao_port);
-    alFreeConfig(ao_config);
+    ao_port = NULL;
   }

 }
@@ -139,6 +248,7 @@

   mp_msg(MSGT_AO, MSGL_INFO, MSGTR_AO_SGI_Reset);

+  alDiscardFrames(ao_port, queue_size);
 }

 // stop playing, keep buffers (for pause)
@@ -158,10 +268,10 @@
 // return: how many bytes can be played without blocking
 static int get_space() {

-  // printf("ao_sgi, get_space: (ao_outburst %d)\n", ao_outburst);
+  // printf("ao_sgi, get_space: (ao_outburst %d)\n", ao_data.outburst);
   // printf("ao_sgi, get_space: alGetFillable [%d] \n", alGetFillable(ao_port));

-  return alGetFillable(ao_port)*(2*ao_data.channels);
+  return alGetFillable(ao_port) * bytes_per_frame;

 }

@@ -171,12 +281,34 @@
 // return: number of bytes played
 static int play(void* data, int len, int flags) {

+  /* Always process data in quadword-aligned chunks (64-bits). */
+  const int plen = len / (sizeof(uint64_t) * bytes_per_frame);
+  const int framecount = plen * sizeof(uint64_t);
+
   // printf("ao_sgi, play: len %d flags %d (%d %d)\n", len, flags, ao_port, ao_config);
-  // printf("channels %d\n", ao_channels);
+  // printf("channels %d\n", ao_data.channels);

-  alWriteFrames(ao_port, data, len/(2*ao_data.channels));
-
-  return len;
+  if(us2si_mask) {
+    /* Perform parallel on-the-fly unsigned-to-signed conversion */
+    register uint64_t *smpls = data;
+    const uint64_t *smple = smpls + (plen * bytes_per_frame);
+    const uint64_t mask = us2si_mask;
+    while(smpls < smple)
+      *smpls++ ^= mask;
+  }
+
+  if((ao_data.format & (AF_FORMAT_BITS_MASK | AF_FORMAT_POINT_MASK)) ==
+     (AF_FORMAT_32BIT | AF_FORMAT_I)) {
+    /* libaudio expects doubleword-aligned 24-bit values */
+    int32_t *smpls = data;
+    const int32_t *smple = smpls + (framecount * ao_data.channels);
+    while(smpls < smple)
+      *smpls++ >>= 8;
+  }
+
+  alWriteFrames(ao_port, data, framecount);
+
+  return framecount * bytes_per_frame;

 }

@@ -185,8 +317,10 @@

   // printf("ao_sgi, get_delay: (ao_buffersize %d)\n", ao_buffersize);

-  //return 0;
-  return  (float)queue_size/((float)sample_rate);
+  // return  (float)queue_size/((float)sample_rate);
+  const int outstanding = alGetFilled(ao_port);
+  return (float)((outstanding < 0) ? queue_size : outstanding) /
+    ((float)sample_rate);
 }





More information about the MPlayer-dev-eng mailing list