changeset 13832:2f8cfe66dbfd

Different buffering scheme, avoiding possible races (SDL is using threads!).
author reimar
date Sun, 31 Oct 2004 21:02:47 +0000
parents 719c3c818335
children b2eaddcce355
files libao2/ao_sdl.c
diffstat 1 files changed, 66 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/libao2/ao_sdl.c	Sun Oct 31 19:58:36 2004 +0000
+++ b/libao2/ao_sdl.c	Sun Oct 31 21:02:47 2004 +0000
@@ -46,68 +46,78 @@
 #define SAMPLESIZE 1024
 #endif
 
-// General purpose Ring-buffering routines
-
-#define BUFFSIZE 4096
-#define NUM_BUFS 8
+#define CHUNK_SIZE 4096
+#define NUM_CHUNKS 8
+// This type of ring buffer may never fill up completely, at least
+// one byte must always be unused.
+// For performance reasons (alignment etc.) one whole chunk always stays
+// empty, not only one byte.
+#define BUFFSIZE ((NUM_CHUNKS + 1) * CHUNK_SIZE)
 
-static unsigned char *buffer[NUM_BUFS];
+static unsigned char *buffer;
 
-static unsigned int buf_read=0;
-static unsigned int buf_write=0;
-static unsigned int buf_read_pos=0;
-static unsigned int buf_write_pos=0;
+// may only be modified by SDL's playback thread or while it is stopped
+static volatile int read_pos;
+// may only be modified by mplayer's thread
+static volatile int write_pos;
 #ifdef USE_SDL_INTERNAL_MIXER
 static unsigned char volume=SDL_MIX_MAXVOLUME;
 #endif
-static int full_buffers=0;
-static int buffered_bytes=0;
 
+// may only be called by mplayer's thread
+// return value may change between immediately following two calls,
+// and the real number of free bytes might be larger!
+static int buf_free() {
+  int free = read_pos - write_pos - CHUNK_SIZE;
+  if (free < 0) free += BUFFSIZE;
+  return free;
+}
+
+// may only be called by SDL's playback thread
+// return value may change between immediately following two calls,
+// and the real number of buffered bytes might be larger!
+static int buf_used() {
+  int used = write_pos - read_pos;
+  if (used < 0) used += BUFFSIZE;
+  return used;
+}
 
 static int write_buffer(unsigned char* data,int len){
-  int len2=0;
-  int x;
-  while(len>0){
-    if(full_buffers==NUM_BUFS) break;
-    x=BUFFSIZE-buf_write_pos;
-    if(x>len) x=len;
-    memcpy(buffer[buf_write]+buf_write_pos,data+len2,x);
-    if (buf_write_pos==0)
-	++full_buffers;
-    len2+=x; len-=x;
-    buffered_bytes+=x; buf_write_pos+=x;
-    if(buf_write_pos>=BUFFSIZE){
-       // block is full, find next!
-       buf_write=(buf_write+1)%NUM_BUFS;
-       buf_write_pos=0;
-    }
+  int first_len = BUFFSIZE - write_pos;
+  int free = buf_free();
+  if (len > free) len = free;
+  if (first_len > len) first_len = len;
+  // till end of buffer
+  memcpy (&buffer[write_pos], data, first_len);
+  if (len > first_len) { // we have to wrap around
+    // remaining part from beginning of buffer
+    memcpy (buffer, &data[first_len], len - first_len);
   }
-  return len2;
+  write_pos = (write_pos + len) % BUFFSIZE;
+  return len;
 }
 
 static int read_buffer(unsigned char* data,int len){
-  int len2=0;
-  int x;
-  while(len>0){
-    if(buffered_bytes==0) break; // no more data buffered!
-    x=BUFFSIZE-buf_read_pos;
-    if(x>len) x=len;
-    if (x>buffered_bytes) x=buffered_bytes;
+  int first_len = BUFFSIZE - read_pos;
+  int buffered = buf_used();
+  if (len > buffered) len = buffered;
+  if (first_len > len) first_len = len;
+  // till end of buffer
 #ifdef USE_SDL_INTERNAL_MIXER
-    SDL_MixAudio(data+len2,buffer[buf_read]+buf_read_pos,x,volume);
+  SDL_MixAudio (data, &buffer[read_pos], first_len, volume);
 #else
-    memcpy(data+len2,buffer[buf_read]+buf_read_pos,x);
+  memcpy (data, &buffer[read_pos], first_len);
 #endif
-    len2+=x; len-=x;
-    buffered_bytes-=x; buf_read_pos+=x;
-    if(buf_read_pos>=BUFFSIZE){
-       // block is empty, find next!
-       buf_read=(buf_read+1)%NUM_BUFS;
-       --full_buffers;
-       buf_read_pos=0;
-    }
+  if (len > first_len) { // we have to wrap around
+    // remaining part from beginning of buffer
+#ifdef USE_SDL_INTERNAL_MIXER
+    SDL_MixAudio (&data[first_len], buffer, len - first_len, volume);
+#else
+    memcpy (&data[first_len], buffer, len - first_len);
+#endif
   }
-  return len2;
+  read_pos = (read_pos + len) % BUFFSIZE;
+  return len;
 }
 
 // end ring buffer stuff
@@ -168,9 +178,8 @@
 	/* SDL Audio Specifications */
 	SDL_AudioSpec aspec, obtained;
 	
-	int i;
 	/* Allocate ring-buffer memory */
-	for(i=0;i<NUM_BUFS;i++) buffer[i]=(unsigned char *) malloc(BUFFSIZE);
+	buffer = (unsigned char *) malloc(BUFFSIZE);
 
 	mp_msg(MSGT_AO,MSGL_INFO,MSGTR_AO_SDL_INFO, rate, (channels > 1) ? "Stereo" : "Mono", audio_out_format_name(format));
 
@@ -272,6 +281,7 @@
 	mp_msg(MSGT_AO,MSGL_V,"SDL: buf size = %d\n",obtained.size);
 	ao_data.buffersize=obtained.size;
 	
+	reset();
 	/* unsilence audio, if callback is ready */
 	SDL_PauseAudio(0);
 
@@ -281,7 +291,7 @@
 // close audio device
 static void uninit(int immed){
 	mp_msg(MSGT_AO,MSGL_V,"SDL: Audio Subsystem shutting down!\n");
-	while(buffered_bytes > 0)
+	while(buf_free() < BUFFSIZE - CHUNK_SIZE)
 		usec_sleep(50000);
 	SDL_CloseAudio();
 	SDL_QuitSubSystem(SDL_INIT_AUDIO);
@@ -292,15 +302,11 @@
 
 	//printf("SDL: reset called!\n");	
 
+	SDL_PauseAudio(1);
 	/* Reset ring-buffer state */
-	buf_read=0;
-	buf_write=0;
-	buf_read_pos=0;
-	buf_write_pos=0;
-
-	full_buffers=0;
-	buffered_bytes=0;
-
+	read_pos = 0;
+	write_pos = 0;
+	SDL_PauseAudio(0);
 }
 
 // stop playing, keep buffers (for pause)
@@ -322,7 +328,7 @@
 
 // return: how many bytes can be played without blocking
 static int get_space(){
-    return NUM_BUFS*BUFFSIZE - buffered_bytes;
+    return buf_free();
 }
 
 // plays 'len' bytes of 'data'
@@ -348,7 +354,8 @@
 
 // return: delay in seconds between first and last sample in buffer
 static float get_delay(){
-    return (float)(buffered_bytes + ao_data.buffersize)/(float)ao_data.bps;
+    int buffered = BUFFSIZE - CHUNK_SIZE - buf_free(); // could be less
+    return (float)(buffered + ao_data.buffersize)/(float)ao_data.bps;
 }