Blame src/libout123/buffer.c

Packit c32a2d
/*
Packit c32a2d
	buffer.c: output buffer
Packit c32a2d
Packit c32a2d
	copyright 1997-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
Packit c32a2d
	see COPYING and AUTHORS files in distribution or http://mpg123.org
Packit c32a2d
	initially written by Oliver Fromme
Packit c32a2d
Packit c32a2d
	I (ThOr) am reviewing this file at about the same daytime as Oliver's timestamp here:
Packit c32a2d
	Mon Apr 14 03:53:18 MET DST 1997
Packit c32a2d
	- dammed night coders;-)
Packit c32a2d
Packit c32a2d
	This has been heavily reworked to be barely recognizable for the creation of
Packit c32a2d
	libout123. There is more structure in the communication, as is necessary if
Packit c32a2d
	the libout123 functionality is offered via some API to unknown client
Packit c32a2d
	programs instead of being used from mpg123 alone. The basic idea is the same,
Packit c32a2d
	the xfermem part only sligthly modified for more synchronization, as I sensed
Packit c32a2d
	potential deadlocks. --ThOr
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
	Communication to the buffer is normally via xfermem_putcmd() and blocking
Packit c32a2d
	on a response, relying on the buffer process periodically checking for
Packit c32a2d
	pending commands.
Packit c32a2d
Packit c32a2d
	For more immediate concerns, you can send SIGINT. The only result is that this
Packit c32a2d
	interrupts a current device writing operation and causes the buffer to wait
Packit c32a2d
	for a following command.
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
/* Needed for kill() from signal.h. */
Packit c32a2d
#define _POSIX_SOURCE
Packit c32a2d
Packit c32a2d
#include "buffer.h"
Packit c32a2d
#include "out123_int.h"
Packit c32a2d
#include "xfermem.h"
Packit c32a2d
#include <errno.h>
Packit c32a2d
#include "debug.h"
Packit c32a2d
#ifdef HAVE_SYS_WAIT_H
Packit c32a2d
#include <sys/wait.h>
Packit c32a2d
#endif
Packit c32a2d
#ifdef HAVE_SIGNAL_H
Packit c32a2d
#include <signal.h>
Packit c32a2d
#else
Packit c32a2d
#ifdef HAVE_SYS_SIGNAL_H
Packit c32a2d
#include <sys/signal.h>
Packit c32a2d
#endif
Packit c32a2d
#endif
Packit c32a2d
Packit c32a2d
Packit c32a2d
#define BUF_CMD_OPEN     XF_CMD_CUSTOM1
Packit c32a2d
#define BUF_CMD_CLOSE    XF_CMD_CUSTOM2
Packit c32a2d
#define BUF_CMD_START    XF_CMD_CUSTOM3
Packit c32a2d
#define BUF_CMD_STOP     XF_CMD_CUSTOM4
Packit c32a2d
#define BUF_CMD_AUDIOCAP XF_CMD_CUSTOM5
Packit c32a2d
#define BUF_CMD_PARAM    XF_CMD_CUSTOM6
Packit c32a2d
#define BUF_CMD_NDRAIN   XF_CMD_CUSTOM7
Packit c32a2d
#define BUF_CMD_AUDIOFMT XF_CMD_CUSTOM8
Packit c32a2d
Packit c32a2d
/* TODO: Dynamically allocate that to allow multiple instances. */
Packit c32a2d
int outburst = 32768;
Packit c32a2d
Packit c32a2d
/* This is static and global for the forked buffer process.
Packit c32a2d
   Another forked buffer process will have its on value. */
Packit c32a2d
static int intflag = FALSE;
Packit c32a2d
Packit c32a2d
static void catch_interrupt (void)
Packit c32a2d
{
Packit c32a2d
	intflag = TRUE;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static int read_record(out123_handle *ao
Packit c32a2d
,	int who, void **buf, byte *prebuf, int *preoff, int presize, size_t *recsize);
Packit c32a2d
static int buffer_loop(out123_handle *ao);
Packit c32a2d
Packit c32a2d
static void catch_child(void)
Packit c32a2d
{
Packit c32a2d
	/* Disabled for now. We do not really need that.
Packit c32a2d
	   Rather get return status in a controlled way in buffer_exit(). */
Packit c32a2d
	/* while (waitpid(-1, NULL, WNOHANG) > 0); */
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
	Functions called from the controlling process.
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
/* Start a buffer process. */
Packit c32a2d
int buffer_init(out123_handle *ao, size_t bytes)
Packit c32a2d
{
Packit c32a2d
	buffer_exit(ao);
Packit c32a2d
	if(bytes < outburst) bytes = 2*outburst;
Packit c32a2d
Packit c32a2d
#ifdef DONT_CATCH_SIGNALS
Packit c32a2d
#error I really need to catch signals here!
Packit c32a2d
#endif
Packit c32a2d
	xfermem_init(&ao->buffermem, bytes, 0, 0);
Packit c32a2d
	/* Is catch_child() really useful? buffer_exit() does waitpid().
Packit c32a2d
	   And if buffer_exit() is not called, the main process might be
Packit c32a2d
	   killed off and not be able to run a signal handler anyway. */
Packit c32a2d
	catchsignal(SIGCHLD, catch_child);
Packit c32a2d
	switch((ao->buffer_pid = fork()))
Packit c32a2d
	{
Packit c32a2d
		case -1: /* error */
Packit c32a2d
			if(!AOQUIET)
Packit c32a2d
				error("cannot fork!");
Packit c32a2d
			goto buffer_init_bad;
Packit c32a2d
		case 0: /* child */
Packit c32a2d
		{
Packit c32a2d
			int ret;
Packit c32a2d
			/*
Packit c32a2d
				Ensure the normal default value for buffer_pid to be able
Packit c32a2d
				to call normal out123 routines from the buffer proess.
Packit c32a2d
				One could keep it at zero and even use this for detecting the
Packit c32a2d
				buffer process and do special stuff for that. But the point
Packit c32a2d
				is that there shouldn't be special stuff.
Packit c32a2d
			*/
Packit c32a2d
			ao->buffer_pid = -1;
Packit c32a2d
			/* Not preparing audio output anymore, that comes later. */
Packit c32a2d
			xfermem_init_reader(ao->buffermem);
Packit c32a2d
			ret = buffer_loop(ao); /* Here the work happens. */
Packit c32a2d
			xfermem_done_reader(ao->buffermem);
Packit c32a2d
			xfermem_done(ao->buffermem);
Packit c32a2d
			/* Proper cleanup of output handle, including out123_close(). */
Packit c32a2d
			out123_del(ao);
Packit c32a2d
			exit(ret);
Packit c32a2d
		}
Packit c32a2d
		default: /* parent */
Packit c32a2d
		{
Packit c32a2d
			int cmd;
Packit c32a2d
			xfermem_init_writer(ao->buffermem);
Packit c32a2d
			debug("waiting for inital pong from buffer process");
Packit c32a2d
			if( (cmd=xfermem_getcmd(ao->buffermem->fd[XF_WRITER], TRUE))
Packit c32a2d
			    != XF_CMD_PONG )
Packit c32a2d
			{
Packit c32a2d
				if(!AOQUIET)
Packit c32a2d
					error2("Got %i instead of expected initial response %i. Killing rogue buffer process."
Packit c32a2d
					,	cmd, XF_CMD_PONG);
Packit c32a2d
				kill(ao->buffer_pid, SIGKILL);
Packit c32a2d
				buffer_exit(ao);
Packit c32a2d
				return -1;
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	return 0;
Packit c32a2d
buffer_init_bad:
Packit c32a2d
	if(ao->buffermem)
Packit c32a2d
	{
Packit c32a2d
		xfermem_done(ao->buffermem);
Packit c32a2d
		ao->buffermem = NULL;
Packit c32a2d
	}
Packit c32a2d
	return -1;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* End a buffer process. */
Packit c32a2d
void buffer_exit(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	int status = 0;
Packit c32a2d
	if(ao->buffer_pid == -1) return;
Packit c32a2d
Packit c32a2d
	debug("ending buffer");
Packit c32a2d
	buffer_stop(ao); /* Puts buffer into waiting-for-command mode. */
Packit c32a2d
	buffer_end(ao);  /* Gives command to end operation. */
Packit c32a2d
	xfermem_done_writer(ao->buffermem);
Packit c32a2d
	waitpid(ao->buffer_pid, &status, 0);
Packit c32a2d
	xfermem_done(ao->buffermem);
Packit c32a2d
	ao->buffermem = NULL;
Packit c32a2d
	ao->buffer_pid = -1;
Packit c32a2d
	if(WIFEXITED(status))
Packit c32a2d
	{
Packit c32a2d
		int ret = WEXITSTATUS(status);
Packit c32a2d
		if(ret && !AOQUIET)
Packit c32a2d
			error1("Buffer process isses arose, non-zero return value %i.", ret);
Packit c32a2d
	}
Packit c32a2d
	else if(!AOQUIET)
Packit c32a2d
		error("Buffer process did not exit normally.");
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
	Communication from writer to reader (buffer process).
Packit c32a2d
	Remember: The ao struct here is the writer's instance.
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
static int buffer_cmd_finish(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	/* Only if buffer returns XF_CMD_OK we got lucky. Otherwise, we expect
Packit c32a2d
	   the buffer to deliver a reason right after XF_CMD_ERROR. */
Packit c32a2d
	switch(xfermem_getcmd(ao->buffermem->fd[XF_WRITER], TRUE))
Packit c32a2d
	{
Packit c32a2d
		case XF_CMD_OK: return 0;
Packit c32a2d
		case XF_CMD_ERROR:
Packit c32a2d
			if(!GOOD_READVAL(ao->buffermem->fd[XF_WRITER], ao->errcode))
Packit c32a2d
				ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
			return -1;
Packit c32a2d
		break;
Packit c32a2d
		default:
Packit c32a2d
			ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
			return -1;
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int buffer_sync_param(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	int writerfd = ao->buffermem->fd[XF_WRITER];
Packit c32a2d
	if(xfermem_putcmd(writerfd, BUF_CMD_PARAM) != 1)
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
	/* Calling an external serialization routine to avoid forgetting
Packit c32a2d
	   any fresh parameters here. */
Packit c32a2d
	if(write_parameters(ao, XF_WRITER))
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
	return buffer_cmd_finish(ao);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int buffer_open(out123_handle *ao, const char* driver, const char* device)
Packit c32a2d
{
Packit c32a2d
	int writerfd = ao->buffermem->fd[XF_WRITER];
Packit c32a2d
Packit c32a2d
	if(xfermem_putcmd(writerfd, BUF_CMD_OPEN) != 1)
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
	/* Passing over driver and device name. */
Packit c32a2d
	if(  xfer_write_string(ao, XF_WRITER, driver)
Packit c32a2d
	  || xfer_write_string(ao, XF_WRITER, device) )
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(buffer_cmd_finish(ao) == 0)
Packit c32a2d
		/* Retrieve driver and device name. */
Packit c32a2d
		return ( xfer_read_string(ao, XF_WRITER, &ao->driver)
Packit c32a2d
		      || xfer_read_string(ao, XF_WRITER, &ao->device)
Packit c32a2d
		      || xfer_read_string(ao, XF_WRITER, &ao->realname) );
Packit c32a2d
	else
Packit c32a2d
		return -1;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int buffer_encodings(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	int writerfd = ao->buffermem->fd[XF_WRITER];
Packit c32a2d
Packit c32a2d
	if(xfermem_putcmd(writerfd, BUF_CMD_AUDIOCAP) != 1)
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
	/* Now shoving over the parameters for opening the device. */
Packit c32a2d
	if(
Packit c32a2d
		!GOOD_WRITEVAL(writerfd, ao->channels)
Packit c32a2d
	||	!GOOD_WRITEVAL(writerfd, ao->rate)
Packit c32a2d
	)
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(buffer_cmd_finish(ao) == 0)
Packit c32a2d
	{
Packit c32a2d
		int encodings;
Packit c32a2d
		/* If all good, the answer can be read how. */
Packit c32a2d
		if(!GOOD_READVAL(writerfd, encodings))
Packit c32a2d
		{
Packit c32a2d
			ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
			return -1;
Packit c32a2d
		}
Packit c32a2d
		else return encodings;
Packit c32a2d
	}
Packit c32a2d
	else return -1;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int buffer_formats( out123_handle *ao, const long *rates, int ratecount
Packit c32a2d
                  , int minchannels, int maxchannels
Packit c32a2d
                  , struct mpg123_fmt **fmtlist )
Packit c32a2d
{
Packit c32a2d
	int writerfd = ao->buffermem->fd[XF_WRITER];
Packit c32a2d
	size_t ratesize;
Packit c32a2d
Packit c32a2d
	debug("buffer_formats");
Packit c32a2d
Packit c32a2d
	if(xfermem_putcmd(writerfd, BUF_CMD_AUDIOFMT) != 1)
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	ratesize = ratecount*sizeof(rates);
Packit c32a2d
Packit c32a2d
	if(
Packit c32a2d
		!GOOD_WRITEVAL(writerfd, maxchannels)
Packit c32a2d
	||	!GOOD_WRITEVAL(writerfd, minchannels)
Packit c32a2d
	||	!GOOD_WRITEVAL(writerfd, ratesize)
Packit c32a2d
	||	!GOOD_WRITEBUF(writerfd, rates, ratesize)
Packit c32a2d
	){
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
	if(buffer_cmd_finish(ao) == 0)
Packit c32a2d
	{
Packit c32a2d
		int fmtcount;
Packit c32a2d
		size_t fmtsize;
Packit c32a2d
		if(
Packit c32a2d
			!GOOD_READVAL(writerfd, fmtcount)
Packit c32a2d
		||	read_record(ao, XF_WRITER, (void**)fmtlist, NULL, NULL, 0, &fmtsize)
Packit c32a2d
		){
Packit c32a2d
			ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
			return -1;
Packit c32a2d
		} else
Packit c32a2d
			return fmtsize/sizeof(struct mpg123_fmt);
Packit c32a2d
	}
Packit c32a2d
	else return -1;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int buffer_start(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	int writerfd = ao->buffermem->fd[XF_WRITER];
Packit c32a2d
	if(xfermem_putcmd(writerfd, BUF_CMD_START) != 1)
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
	/* Now shoving over the parameters for opening the device. */
Packit c32a2d
	if(
Packit c32a2d
		!GOOD_WRITEVAL(writerfd, ao->format)
Packit c32a2d
	||	!GOOD_WRITEVAL(writerfd, ao->channels)
Packit c32a2d
	|| !GOOD_WRITEVAL(writerfd, ao->rate)
Packit c32a2d
	)
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	return buffer_cmd_finish(ao);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
#define BUFFER_SIMPLE_CONTROL(name, cmd) \
Packit c32a2d
void name(out123_handle *ao) \
Packit c32a2d
{ \
Packit c32a2d
	xfermem_putcmd(ao->buffermem->fd[XF_WRITER], cmd); \
Packit c32a2d
	xfermem_getcmd(ao->buffermem->fd[XF_WRITER], TRUE); \
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
BUFFER_SIMPLE_CONTROL(buffer_stop,  BUF_CMD_STOP)
Packit c32a2d
BUFFER_SIMPLE_CONTROL(buffer_continue, XF_CMD_CONTINUE)
Packit c32a2d
BUFFER_SIMPLE_CONTROL(buffer_ignore_lowmem, XF_CMD_IGNLOW)
Packit c32a2d
BUFFER_SIMPLE_CONTROL(buffer_drain, XF_CMD_DRAIN)
Packit c32a2d
BUFFER_SIMPLE_CONTROL(buffer_end, XF_CMD_TERMINATE)
Packit c32a2d
BUFFER_SIMPLE_CONTROL(buffer_close, BUF_CMD_CLOSE)
Packit c32a2d
Packit c32a2d
#define BUFFER_SIGNAL_CONTROL(name, cmd) \
Packit c32a2d
void name(out123_handle *ao) \
Packit c32a2d
{ \
Packit c32a2d
	kill(ao->buffer_pid, SIGINT); \
Packit c32a2d
	xfermem_putcmd(ao->buffermem->fd[XF_WRITER], cmd); \
Packit c32a2d
	xfermem_getcmd(ao->buffermem->fd[XF_WRITER], TRUE); \
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
BUFFER_SIGNAL_CONTROL(buffer_pause, XF_CMD_PAUSE)
Packit c32a2d
BUFFER_SIGNAL_CONTROL(buffer_drop, XF_CMD_DROP)
Packit c32a2d
Packit c32a2d
size_t buffer_fill(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	return xfermem_get_usedspace(ao->buffermem);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void buffer_ndrain(out123_handle *ao, size_t bytes)
Packit c32a2d
{
Packit c32a2d
	size_t oldfill;
Packit c32a2d
	int writerfd = ao->buffermem->fd[XF_WRITER];
Packit c32a2d
Packit c32a2d
	oldfill = buffer_fill(ao);
Packit c32a2d
	if(xfermem_putcmd(writerfd, BUF_CMD_NDRAIN) != 1)
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return;
Packit c32a2d
	}
Packit c32a2d
	/* Now shoving over the parameters for opening the device. */
Packit c32a2d
	if(  !GOOD_WRITEVAL(writerfd, bytes)
Packit c32a2d
	  || !GOOD_WRITEVAL(writerfd, oldfill) )
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	buffer_cmd_finish(ao);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* The workhorse: Send data to the buffer with some synchronization and even
Packit c32a2d
   error checking. */
Packit c32a2d
size_t buffer_write(out123_handle *ao, void *buffer, size_t bytes)
Packit c32a2d
{
Packit c32a2d
	/*
Packit c32a2d
		Writing the whole buffer in one piece is no good as that means
Packit c32a2d
		waiting for the buffer being empty. That is called a buffer underrun.
Packit c32a2d
		We want to refill the buffer before that happens. So, what is sane?
Packit c32a2d
	*/
Packit c32a2d
	size_t written = 0;
Packit c32a2d
	size_t max_piece = ao->buffermem->size / 2;
Packit c32a2d
	while(bytes)
Packit c32a2d
	{
Packit c32a2d
		size_t count_piece = bytes > max_piece
Packit c32a2d
		?	max_piece
Packit c32a2d
		:	bytes;
Packit c32a2d
		int ret = xfermem_write(ao->buffermem
Packit c32a2d
		,	(char*)buffer+written, count_piece);
Packit c32a2d
		if(ret)
Packit c32a2d
		{
Packit c32a2d
			if(!AOQUIET)
Packit c32a2d
				error1("writing to buffer memory failed (%i)", ret);
Packit c32a2d
			if(ret == XF_CMD_ERROR)
Packit c32a2d
			{
Packit c32a2d
				/* Buffer tells me that it has an error waiting. */
Packit c32a2d
				if(!GOOD_READVAL(ao->buffermem->fd[XF_WRITER], ao->errcode))
Packit c32a2d
					ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
			}
Packit c32a2d
			return 0;
Packit c32a2d
		}
Packit c32a2d
		bytes   -= count_piece;
Packit c32a2d
		written += count_piece;
Packit c32a2d
	}
Packit c32a2d
	return written;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
	Code for the buffer process itself.
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
Packit c32a2d
buffer loop:
Packit c32a2d
Packit c32a2d
{
Packit c32a2d
	1. normal operation: get data, feed to audio device
Packit c32a2d
	   (if device open and alive, if data there, if no other command pending)
Packit c32a2d
	2. command response: pause/unpause, open module/device, query caps
Packit c32a2d
Packit c32a2d
	One command at a time, synchronized ... writer process blocks, waiting for
Packit c32a2d
	response.
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
	Fill buffer to that value when starting playback from stopped state or after
Packit c32a2d
	experiencing a serious underrun.
Packit c32a2d
	One might also define intermediate preload to recover from underruns. Earlier
Packit c32a2d
	code used 1/8 of the buffer.
Packit c32a2d
*/
Packit c32a2d
static size_t preload_size(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	size_t preload = 0;
Packit c32a2d
	txfermem *xf = ao->buffermem;
Packit c32a2d
	/* Fill configured part of buffer on first run before starting to play.
Packit c32a2d
	 * Live mp3 streams constantly approach buffer underrun otherwise. [dk]
Packit c32a2d
	 */
Packit c32a2d
	if(ao->preload > 0.)     preload = (size_t)(ao->preload*xf->size);
Packit c32a2d
	if(preload > xf->size/2) preload = xf->size/2;
Packit c32a2d
Packit c32a2d
	return preload;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Play one piece of audio from the buffer after settling preload etc.
Packit c32a2d
   On error, the device is closed and this naturally stops playback
Packit c32a2d
   as that depends on ao->state == play_live. 
Packit c32a2d
   This plays _at_ _most_ the given amount of bytes, usually less. */
Packit c32a2d
static void buffer_play(out123_handle *ao, size_t bytes)
Packit c32a2d
{
Packit c32a2d
	size_t written;
Packit c32a2d
	txfermem *xf = ao->buffermem;
Packit c32a2d
	/* Settle amount of bytes accessible in one block. */
Packit c32a2d
	if (bytes > xf->size - xf->readindex)
Packit c32a2d
		bytes = xf->size - xf->readindex;
Packit c32a2d
	/* Not more than configured output block. */
Packit c32a2d
	if (bytes > outburst)
Packit c32a2d
		bytes = outburst;
Packit c32a2d
	/* The output can only take multiples of framesize. */
Packit c32a2d
	bytes -= bytes % ao->framesize;
Packit c32a2d
	/* Actual work by out123_play to ensure logic like automatic continue. */
Packit c32a2d
	written = out123_play(ao, (unsigned char*)xf->data+xf->readindex, bytes);
Packit c32a2d
	/* Advance read pointer by the amount of written bytes. */
Packit c32a2d
	xf->readindex = (xf->readindex + written) % xf->size;
Packit c32a2d
	/* Detect a fatal error by proxy. */
Packit c32a2d
	if(ao->errcode == OUT123_DEV_PLAY)
Packit c32a2d
		out123_close(ao);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Now I'm getting really paranoid: Helper to skip bytes from command
Packit c32a2d
   channel if we cannot allocate enough memory to hold the data. */
Packit c32a2d
static void skip_bytes(int fd, size_t count)
Packit c32a2d
{
Packit c32a2d
	while(count)
Packit c32a2d
	{
Packit c32a2d
		char buf[1024];
Packit c32a2d
		if(!unintr_read(fd, buf, (count < sizeof(buf) ? count : sizeof(buf))))
Packit c32a2d
			return;
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Write a string to command channel.
Packit c32a2d
   Return 0 on success, set ao->errcode on issues. */
Packit c32a2d
int xfer_write_string(out123_handle *ao, int who, const char *buf)
Packit c32a2d
{
Packit c32a2d
	txfermem *xf = ao->buffermem;
Packit c32a2d
	int my_fd = xf->fd[who];
Packit c32a2d
	size_t len;
Packit c32a2d
Packit c32a2d
	/* A NULL string is passed als zero bytes. */
Packit c32a2d
	len = buf ? (strlen(buf)+1) : 0;
Packit c32a2d
	if( !GOOD_WRITEVAL(my_fd, len)
Packit c32a2d
	 || !GOOD_WRITEBUF(my_fd, buf, len) )
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
	return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
int xfer_read_string(out123_handle *ao, int who, char **buf)
Packit c32a2d
{
Packit c32a2d
	/* ao->errcode set in read_record() */
Packit c32a2d
	return read_record(ao, who, (void**)buf, NULL, NULL, 0, NULL)
Packit c32a2d
	? -1 /* read_record could return 2, normalize to -1 */
Packit c32a2d
	: 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Read a value from command channel with prebuffer.
Packit c32a2d
   This assumes responsible use and avoids needless checking of input.
Packit c32a2d
   And, yes, it modifies the preoff argument!
Packit c32a2d
   Returns 0 on success, modifies prebuffer fill. */
Packit c32a2d
int read_buf(int fd, void *addr, size_t size, byte *prebuf, int *preoff, int presize)
Packit c32a2d
{
Packit c32a2d
	size_t need = size;
Packit c32a2d
Packit c32a2d
	if(prebuf)
Packit c32a2d
	{
Packit c32a2d
		int have = presize - *preoff;
Packit c32a2d
		if(have > need)
Packit c32a2d
			have = need;
Packit c32a2d
		memcpy(addr, prebuf+*preoff, have);
Packit c32a2d
		*preoff += have;
Packit c32a2d
		addr = (char*)addr+have;
Packit c32a2d
		need -= have;
Packit c32a2d
	}
Packit c32a2d
	if(need)
Packit c32a2d
		return !GOOD_READBUF(fd, addr, need);
Packit c32a2d
	else
Packit c32a2d
		return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
/* Read a record of unspecified type from command channel.
Packit c32a2d
   Return 0 on success, set ao->errcode on issues. */
Packit c32a2d
static int read_record(out123_handle *ao
Packit c32a2d
,	int who, void **buf, byte *prebuf, int *preoff, int presize
Packit c32a2d
,	size_t *reclen)
Packit c32a2d
{
Packit c32a2d
	txfermem *xf = ao->buffermem;
Packit c32a2d
	int my_fd = xf->fd[who];
Packit c32a2d
	size_t len;
Packit c32a2d
Packit c32a2d
	if(*buf)
Packit c32a2d
		free(*buf);
Packit c32a2d
	*buf = NULL;
Packit c32a2d
Packit c32a2d
	if(read_buf(my_fd, &len, sizeof(len), prebuf, preoff, presize))
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		return 2;
Packit c32a2d
	}
Packit c32a2d
	if(reclen)
Packit c32a2d
		*reclen = len;
Packit c32a2d
	/* If there is an insane length of given, that shall be handled. */
Packit c32a2d
	if(len && !(*buf = malloc(len)))
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_DOOM;
Packit c32a2d
		skip_bytes(my_fd, len);
Packit c32a2d
		return -1;
Packit c32a2d
	}
Packit c32a2d
	if(read_buf(my_fd, *buf, len, prebuf, preoff, presize))
Packit c32a2d
	{
Packit c32a2d
		ao->errcode = OUT123_BUFFER_ERROR;
Packit c32a2d
		free(*buf);
Packit c32a2d
		*buf = NULL;
Packit c32a2d
		return 2;
Packit c32a2d
	}
Packit c32a2d
	return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
/* The main loop, returns 0 when no issue occured. */
Packit c32a2d
int buffer_loop(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	txfermem *xf = ao->buffermem;
Packit c32a2d
	int my_fd = xf->fd[XF_READER];
Packit c32a2d
	int preloading = FALSE;
Packit c32a2d
	int draining = FALSE;
Packit c32a2d
	/* The buffer loop maintains a playback state that can differ from
Packit c32a2d
	   the underlying device's. During prebuffering, the device is paused,
Packit c32a2d
	   but we are playing (as soon as enough data is there, the device is,
Packit c32a2d
	   too). */
Packit c32a2d
	enum playstate mystate = ao->state;
Packit c32a2d
Packit c32a2d
	ao->flags &= ~OUT123_KEEP_PLAYING; /* No need for that here. */
Packit c32a2d
	/* Be prepared to use SIGINT for communication. */
Packit c32a2d
	catchsignal (SIGINT, catch_interrupt);
Packit c32a2d
	/* sigprocmask (SIG_SETMASK, oldsigset, NULL); */
Packit c32a2d
	/* Say hello to the writer. */
Packit c32a2d
	xfermem_putcmd(my_fd, XF_CMD_PONG);
Packit c32a2d
Packit c32a2d
	debug1("buffer with preload %g", ao->preload);
Packit c32a2d
	while(1)
Packit c32a2d
	{
Packit c32a2d
		/* If a device is opened and playing, it is our first duty to keep it playing. */
Packit c32a2d
		if(mystate == play_live)
Packit c32a2d
		{
Packit c32a2d
			size_t bytes = xfermem_get_usedspace(xf);
Packit c32a2d
			debug4( "Play or preload? Got %"SIZE_P" B / %"SIZE_P" B (%i,%i)."
Packit c32a2d
			,	(size_p)bytes, (size_p)preload_size(ao), preloading, draining );
Packit c32a2d
			if(preloading)
Packit c32a2d
				preloading = (bytes < preload_size(ao));
Packit c32a2d
			if(!preloading)
Packit c32a2d
			{
Packit c32a2d
				if(!draining && bytes < outburst)
Packit c32a2d
					preloading = TRUE;
Packit c32a2d
				else
Packit c32a2d
				{
Packit c32a2d
					buffer_play(ao, bytes);
Packit c32a2d
					mystate = ao->state; /* Maybe changed, must be in sync now. */
Packit c32a2d
				}
Packit c32a2d
			}
Packit c32a2d
			/* Be nice and pause the device on preloading. */
Packit c32a2d
			if(preloading && ao->state == play_live)
Packit c32a2d
				out123_pause(ao);
Packit c32a2d
		}
Packit c32a2d
		/* Now always check for a pending command, in a blocking way if there is
Packit c32a2d
		   no playback. */
Packit c32a2d
		debug2("Buffer cmd? (Interruped: %i) (mystate=%i)", intflag, (int)mystate);
Packit c32a2d
		/*
Packit c32a2d
			The writer only ever signals before sending a command and also waiting
Packit c32a2d
			for a response. So, the right place to reset the flag is any time
Packit c32a2d
			before giving the response. But let's ensure two things:
Packit c32a2d
			1. The flag really is only cleared when a command response is given.
Packit c32a2d
			2. Command parsing does not stop until a command demanding a response
Packit c32a2d
			   was handled.
Packit c32a2d
		*/
Packit c32a2d
		do
Packit c32a2d
		{
Packit c32a2d
			/* Getting a whole block of commands to efficiently process those
Packit c32a2d
			   XF_CMD_DATA messages. */
Packit c32a2d
			byte cmd[100];
Packit c32a2d
			int cmdcount;
Packit c32a2d
			int i;
Packit c32a2d
Packit c32a2d
			cmdcount = xfermem_getcmds( my_fd
Packit c32a2d
			,	(preloading || intflag || (mystate != play_live))
Packit c32a2d
			,	cmd
Packit c32a2d
			,	sizeof(cmd) );
Packit c32a2d
			if(cmdcount < 0)
Packit c32a2d
			{
Packit c32a2d
				if(!AOQUIET)
Packit c32a2d
					error1("Reading a command set returned %i, my link is broken.", cmdcount);
Packit c32a2d
				return 1;
Packit c32a2d
			}
Packit c32a2d
#ifdef DEBUG
Packit c32a2d
			for(i=0; i
Packit c32a2d
				debug2("cmd[%i]=%u", i, cmd[i]);
Packit c32a2d
#endif
Packit c32a2d
			/*
Packit c32a2d
				These actions should rely heavily on calling the normal out123
Packit c32a2d
				API functions, just with some parameter passing and error checking
Packit c32a2d
				wrapped around. If there is much code here, it is wrong.
Packit c32a2d
			*/
Packit c32a2d
			for(i=0; i
Packit c32a2d
			{
Packit c32a2d
#define GOOD_READVAL_BUF(fd, val) \
Packit c32a2d
	!read_buf(my_fd, &val, sizeof(val), cmd, &i, cmdcount)
Packit c32a2d
				case XF_CMD_DATA:
Packit c32a2d
					debug("got new data");
Packit c32a2d
					/* Other states should not happen. */
Packit c32a2d
					if(mystate == play_paused)
Packit c32a2d
						mystate = play_live;
Packit c32a2d
					/* When new data arrives, we are obviously not draining. */
Packit c32a2d
					draining = FALSE;
Packit c32a2d
				break;
Packit c32a2d
				case XF_CMD_PING:
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					/* Expecting ping-pong only while playing! Otherwise, the writer
Packit c32a2d
					   could get stuck waiting for free space forever. */
Packit c32a2d
					if(mystate == play_live)
Packit c32a2d
						xfermem_putcmd(my_fd, XF_CMD_PONG);
Packit c32a2d
					else
Packit c32a2d
					{
Packit c32a2d
						xfermem_putcmd(my_fd, XF_CMD_ERROR);
Packit c32a2d
						if(ao->errcode == OUT123_OK)
Packit c32a2d
							ao->errcode = OUT123_NOT_LIVE;
Packit c32a2d
						if(!GOOD_WRITEVAL(my_fd, ao->errcode))
Packit c32a2d
							return 2;
Packit c32a2d
					}
Packit c32a2d
				break;
Packit c32a2d
				case BUF_CMD_PARAM:
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					/* If that does not work, communication is broken anyway and
Packit c32a2d
					   writer will notice soon enough. */
Packit c32a2d
					read_parameters(ao, XF_READER, cmd, &i, cmdcount);
Packit c32a2d
					ao->flags &= ~OUT123_KEEP_PLAYING; /* No need for that here. */
Packit c32a2d
					xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
				break;
Packit c32a2d
				case BUF_CMD_OPEN:
Packit c32a2d
				{
Packit c32a2d
					char *driver  = NULL;
Packit c32a2d
					char *device  = NULL;
Packit c32a2d
					int success;
Packit c32a2d
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					success = (
Packit c32a2d
						!read_record( ao, XF_READER, (void**)&driver
Packit c32a2d
						,	cmd, &i, cmdcount, NULL )
Packit c32a2d
					&&	!read_record( ao, XF_READER, (void**)&device
Packit c32a2d
						,	cmd, &i, cmdcount, NULL )
Packit c32a2d
					&&	!out123_open(ao, driver, device)
Packit c32a2d
					);
Packit c32a2d
					free(device);
Packit c32a2d
					free(driver);
Packit c32a2d
					draining = FALSE;
Packit c32a2d
					mystate = ao->state;
Packit c32a2d
					if(success)
Packit c32a2d
					{
Packit c32a2d
						xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
						if(  xfer_write_string(ao, XF_READER, ao->driver)
Packit c32a2d
						  || xfer_write_string(ao, XF_READER, ao->device)
Packit c32a2d
						  || xfer_write_string(ao, XF_READER, ao->realname ) )
Packit c32a2d
							return 2;
Packit c32a2d
					}
Packit c32a2d
					else
Packit c32a2d
					{
Packit c32a2d
						xfermem_putcmd(my_fd, XF_CMD_ERROR);
Packit c32a2d
						/* Again, no sense to bitch around about communication errors,
Packit c32a2d
						   just quit. */
Packit c32a2d
						if(!GOOD_WRITEVAL(my_fd, ao->errcode))
Packit c32a2d
							return 2;
Packit c32a2d
					}
Packit c32a2d
				}
Packit c32a2d
				break;
Packit c32a2d
				case BUF_CMD_CLOSE:
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					out123_close(ao);
Packit c32a2d
					draining = FALSE;
Packit c32a2d
					mystate = ao->state;
Packit c32a2d
					xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
				break;
Packit c32a2d
				case BUF_CMD_AUDIOCAP:
Packit c32a2d
				{
Packit c32a2d
					int encodings;
Packit c32a2d
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					if(
Packit c32a2d
						!GOOD_READVAL_BUF(my_fd, ao->channels)
Packit c32a2d
					||	!GOOD_READVAL_BUF(my_fd, ao->rate)
Packit c32a2d
					)
Packit c32a2d
						return 2;
Packit c32a2d
					encodings = out123_encodings(ao, ao->rate, ao->channels);
Packit c32a2d
					mystate = ao->state;
Packit c32a2d
					if(encodings >= 0)
Packit c32a2d
					{
Packit c32a2d
						xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
						if(!GOOD_WRITEVAL(my_fd, encodings))
Packit c32a2d
							return 2;
Packit c32a2d
					}
Packit c32a2d
					else
Packit c32a2d
					{
Packit c32a2d
						xfermem_putcmd(my_fd, XF_CMD_ERROR);
Packit c32a2d
						if(!GOOD_WRITEVAL(my_fd, ao->errcode))
Packit c32a2d
							return 2;
Packit c32a2d
					}
Packit c32a2d
				}
Packit c32a2d
				break;
Packit c32a2d
				case BUF_CMD_AUDIOFMT:
Packit c32a2d
				{
Packit c32a2d
					size_t blocksize;
Packit c32a2d
					long *rates = NULL;
Packit c32a2d
					int minchannels;
Packit c32a2d
					int maxchannels;
Packit c32a2d
					struct mpg123_fmt *fmtlist;
Packit c32a2d
					int fmtcount = -1;
Packit c32a2d
Packit c32a2d
					if(
Packit c32a2d
						!GOOD_READVAL_BUF(my_fd, maxchannels)
Packit c32a2d
					||	!GOOD_READVAL_BUF(my_fd, minchannels)
Packit c32a2d
					)
Packit c32a2d
						return 2;
Packit c32a2d
					if(
Packit c32a2d
						read_record( ao, XF_READER, (void**)&rates
Packit c32a2d
						,	cmd, &i, cmdcount, &blocksize )
Packit c32a2d
					){
Packit c32a2d
						xfermem_putcmd(my_fd, XF_CMD_ERROR);
Packit c32a2d
						if(!GOOD_WRITEVAL(my_fd, ao->errcode))
Packit c32a2d
							return 2;
Packit c32a2d
					}
Packit c32a2d
					fmtcount = out123_formats( ao, rates
Packit c32a2d
					,	(int)(blocksize/sizeof(*rates))
Packit c32a2d
					,	minchannels, maxchannels, &fmtlist );
Packit c32a2d
					mystate = ao->state;
Packit c32a2d
					free(rates);
Packit c32a2d
					if(fmtcount >= 0)
Packit c32a2d
					{
Packit c32a2d
						int success;
Packit c32a2d
Packit c32a2d
						blocksize = sizeof(*fmtlist)*fmtcount;
Packit c32a2d
						debug2("responding with %i formats (block: %"SIZE_P")"
Packit c32a2d
						, fmtcount, (size_p)blocksize);
Packit c32a2d
						xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
						success =
Packit c32a2d
							GOOD_WRITEVAL(my_fd, fmtcount)
Packit c32a2d
						&&	GOOD_WRITEVAL(my_fd, blocksize)
Packit c32a2d
						&&	GOOD_WRITEBUF(my_fd, fmtlist, blocksize);
Packit c32a2d
						free(fmtlist);
Packit c32a2d
						if(!success)
Packit c32a2d
							return 2;
Packit c32a2d
					} else
Packit c32a2d
					{
Packit c32a2d
						xfermem_putcmd(my_fd, XF_CMD_ERROR);
Packit c32a2d
						if(!GOOD_WRITEVAL(my_fd, ao->errcode))
Packit c32a2d
							return 2;
Packit c32a2d
					}
Packit c32a2d
				}
Packit c32a2d
				break;
Packit c32a2d
				case BUF_CMD_START:
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					draining = FALSE;
Packit c32a2d
					if(
Packit c32a2d
						!GOOD_READVAL_BUF(my_fd, ao->format)
Packit c32a2d
					||	!GOOD_READVAL_BUF(my_fd, ao->channels)
Packit c32a2d
					||	!GOOD_READVAL_BUF(my_fd, ao->rate)
Packit c32a2d
					)
Packit c32a2d
						return 2;
Packit c32a2d
					if(!out123_start(ao, ao->rate, ao->channels, ao->format))
Packit c32a2d
					{
Packit c32a2d
						out123_pause(ao); /* Be nice, start only on buffer_play(). */
Packit c32a2d
						mystate = play_live;
Packit c32a2d
						preloading = TRUE;
Packit c32a2d
						xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
					}
Packit c32a2d
					else
Packit c32a2d
					{
Packit c32a2d
						mystate = ao->state;
Packit c32a2d
						xfermem_putcmd(my_fd, XF_CMD_ERROR);
Packit c32a2d
						if(!GOOD_WRITEVAL(my_fd, ao->errcode))
Packit c32a2d
							return 2;
Packit c32a2d
					}
Packit c32a2d
				break;
Packit c32a2d
				case BUF_CMD_STOP:
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					if(mystate == play_live)
Packit c32a2d
					{ /* Drain is implied! */
Packit c32a2d
						size_t bytes;
Packit c32a2d
						while((bytes = xfermem_get_usedspace(xf)))
Packit c32a2d
							buffer_play(ao, bytes);
Packit c32a2d
					}
Packit c32a2d
					out123_stop(ao);
Packit c32a2d
					draining = FALSE;
Packit c32a2d
					mystate = ao->state;
Packit c32a2d
					xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
				break;
Packit c32a2d
				case XF_CMD_CONTINUE:
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					debug("continuing");
Packit c32a2d
					mystate = play_live; /* We'll get errors reported later if that is not right. */
Packit c32a2d
					preloading = FALSE; /* It should continue without delay. */
Packit c32a2d
					draining = FALSE; /* But outburst should be cared for. */
Packit c32a2d
					xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
				break;
Packit c32a2d
				case XF_CMD_IGNLOW:
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					preloading = FALSE;
Packit c32a2d
					xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
				break;
Packit c32a2d
				case XF_CMD_DRAIN:
Packit c32a2d
					debug("buffer drain");
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					if(mystate == play_live)
Packit c32a2d
					{
Packit c32a2d
						size_t bytes;
Packit c32a2d
						while(
Packit c32a2d
							(bytes = xfermem_get_usedspace(xf))
Packit c32a2d
						&&	bytes > ao->framesize
Packit c32a2d
						)
Packit c32a2d
							buffer_play(ao, bytes);
Packit c32a2d
						out123_drain(ao);
Packit c32a2d
						mystate = ao->state;
Packit c32a2d
					}
Packit c32a2d
					draining = FALSE;
Packit c32a2d
					xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
				break;
Packit c32a2d
				case BUF_CMD_NDRAIN:
Packit c32a2d
				{
Packit c32a2d
					size_t limit;
Packit c32a2d
					size_t oldfill;
Packit c32a2d
Packit c32a2d
					debug("buffer ndrain");
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					/* Expect further calls to ndrain, avoid prebuffering. */
Packit c32a2d
					draining = TRUE;
Packit c32a2d
					preloading = FALSE;
Packit c32a2d
					if(
Packit c32a2d
						!GOOD_READVAL_BUF(my_fd, limit)
Packit c32a2d
					||	!GOOD_READVAL_BUF(my_fd, oldfill)
Packit c32a2d
					)
Packit c32a2d
						return 2;
Packit c32a2d
					if(mystate == play_live)
Packit c32a2d
					{
Packit c32a2d
						size_t bytes;
Packit c32a2d
						while(
Packit c32a2d
							(bytes = xfermem_get_usedspace(xf))
Packit c32a2d
						&&	bytes > ao->framesize
Packit c32a2d
						&&	oldfill >= bytes /* paranoia, overflow would handle it anyway */
Packit c32a2d
						&&	(oldfill-bytes) < limit
Packit c32a2d
						)
Packit c32a2d
							buffer_play(ao, bytes > limit ? limit : bytes);
Packit c32a2d
						/* Only drain hardware if the end was reached. */
Packit c32a2d
						if(!xfermem_get_usedspace(xf))
Packit c32a2d
						{
Packit c32a2d
							out123_drain(ao);
Packit c32a2d
							mystate = ao->state;
Packit c32a2d
							draining = FALSE;
Packit c32a2d
						}
Packit c32a2d
						debug2( "buffer drained %"SIZE_P" / %"SIZE_P
Packit c32a2d
						,	oldfill-bytes, limit );
Packit c32a2d
					}
Packit c32a2d
					else
Packit c32a2d
						debug("drain without playback ... not good");
Packit c32a2d
					xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
				}
Packit c32a2d
				break;
Packit c32a2d
				case XF_CMD_TERMINATE:
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					/* Will that response always reach the writer? Well, at worst,
Packit c32a2d
					   it's an ignored error on xfermem_getcmd(). */
Packit c32a2d
					xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
					return 0;
Packit c32a2d
				case XF_CMD_PAUSE:
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					draining = FALSE;
Packit c32a2d
					out123_pause(ao);
Packit c32a2d
					mystate = ao->state;
Packit c32a2d
					xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
				break;
Packit c32a2d
				case XF_CMD_DROP:
Packit c32a2d
					intflag = FALSE;
Packit c32a2d
					draining = FALSE;
Packit c32a2d
					xf->readindex = xf->freeindex;
Packit c32a2d
					out123_drop(ao);
Packit c32a2d
					xfermem_putcmd(my_fd, XF_CMD_OK);
Packit c32a2d
				break;
Packit c32a2d
				default:
Packit c32a2d
					if(!AOQUIET)
Packit c32a2d
						error1("Unknown command %u encountered. Confused Suicide!", cmd[i]);
Packit c32a2d
					return 1;
Packit c32a2d
#undef GOOD_READVAL_BUF
Packit c32a2d
			}
Packit c32a2d
		} /* Ensure that an interrupt-giving command has been received. */
Packit c32a2d
		while(intflag);
Packit c32a2d
		if(intflag && !AOQUIET)
Packit c32a2d
			error("buffer: The intflag should not be set anymore.");
Packit c32a2d
		intflag = FALSE; /* Any possible harm by _not_ ensuring that the flag is cleared here? */
Packit c32a2d
	}
Packit c32a2d
}