* Buffering Patch to aplay / arecord 1.0.2
@ 2004-03-06 0:55 Eric C. Weaver
0 siblings, 0 replies; only message in thread
From: Eric C. Weaver @ 2004-03-06 0:55 UTC (permalink / raw)
To: Alsa-devel
[-- 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
}
/*
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2004-03-06 0:55 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-03-06 0:55 Buffering Patch to aplay / arecord 1.0.2 Eric C. Weaver
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.