All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Eric C. Weaver" <weav@sigma.net>
To: Alsa-devel@lists.sourceforge.net
Subject: Buffering Patch to aplay / arecord 1.0.2
Date: Fri, 05 Mar 2004 16:55:52 -0800	[thread overview]
Message-ID: <40492198.3000309@sigma.net> (raw)

[-- Attachment #1: Type: text/plain, Size: 837 bytes --]


The attached diff has my change to aplay.c:capture_go and supporting globals, 
to implement a two-thread double-buffering approach to capture.  I kept having 
overruns, due apparently to file system lock contention in RedHat 9 (2.4.20-8).

The approach is to allocate a 10-second-long buffer (rounded up to a multiple 
of chunk-size), start a disk-write thread to follow behind the original 
thread, and if the file system lock causes the write to block for a while, 
capturing continues unabated.  10 seconds has been sufficient so far; the 
longest FS lock I could cause was 9.5 seconds.  No overruns (with a big enough 
buffer-size) since this patch has been in use!

I release the IP in this patch to the ALSA project for inclusion, should the 
pertinent community members find it worthwhile.

Cheers...

Eric Weaver
Palo Alto, CA


[-- Attachment #2: aplay.diff.text --]
[-- Type: text/plain, Size: 7611 bytes --]

*** aplay.c.orig	2003-11-28 02:24:53.000000000 -0800
--- aplay.c	2004-03-03 16:38:47.000000000 -0800
***************
*** 38,47 ****
--- 38,49 ----
  #include <errno.h>
  #include <alsa/asoundlib.h>
  #include <assert.h>
+ #include <sched.h>		/* Weav */
  #include <sys/poll.h>
  #include <sys/uio.h>
  #include <sys/time.h>
  #include <sys/signal.h>
+ #include <sys/wait.h>
  #include "aconfig.h"
  #include "formats.h"
  #include "version.h"
***************
*** 78,83 ****
--- 80,88 ----
  static int interleaved = 1;
  static int nonblock = 0;
  static char *audiobuf = NULL;
+ static char *audiobuf_writep = NULL; /* Weav */
+ static char *audiobuf_readp = NULL; /* Weav */
+ static int capture_done = 0;
  static snd_pcm_uframes_t chunk_size = 0;
  static unsigned period_time = 0;
  static unsigned buffer_time = 0;
***************
*** 90,101 ****
--- 95,111 ----
  static int buffer_pos = 0;
  static size_t bits_per_sample, bits_per_frame;
  static size_t chunk_bytes;
+ static size_t audiobuf_bytes;	/* Weav */
  static snd_output_t *log;
  
  static int fd = -1;
  static off64_t pbrec_count = (size_t)-1, fdcount;
  static int vocmajor, vocminor;
  
+ static int thread_stack_elts = 10240;
+ static char *thread_stack[10240];
+ 
+ 
  /* needed prototypes */
  
  static void playback(char *filename);
***************
*** 524,529 ****
--- 534,540 ----
  		error("not enough memory");
  		return 1;
  	}
+ 	audiobuf_writep = audiobuf_readp = audiobuf; /* Weav */
  
  	if (mmap_flag) {
  		writei_func = snd_pcm_mmap_writei;
***************
*** 932,942 ****
  	bits_per_sample = snd_pcm_format_physical_width(hwparams.format);
  	bits_per_frame = bits_per_sample * hwparams.channels;
  	chunk_bytes = chunk_size * bits_per_frame / 8;
! 	audiobuf = realloc(audiobuf, chunk_bytes);
  	if (audiobuf == NULL) {
  		error("not enough memory");
  		exit(EXIT_FAILURE);
  	}
  	// fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size);
  }
  
--- 943,960 ----
  	bits_per_sample = snd_pcm_format_physical_width(hwparams.format);
  	bits_per_frame = bits_per_sample * hwparams.channels;
  	chunk_bytes = chunk_size * bits_per_frame / 8;
! 	audiobuf_bytes = 10 * rate * bits_per_frame / 8; /* Weav */
! 	audiobuf_bytes += chunk_bytes - (audiobuf_bytes % chunk_bytes);
! #if 0
! 	d_printf("audiobuf size = %d (0x%x) bytes\n",
! 		 audiobuf_bytes, audiobuf_bytes);
! #endif
! 	audiobuf = realloc(audiobuf, audiobuf_bytes); /* Weav */
  	if (audiobuf == NULL) {
  		error("not enough memory");
  		exit(EXIT_FAILURE);
  	}
+ 	audiobuf_writep = audiobuf_readp = audiobuf; /* Weav */
  	// fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size);
  }
  
***************
*** 1817,1847 ****
  
  /* capturing raw data, this proc handels WAVE files and .VOCs (as one block) */
  
  void capture_go(int fd, size_t count, int rtype, char *name)
  {
  	size_t c, cur;
  	ssize_t r, err;
  
  	header(rtype, name);
  	set_params();
! 
  	do {
  		for (cur = count; cur > 0; cur -= r) {
  			c = cur;
  			if (c > chunk_bytes)
  				c = chunk_bytes;
  			c = c * 8 / bits_per_frame;
! 			if ((size_t)(r = pcm_read(audiobuf, c)) != c)
  				break;
  			r = r * bits_per_frame / 8;
  			if ((err = write(fd, audiobuf, r)) != r) {
  				perror(name);
  				exit(EXIT_FAILURE);
  			}
  			if (err > 0)
  				fdcount += err;
  		}
  	} while (rtype == FORMAT_RAW && !timelimit);
  }
  
  /*
--- 1835,1976 ----
  
  /* capturing raw data, this proc handels WAVE files and .VOCs (as one block) */
  
+ 
+ int capture_writeloop (void *arg)
+ {
+   int fd = *(int*)arg;
+ 
+ #if 0
+   d_printf("Entering capture_writeloop; fd = %d\n", fd);
+ #endif
+   do {
+ #if 0
+     d_printf("readp = 0x%x; writep = 0x%x; cd=%d\n",
+ 	     audiobuf_readp, audiobuf_writep, capture_done);
+ #endif
+     if (audiobuf_readp > audiobuf_writep) {	/* wrapped */
+       int bytes = write(fd, audiobuf_readp, audiobuf_bytes - (audiobuf_readp - audiobuf));
+       if (bytes < 0) {
+ 	perror("capture_writeloop: write");
+ 	return 0;
+       }
+       fdcount += bytes;
+ #if 0
+       d_printf("Wrote %d bytes from 0x%x\n", bytes, audiobuf_readp);
+ #endif
+       audiobuf_readp += bytes;
+       if (audiobuf_readp >= audiobuf + audiobuf_bytes) /* (un)wrap... */
+ 	audiobuf_readp = audiobuf;
+     }
+     if (audiobuf_writep > audiobuf_readp) { /* Got some ahead of us */
+       int bytes = write (fd, audiobuf_readp, audiobuf_writep - audiobuf_readp);
+       if (bytes < 0) {
+ 	perror("capture_writeloop: write");
+ 	return 0;
+       }
+       fdcount += bytes;
+ #if 0
+       d_printf("Wrote %d bytes from 0x%x\n", bytes, audiobuf_readp);
+ #endif
+       audiobuf_readp += bytes;
+     }
+     else
+       /* nanosleep(500000000);	/* 1/2 sec. */
+       sleep(1);
+   } while (!capture_done || (audiobuf_readp != audiobuf_writep));
+   return 0;
+ }
+ 
+ 
  void capture_go(int fd, size_t count, int rtype, char *name)
  {
  	size_t c, cur;
  	ssize_t r, err;
+ 	int cloneid;
+ 	pthread_t threadid;
+ 	pthread_attr_t thread_attr;
+ 	/*
+ 	int writeloop_stack_elts = 1024 * 10;
+ 	char *writeloop_stack = malloc(writeloop_stack_elts);
+ 	*/
+ 	void *clone_stack_ptr = &thread_stack[(thread_stack_elts - 8) & ~7];
  
  	header(rtype, name);
  	set_params();
! 	capture_done = 0;
! #if 0
! 	d_printf("thread_stack = 0x%x; &fd = 0x%x, fd = %d\n",
! 		thread_stack, &fd, fd);
! 	d_printf("clone_stack_ptr = 0x%x\n", clone_stack_ptr);
! #endif
! 	cloneid = clone(&capture_writeloop, clone_stack_ptr,
! 			CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
! 			CLONE_PTRACE | CLONE_VM |
! 			/* CLONE_THREAD | */ /* gives EINVAL */
! 			SIGCHLD,
! 			&fd);
! 	/* pthread_attr_init(&thread_attr);
! 	cloneid = pthread_create(&thread,
! 	*/
! 	if (cloneid < 0) {
! 	  perror("capture_go: clone");
! 	  exit(1);
! 	}
! #if 0
! 	d_printf("cloneid = %d\n", cloneid);
! #endif
  	do {
  		for (cur = count; cur > 0; cur -= r) {
  			c = cur;
  			if (c > chunk_bytes)
  				c = chunk_bytes;
+ 			/* Limit to audiobuf extent... */
+ 			if (audiobuf_writep + c > audiobuf + audiobuf_bytes)
+ 			  c = (audiobuf + audiobuf_bytes - audiobuf_writep) ;
  			c = c * 8 / bits_per_frame;
! 			if (c == 0) {
! 			  struct timespec howlong = {0, 100000000};
! #if 0
! 			  d_printf ("No room! Waiting...\n");
! #endif
! 			  r = 0;
! 			  nanosleep(&howlong, NULL); /* 1/10 sec. */
! 			  continue;
! 			}
! 			if (audiobuf_writep < audiobuf_readp  &&
! 			    audiobuf_writep + c >= audiobuf_readp )
! 			  c = (audiobuf_readp - audiobuf_writep) * 8 / bits_per_frame;
! #if 0
! 			d_printf("Calling pcm_read for %d frames at 0x%x\n",
! 				c, audiobuf_writep);
! #endif
! 			if ((size_t)(r = pcm_read(audiobuf_writep, c)) != c)
  				break;
  			r = r * bits_per_frame / 8;
+ #if 0
+ 			d_printf("Read %d (0x%x) PCM bytes out of %d requested at 0x%x\n",
+ 				r, r, c * bits_per_frame / 8, audiobuf_writep);
+ #endif
+ 			if (audiobuf_writep + r >= audiobuf + audiobuf_bytes)
+ 			  audiobuf_writep = audiobuf;
+ 			else
+ 			  audiobuf_writep += r;
+ 			/*
  			if ((err = write(fd, audiobuf, r)) != r) {
  				perror(name);
  				exit(EXIT_FAILURE);
  			}
  			if (err > 0)
  				fdcount += err;
+ 			*/
  		}
  	} while (rtype == FORMAT_RAW && !timelimit);
+ 	capture_done = 1;	/* tells writeloop thread to finish & exit */
+ 	waitpid(cloneid, NULL, __WALL);	/* Hold on until it does so */
+ #if 0
+ 	d_printf("capture_go: Writeloop thread %d exited; returning\n",
+ 		cloneid);
+ #endif
  }
  
  /*

                 reply	other threads:[~2004-03-06  0:55 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=40492198.3000309@sigma.net \
    --to=weav@sigma.net \
    --cc=Alsa-devel@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.