changeset 1196:fc51929ec8ea

Applied patch by J¸«ärgen Keil (jk@tools.de), improves smoothness of video playback.
author atmosfear
date Fri, 22 Jun 2001 21:37:13 +0000
parents d45ff719db7a
children 80ca716ce5e9
files libao2/ao_sun.c
diffstat 1 files changed, 204 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/libao2/ao_sun.c	Fri Jun 22 20:23:14 2001 +0000
+++ b/libao2/ao_sun.c	Fri Jun 22 21:37:13 2001 +0000
@@ -1,12 +1,14 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
 #include <sys/ioctl.h>
-#include <unistd.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <fcntl.h>
 #include <sys/audioio.h>
 #ifdef	__svr4__
 #include <stropts.h>
@@ -20,18 +22,23 @@
 
 static ao_info_t info = 
 {
-	"Sun audio output",
-	"sun",
-	"jk@tools.de",
-	""
+    "Sun audio output",
+    "sun",
+    "jk@tools.de",
+    ""
 };
 
 LIBAO_EXTERN(sun)
 
 
+/* These defines are missing on NetBSD */
 #ifndef	AUDIO_PRECISION_8
-#define AUDIO_PRECISION_8  8
-#define AUDIO_PRECISION_16 16
+#define AUDIO_PRECISION_8	8
+#define AUDIO_PRECISION_16	16
+#endif
+#ifndef	AUDIO_CHANNELS_MONO
+#define	AUDIO_CHANNELS_MONO	1
+#define	AUDIO_CHANNELS_STEREO	2
 #endif
 
 
@@ -43,9 +50,19 @@
 // ao_outburst
 // ao_buffersize
 
-static char *dsp="/dev/audio";
+static char *audio_dev = "/dev/audio";
 static int queued_bursts = 0;
-static int audio_fd=-1;
+static int queued_samples = 0;
+static int bytes_per_sample = 0;
+static int audio_fd = -1;
+static enum {
+    RTSC_UNKNOWN = 0,
+    RTSC_ENABLED,
+    RTSC_DISABLED
+} enable_sample_timing;
+
+extern int verbose;
+
 
 // convert an OSS audio format specification into a sun audio encoding
 static int oss2sunfmt(int oss_format)
@@ -68,16 +85,127 @@
   }
 }
 
+// try to figure out, if the soundcard driver provides usable (precise)
+// sample counter information
+static int realtime_samplecounter_available(char *dev)
+{
+    int fd = -1;
+    audio_info_t info;
+    int rtsc_ok = RTSC_DISABLED;
+    int len;
+    void *silence = NULL;
+    struct timeval start, end;
+    struct timespec delay;
+    int usec_delay;
+    unsigned last_samplecnt;
+    unsigned increment;
+    unsigned min_increment;
+
+    len = 44100 * 4 / 4;    // amount of data for 0.25sec of 44.1khz, stereo, 16bit
+    silence = calloc(1, len);
+    if (silence == NULL)
+	goto error;
+    
+    if ((fd = open(dev, O_WRONLY)) < 0)
+	goto error;
+
+    AUDIO_INITINFO(&info);
+    info.play.sample_rate = 44100;
+    info.play.channels = AUDIO_CHANNELS_STEREO;
+    info.play.precision = AUDIO_PRECISION_16;
+    info.play.encoding = AUDIO_ENCODING_LINEAR;
+    info.play.samples = 0;
+    if (ioctl(fd, AUDIO_SETINFO, &info)) {
+	if (verbose)
+	    printf("rtsc: SETINFO failed\n");
+	goto error;
+    }
+    
+    if (write(fd, silence, len) != len) {
+	if (verbose)
+	    printf("rtsc: write failed");
+	goto error;
+    }
+
+    if (ioctl(fd, AUDIO_GETINFO, &info)) {
+	if (verbose)
+	    perror("rtsc: GETINFO1");
+	goto error;
+    }
+
+    last_samplecnt = info.play.samples;
+    min_increment = ~0;
+
+    gettimeofday(&start, NULL);
+    for (;;) {
+	delay.tv_sec = 0;
+	delay.tv_nsec = 10000000;
+	nanosleep(&delay, NULL);
+	gettimeofday(&end, NULL);
+	usec_delay = (end.tv_sec - start.tv_sec) * 1000000
+	    + end.tv_usec - start.tv_usec;
+
+	// stop monitoring sample counter after 0.2 seconds
+	if (usec_delay > 200000)
+	    break;
+
+	if (ioctl(fd, AUDIO_GETINFO, &info)) {
+	    if (verbose)
+		perror("rtsc: GETINFO2 failed");
+	    goto error;
+	}
+	if (info.play.samples < last_samplecnt) {
+	    if (verbose)
+		printf("rtsc: %d > %d?\n", last_samplecnt, info.play.samples);
+	    goto error;
+	}
+
+	if ((increment = info.play.samples - last_samplecnt) > 0) {
+	    if (verbose)
+		printf("ao_sun: sample counter increment: %d\n", increment);
+	    if (increment < min_increment) {
+		min_increment = increment;
+		if (min_increment < 2000)
+		    break;	// looks good
+	    }
+	}
+	last_samplecnt = info.play.samples;
+    }
+
+    if (min_increment < 2000)
+	rtsc_ok = RTSC_ENABLED;
+
+    if (verbose)
+	printf("ao_sun: minimum sample counter increment per 10msec interval: %d\n"
+	       "\t%susing sample counter based timing code\n",
+	       min_increment, rtsc_ok == RTSC_ENABLED ? "" : "not ");
+    
+
+error:
+    if (silence != NULL) free(silence);
+    if (fd >= 0) {
+#ifdef	__svr4__
+	// remove the 0 bytes from the above measurement from the
+	// audio driver's STREAMS queue
+	ioctl(fd, I_FLUSH, FLUSHW);
+#endif
+	//ioctl(fd, AUDIO_DRAIN, 0);
+	close(fd);
+    }
+
+    return rtsc_ok;
+}
+
 // to set/get/query special features/parameters
 static int control(int cmd,int arg){
-    switch(cmd){
-	case AOCONTROL_SET_DEVICE:
-	    dsp=(char*)arg;
-	    return CONTROL_OK;
-	case AOCONTROL_QUERY_FORMAT:
-	    return CONTROL_TRUE;
-    }
-    return CONTROL_UNKNOWN;
+  switch(cmd){
+    case AOCONTROL_SET_DEVICE:
+      audio_dev=(char*)arg;
+      return CONTROL_OK;
+    case AOCONTROL_QUERY_FORMAT:
+      return CONTROL_TRUE;
+  }
+  return CONTROL_UNKNOWN;
 }
 
 // open & setup audio device
@@ -87,11 +215,17 @@
   audio_info_t info;
   int byte_per_sec;
 
-  printf("ao2: %d Hz  %d chans  0x%X\n",rate,channels,format);
+  if (enable_sample_timing == RTSC_UNKNOWN
+      && !getenv("AO_SUN_DISABLE_SAMPLE_TIMING")) {
+      enable_sample_timing = realtime_samplecounter_available(audio_dev);
+  }
 
-  audio_fd=open(dsp, O_WRONLY);
+  printf("ao2: %d Hz  %d chans  %s [0x%X]\n",
+	 rate,channels,audio_out_format_name(format),format);
+
+  audio_fd=open(audio_dev, O_WRONLY);
   if(audio_fd<0){
-    printf("Can't open audio device %s  -> nosound\n",dsp);
+    printf("Can't open audio device %s, %s  -> nosound\n", audio_dev, strerror(errno));
     return 0;
   }
 
@@ -101,15 +235,12 @@
   info.play.encoding = oss2sunfmt(ao_format = format);
   info.play.precision = (format==AFMT_S16_LE? AUDIO_PRECISION_16:AUDIO_PRECISION_8);
   info.play.channels = ao_channels = channels;
-  --ao_channels;
   info.play.sample_rate = ao_samplerate = rate;
-  info.play.samples = 0;
-  info.play.eof = 0;
   if(ioctl (audio_fd, AUDIO_SETINFO, &info)<0)
     printf("audio_setup: your card doesn't support %d channel, %s, %d Hz samplerate\n",channels,audio_out_format_name(format),rate);
-  byte_per_sec = (channels * info.play.precision * rate);
-  ao_outburst=byte_per_sec > 100000 ? 16384 : 8192;
-  queued_bursts = 0;
+  bytes_per_sample = channels * info.play.precision / 8;
+  byte_per_sec = bytes_per_sample * rate;
+  ao_outburst = byte_per_sec > 100000 ? 16384 : 8192;
 
   if(ao_buffersize==-1){
     // Measuring buffer size:
@@ -141,11 +272,24 @@
 #endif
   }
 
-    return 1;
+  AUDIO_INITINFO(&info);
+  info.play.samples = 0;
+  info.play.eof = 0;
+  info.play.error = 0;
+  ioctl (audio_fd, AUDIO_SETINFO, &info);
+
+  queued_bursts = 0;
+  queued_samples = 0;
+
+  return 1;
 }
 
 // close audio device
 static void uninit(){
+#ifdef	__svr4__
+    // throw away buffered data in the audio driver's STREAMS queue
+    ioctl(audio_fd, I_FLUSH, FLUSHW);
+#endif
     close(audio_fd);
 }
 
@@ -153,14 +297,10 @@
 static void reset(){
     audio_info_t info;
 
-#ifdef	__svr4__
-    // throw away buffered data in the audio driver's STREAMS queue
-    ioctl(audio_fd, I_FLUSH, FLUSHW);
-#endif
     uninit();
-    audio_fd=open(dsp, O_WRONLY);
+    audio_fd=open(audio_dev, O_WRONLY);
     if(audio_fd<0){
-	printf("\nFatal error: *** CANNOT RE-OPEN / RESET AUDIO DEVICE ***\n");
+	printf("\nFatal error: *** CANNOT RE-OPEN / RESET AUDIO DEVICE (%s) ***\n", strerror(errno));
 	return;
     }
 
@@ -169,12 +309,14 @@
     AUDIO_INITINFO(&info);
     info.play.encoding = oss2sunfmt(ao_format);
     info.play.precision = (ao_format==AFMT_S16_LE? AUDIO_PRECISION_16:AUDIO_PRECISION_8);
-    info.play.channels = ao_channels+1;
+    info.play.channels = ao_channels;
     info.play.sample_rate = ao_samplerate;
     info.play.samples = 0;
     info.play.eof = 0;
+    info.play.error = 0;
     ioctl (audio_fd, AUDIO_SETINFO, &info);
     queued_bursts = 0;
+    queued_samples = 0;
 }
 
 // stop playing, keep buffers (for pause)
@@ -198,49 +340,54 @@
 
 // return: how many bytes can be played without blocking
 static int get_space(){
-  int playsize=ao_outburst;
+    int playsize = ao_outburst;
+    audio_info_t info;
 
     // check buffer
 #ifdef HAVE_AUDIO_SELECT
-    {  fd_set rfds;
-       struct timeval tv;
-       FD_ZERO(&rfds);
-       FD_SET(audio_fd, &rfds);
-       tv.tv_sec = 0;
-       tv.tv_usec = 0;
-       if(!select(audio_fd+1, NULL, &rfds, NULL, &tv)) return 0; // not block!
+    {
+	fd_set rfds;
+	struct timeval tv;
+	FD_ZERO(&rfds);
+	FD_SET(audio_fd, &rfds);
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
+	if(!select(audio_fd+1, NULL, &rfds, NULL, &tv)) return 0; // not block!
     }
 #endif
 
-  {
-    audio_info_t info;
     ioctl(audio_fd, AUDIO_GETINFO, &info);
-    if(queued_bursts - info.play.eof > 2)
-      return 0;
-  }
-  return ao_outburst;
+    if (queued_bursts - info.play.eof > 2)
+	return 0;
+
+    return ao_outburst;
 }
 
 // plays 'len' bytes of 'data'
 // it should round it down to outburst*n
 // return: number of bytes played
 static int play(void* data,int len,int flags){
-    len/=ao_outburst;
-    len=write(audio_fd,data,len*ao_outburst);
-    if(len>0) {
-      queued_bursts ++;
-      write(audio_fd,data,0);
+    if (len < ao_outburst) return 0;
+    len /= ao_outburst;
+    len = write(audio_fd, data, len*ao_outburst);
+    if(len > 0) {
+      queued_samples += len / bytes_per_sample;
+      if (write(audio_fd,data,0) < 0)
+	  perror("ao_sun: send EOF audio record");
+      else
+	  queued_bursts ++;
     }
     return len;
 }
 
-static int audio_delay_method=2;
 
 // return: how many unplayed bytes are in the buffer
 static int get_delay(){
-    int q;
     audio_info_t info;
     ioctl(audio_fd, AUDIO_GETINFO, &info);
-    return (queued_bursts - info.play.eof) * ao_outburst;
+    if (info.play.samples && enable_sample_timing == RTSC_ENABLED)
+	return (queued_samples - info.play.samples) * bytes_per_sample;
+    else
+	return (queued_bursts - info.play.eof) * ao_outburst;
 }