changeset 16667:27a2bc4aad72

General bug fixes, like missing includes, formats that were incorrectly claimed to be supported etc. Patch by dega (dega quickclic net)
author reimar
date Tue, 04 Oct 2005 22:23:51 +0000
parents 36fbad3f4aa6
children de87e6b2a4c7
files libao2/ao_sgi.c
diffstat 1 files changed, 133 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/libao2/ao_sgi.c	Tue Oct 04 22:12:16 2005 +0000
+++ b/libao2/ao_sgi.c	Tue Oct 04 22:23:51 2005 +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,110 @@
 static ALport	ao_port;
 static int sample_rate;
 static int queue_size;
+static int bytes_per_frame;
+
+/**
+ * \param   [in/out]  format
+ * \param   [out]     width
+ *
+ * \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
+ */
+static int fmt2sgial(int *format, int *width) {
+  int smpfmt = AL_SAMPFMT_TWOSCOMP;
+
+  /* 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. */
+
+  /* 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. */
+
+  switch(*format) {
+  case AF_FORMAT_U8:
+  case AF_FORMAT_S8:
+    *width = AL_SAMPLE_8;
+    *format = AF_FORMAT_S8;
+    break;
+
+  case AF_FORMAT_U16_LE:
+  case AF_FORMAT_U16_BE:
+  case AF_FORMAT_S16_LE:
+  case AF_FORMAT_S16_BE:
+    *width = AL_SAMPLE_16;
+    *format = AF_FORMAT_S16_NE;
+    break;
+
+  case AF_FORMAT_U24_LE:
+  case AF_FORMAT_U24_BE:
+  case AF_FORMAT_S24_LE:
+  case AF_FORMAT_S24_BE:
+  case AF_FORMAT_U32_LE:
+  case AF_FORMAT_U32_BE:
+  case AF_FORMAT_S32_LE:
+  case AF_FORMAT_S32_BE:
+    *width = AL_SAMPLE_24;
+    *format = AF_FORMAT_S32_NE;
+    break;
+
+  case AF_FORMAT_FLOAT_LE:
+  case AF_FORMAT_FLOAT_BE:
+    *width = 4;
+    *format = AF_FORMAT_FLOAT_NE;
+    smpfmt = AL_SAMPFMT_FLOAT;
+    break;
+
+  default:
+    *width = AL_SAMPLE_16;
+    *format = AF_FORMAT_S16_NE;
+    break;
+
+  }
+
+  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);
+
   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 +157,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 +180,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 +209,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 +228,7 @@
   
   mp_msg(MSGT_AO, MSGL_INFO, MSGTR_AO_SGI_Reset);
   
+  alDiscardFrames(ao_port, queue_size);
 }
 
 // stop playing, keep buffers (for pause)
@@ -158,10 +248,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 +261,24 @@
 // 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(ao_data.format == AF_FORMAT_S32_NE) {
+    /* The zen of this is explained in fmt2sgial() */
+    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 +287,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);
 }