Blame src/libout123/libout123.c

Packit c32a2d
/*
Packit c32a2d
	audio: audio output interface
Packit c32a2d
Packit c32a2d
	copyright ?-2016 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 Michael Hipp
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
#include "out123_int.h"
Packit c32a2d
#include "wav.h"
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
#include "buffer.h"
Packit c32a2d
static int have_buffer(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	return (ao->buffer_pid != -1);
Packit c32a2d
}
Packit c32a2d
#endif
Packit c32a2d
#include "stringlists.h"
Packit c32a2d
Packit c32a2d
#include "debug.h"
Packit c32a2d
Packit c32a2d
/* An output that is live and does not deal with pausing itself.
Packit c32a2d
   The device needs to be closed if we stop feeding. */
Packit c32a2d
#define SENSITIVE_OUTPUT(ao) \
Packit c32a2d
  (    (ao)->propflags & OUT123_PROP_LIVE \
Packit c32a2d
  && !((ao)->propflags & OUT123_PROP_PERSISTENT) )
Packit c32a2d
Packit c32a2d
static const char *default_name = "out123";
Packit c32a2d
Packit c32a2d
static int modverbose(out123_handle *ao, int final)
Packit c32a2d
{
Packit c32a2d
	debug3("modverbose: %x %x %x"
Packit c32a2d
	,	(unsigned)ao->flags, (unsigned)ao->auxflags, (unsigned)OUT123_QUIET);
Packit c32a2d
	return AOQUIET
Packit c32a2d
	?	(final ? 0 : -1)
Packit c32a2d
	:	ao->verbose;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static void check_output_module( out123_handle *ao
Packit c32a2d
,	const char *name, const char *device, int final );
Packit c32a2d
Packit c32a2d
static void out123_clear_module(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	ao->open = NULL;
Packit c32a2d
	ao->get_formats = NULL;
Packit c32a2d
	ao->write = NULL;
Packit c32a2d
	ao->flush = NULL;
Packit c32a2d
	ao->drain = NULL;
Packit c32a2d
	ao->close = NULL;
Packit c32a2d
	ao->deinit = NULL;
Packit c32a2d
Packit c32a2d
	ao->module = NULL;
Packit c32a2d
	ao->userptr = NULL;
Packit c32a2d
	ao->fn = -1;
Packit c32a2d
	/* The default is live output devices, files are the special case. */
Packit c32a2d
	ao->propflags = OUT123_PROP_LIVE;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Ensure that real name is not leaked, needs to be freed before any call to
Packit c32a2d
   ao->open(ao). One might free it on closing already, but it might be sensible
Packit c32a2d
   to keep it around, might still be the same after re-opening. */
Packit c32a2d
static int aoopen(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	if(ao->realname)
Packit c32a2d
	{
Packit c32a2d
		free(ao->realname);
Packit c32a2d
		ao->realname = NULL;
Packit c32a2d
	}
Packit c32a2d
	return ao->open(ao);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
out123_handle* attribute_align_arg out123_new(void)
Packit c32a2d
{
Packit c32a2d
	out123_handle* ao = malloc( sizeof( out123_handle ) );
Packit c32a2d
	if(!ao)
Packit c32a2d
		return NULL;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	ao->buffer_pid = -1;
Packit c32a2d
	ao->buffer_fd[0] = -1;
Packit c32a2d
	ao->buffer_fd[1] = -1;
Packit c32a2d
	ao->buffermem = NULL;
Packit c32a2d
#endif
Packit c32a2d
Packit c32a2d
	out123_clear_module(ao);
Packit c32a2d
	ao->name = compat_strdup(default_name);
Packit c32a2d
	ao->realname = NULL;
Packit c32a2d
	ao->driver = NULL;
Packit c32a2d
	ao->device = NULL;
Packit c32a2d
Packit c32a2d
	ao->flags = OUT123_KEEP_PLAYING;
Packit c32a2d
	ao->rate = -1;
Packit c32a2d
	ao->gain = -1;
Packit c32a2d
	ao->channels = -1;
Packit c32a2d
	ao->format = -1;
Packit c32a2d
	ao->framesize = 0;
Packit c32a2d
	ao->state = play_dead;
Packit c32a2d
	ao->auxflags = 0;
Packit c32a2d
	ao->preload = 0.;
Packit c32a2d
	ao->verbose = 0;
Packit c32a2d
	ao->device_buffer = 0.;
Packit c32a2d
	ao->bindir = NULL;
Packit c32a2d
	return ao;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void attribute_align_arg out123_del(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug2("[%ld]out123_del(%p)", (long)getpid(), (void*)ao);
Packit c32a2d
	if(!ao) return;
Packit c32a2d
Packit c32a2d
	out123_close(ao); /* TODO: That talks to the buffer if present. */
Packit c32a2d
	out123_set_buffer(ao, 0);
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao)) buffer_exit(ao);
Packit c32a2d
#endif
Packit c32a2d
	if(ao->name)
Packit c32a2d
		free(ao->name);
Packit c32a2d
	if(ao->bindir)
Packit c32a2d
		free(ao->bindir);
Packit c32a2d
	free(ao);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Error reporting */
Packit c32a2d
Packit c32a2d
/* Carefully keep that in sync with the error enum! */
Packit c32a2d
/* Sizing according to contents so that we can check! */
Packit c32a2d
static const char *const errstring[] =
Packit c32a2d
{
Packit c32a2d
	"no problem"
Packit c32a2d
,	"out of memory"
Packit c32a2d
,	"bad driver name"
Packit c32a2d
,	"failure loading driver module"
Packit c32a2d
,	"no driver loaded"
Packit c32a2d
,	"no active audio device"
Packit c32a2d
,	"some device playback error"
Packit c32a2d
,	"failed to open device"
Packit c32a2d
,	"buffer (communication) error"
Packit c32a2d
,	"basic module system error"
Packit c32a2d
,	"bad function argument(s)"
Packit c32a2d
,	"unknown parameter code"
Packit c32a2d
,	"attempt to set read-only parameter"
Packit c32a2d
,	"invalid out123 handle"
Packit c32a2d
};
Packit c32a2d
Packit c32a2d
const char* attribute_align_arg out123_strerror(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	return out123_plain_strerror(out123_errcode(ao));
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int out123_errcode(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	if(!ao) return OUT123_BAD_HANDLE;
Packit c32a2d
	else    return ao->errcode;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
const char* attribute_align_arg out123_plain_strerror(int errcode)
Packit c32a2d
{
Packit c32a2d
	if(errcode == OUT123_ERR)
Packit c32a2d
		return "some generic error";
Packit c32a2d
	if(errcode >= OUT123_ERRCOUNT || errcode < 0)
Packit c32a2d
		return "invalid error code";
Packit c32a2d
Packit c32a2d
	/* Let's be paranoid, one _may_ forget to extend errstrings when
Packit c32a2d
	   adding a new entry to the enum. */
Packit c32a2d
	if(errcode >= sizeof(errstring)/sizeof(char*))
Packit c32a2d
		return "outdated error list (library bug)";
Packit c32a2d
Packit c32a2d
	return errstring[errcode];
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static int out123_seterr(out123_handle *ao, enum out123_error errcode)
Packit c32a2d
{
Packit c32a2d
	if(!ao)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	ao->errcode = errcode;
Packit c32a2d
	return errcode == OUT123_OK ? OUT123_OK : OUT123_ERR;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* pre-playback setup */
Packit c32a2d
Packit c32a2d
int attribute_align_arg
Packit c32a2d
out123_set_buffer(out123_handle *ao, size_t buffer_bytes)
Packit c32a2d
{
Packit c32a2d
	debug2("out123_set_buffer(%p, %"SIZE_P")", (void*)ao, (size_p)buffer_bytes);
Packit c32a2d
	if(!ao)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
	/* Close any audio output module if present, also kill of buffer if present,
Packit c32a2d
	   then start new buffer process with newly allocated storage if given
Packit c32a2d
	   size is non-zero. */
Packit c32a2d
	out123_close(ao);
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
		buffer_exit(ao);
Packit c32a2d
	if(buffer_bytes)
Packit c32a2d
		return buffer_init(ao, buffer_bytes);
Packit c32a2d
#endif
Packit c32a2d
	return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int attribute_align_arg
Packit c32a2d
out123_param( out123_handle *ao, enum out123_parms code
Packit c32a2d
            , long value, double fvalue, const char *svalue )
Packit c32a2d
{
Packit c32a2d
	int ret = 0;
Packit c32a2d
Packit c32a2d
	debug4("out123_param(%p, %i, %li, %g)", (void*)ao, (int)code, value, fvalue);
Packit c32a2d
	if(!ao)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
Packit c32a2d
	switch(code)
Packit c32a2d
	{
Packit c32a2d
		case OUT123_FLAGS:
Packit c32a2d
			ao->flags = (int)value;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_PRELOAD:
Packit c32a2d
			ao->preload = fvalue;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_GAIN:
Packit c32a2d
			ao->gain = value;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_VERBOSE:
Packit c32a2d
			ao->verbose = (int)value;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_DEVICEBUFFER:
Packit c32a2d
			ao->device_buffer = fvalue;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_PROPFLAGS:
Packit c32a2d
			ao->errcode = OUT123_SET_RO_PARAM;
Packit c32a2d
			ret = OUT123_ERR;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_NAME:
Packit c32a2d
			if(ao->name)
Packit c32a2d
				free(ao->name);
Packit c32a2d
			ao->name = compat_strdup(svalue ? svalue : default_name);
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_BINDIR:
Packit c32a2d
			if(ao->bindir)
Packit c32a2d
				free(ao->bindir);
Packit c32a2d
			ao->bindir = compat_strdup(svalue);
Packit c32a2d
		break;
Packit c32a2d
		default:
Packit c32a2d
			ao->errcode = OUT123_BAD_PARAM;
Packit c32a2d
			if(!AOQUIET) error1("bad parameter code %i", (int)code);
Packit c32a2d
			ret = OUT123_ERR;
Packit c32a2d
	}
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	/* If there is a buffer, it needs to update its copy of parameters. */
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
		/* No error check; if that fails, buffer is dead and we will notice
Packit c32a2d
		   soon enough. */
Packit c32a2d
		buffer_sync_param(ao);
Packit c32a2d
#endif
Packit c32a2d
	return ret;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int attribute_align_arg
Packit c32a2d
out123_getparam( out123_handle *ao, enum out123_parms code
Packit c32a2d
               , long *ret_value, double *ret_fvalue, char* *ret_svalue )
Packit c32a2d
{
Packit c32a2d
	int ret = 0;
Packit c32a2d
	long value = 0;
Packit c32a2d
	double fvalue = 0.;
Packit c32a2d
	char *svalue = NULL;
Packit c32a2d
Packit c32a2d
	debug4( "out123_getparam(%p, %i, %p, %p)"
Packit c32a2d
	,	(void*)ao, (int)code, (void*)ret_value, (void*)ret_fvalue );
Packit c32a2d
	if(!ao)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
Packit c32a2d
	switch(code)
Packit c32a2d
	{
Packit c32a2d
		case OUT123_FLAGS:
Packit c32a2d
			value = ao->flags;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_PRELOAD:
Packit c32a2d
			fvalue = ao->preload;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_GAIN:
Packit c32a2d
			value = ao->gain;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_VERBOSE:
Packit c32a2d
			value = ao->verbose;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_DEVICEBUFFER:
Packit c32a2d
			fvalue = ao->device_buffer;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_PROPFLAGS:
Packit c32a2d
			value = ao->propflags;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_NAME:
Packit c32a2d
			svalue = ao->realname ? ao->realname : ao->name;
Packit c32a2d
		break;
Packit c32a2d
		case OUT123_BINDIR:
Packit c32a2d
			svalue = ao->bindir;
Packit c32a2d
		break;
Packit c32a2d
		default:
Packit c32a2d
			if(!AOQUIET) error1("bad parameter code %i", (int)code);
Packit c32a2d
			ao->errcode = OUT123_BAD_PARAM;
Packit c32a2d
			ret = OUT123_ERR;
Packit c32a2d
	}
Packit c32a2d
	if(!ret)
Packit c32a2d
	{
Packit c32a2d
		if(ret_value)  *ret_value  = value;
Packit c32a2d
		if(ret_fvalue) *ret_fvalue = fvalue;
Packit c32a2d
		if(ret_svalue) *ret_svalue = svalue;
Packit c32a2d
	}
Packit c32a2d
	return ret;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int attribute_align_arg
Packit c32a2d
out123_param_from(out123_handle *ao, out123_handle* from_ao)
Packit c32a2d
{
Packit c32a2d
	debug2("out123_param_from(%p, %p)", (void*)ao, (void*)from_ao);
Packit c32a2d
	if(!ao || !from_ao) return -1;
Packit c32a2d
Packit c32a2d
	ao->flags     = from_ao->flags;
Packit c32a2d
	ao->preload   = from_ao->preload;
Packit c32a2d
	ao->gain      = from_ao->gain;
Packit c32a2d
	ao->device_buffer = from_ao->device_buffer;
Packit c32a2d
	ao->verbose   = from_ao->verbose;
Packit c32a2d
	if(ao->name)
Packit c32a2d
		free(ao->name);
Packit c32a2d
	ao->name = compat_strdup(from_ao->name);
Packit c32a2d
	if(ao->bindir)
Packit c32a2d
		free(ao->bindir);
Packit c32a2d
	ao->bindir = compat_strdup(from_ao->bindir);
Packit c32a2d
Packit c32a2d
	return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
/* Serialization of tunable parameters to communicate them between
Packit c32a2d
   main process and buffer. Make sure these two stay in sync ... */
Packit c32a2d
Packit c32a2d
int write_parameters(out123_handle *ao, int who)
Packit c32a2d
{
Packit c32a2d
	int fd = ao->buffermem->fd[who];
Packit c32a2d
	if(
Packit c32a2d
		GOOD_WRITEVAL(fd, ao->flags)
Packit c32a2d
	&&	GOOD_WRITEVAL(fd, ao->preload)
Packit c32a2d
	&&	GOOD_WRITEVAL(fd, ao->gain)
Packit c32a2d
	&&	GOOD_WRITEVAL(fd, ao->device_buffer)
Packit c32a2d
	&&	GOOD_WRITEVAL(fd, ao->verbose)
Packit c32a2d
	&&	GOOD_WRITEVAL(fd, ao->propflags)
Packit c32a2d
	&& !xfer_write_string(ao, who, ao->name)
Packit c32a2d
	&& !xfer_write_string(ao, who, ao->bindir)
Packit c32a2d
	)
Packit c32a2d
		return 0;
Packit c32a2d
	else
Packit c32a2d
		return -1;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int read_parameters(out123_handle *ao
Packit c32a2d
,	int who, byte *prebuf, int *preoff, int presize)
Packit c32a2d
{
Packit c32a2d
	int fd = ao->buffermem->fd[who];
Packit c32a2d
#define GOOD_READVAL_BUF(fd, val) \
Packit c32a2d
	!read_buf(fd, &val, sizeof(val), prebuf, preoff, presize)
Packit c32a2d
	if(
Packit c32a2d
		GOOD_READVAL_BUF(fd, ao->flags)
Packit c32a2d
	&&	GOOD_READVAL_BUF(fd, ao->preload)
Packit c32a2d
	&&	GOOD_READVAL_BUF(fd, ao->gain)
Packit c32a2d
	&&	GOOD_READVAL_BUF(fd, ao->device_buffer)
Packit c32a2d
	&&	GOOD_READVAL_BUF(fd, ao->verbose)
Packit c32a2d
	&&	GOOD_READVAL_BUF(fd, ao->propflags)
Packit c32a2d
	&& !xfer_read_string(ao, who, &ao->name)
Packit c32a2d
	&& !xfer_read_string(ao, who, &ao->bindir)
Packit c32a2d
	)
Packit c32a2d
		return 0;
Packit c32a2d
	else
Packit c32a2d
		return -1;
Packit c32a2d
#undef GOOD_READVAL_BUF
Packit c32a2d
}
Packit c32a2d
#endif
Packit c32a2d
Packit c32a2d
int attribute_align_arg
Packit c32a2d
out123_open(out123_handle *ao, const char* driver, const char* device)
Packit c32a2d
{
Packit c32a2d
	debug4( "[%ld]out123_open(%p, %s, %s)", (long)getpid(), (void*)ao
Packit c32a2d
	,	driver ? driver : "<nil>", device ? device : "<nil>" );
Packit c32a2d
	if(!ao)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
Packit c32a2d
	out123_close(ao);
Packit c32a2d
	debug("out123_open() continuing");
Packit c32a2d
Packit c32a2d
	/* Ensure that audio format is freshly set for "no format yet" mode.
Packit c32a2d
	   In out123_start*/
Packit c32a2d
	ao->rate = -1;
Packit c32a2d
	ao->channels = -1;
Packit c32a2d
	ao->format = -1;
Packit c32a2d
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
	{
Packit c32a2d
		if(buffer_open(ao, driver, device))
Packit c32a2d
			return OUT123_ERR;
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
#endif
Packit c32a2d
	{
Packit c32a2d
		/* We just quickly check if the device can be accessed at all,
Packit c32a2d
		   same as out123_encodings! */
Packit c32a2d
		char *nextname, *modnames;
Packit c32a2d
		const char *names = driver ? driver : DEFAULT_OUTPUT_MODULE;
Packit c32a2d
Packit c32a2d
		if(!names) return out123_seterr(ao, OUT123_BAD_DRIVER_NAME);
Packit c32a2d
Packit c32a2d
		/* It is ridiculous how these error messages are larger than the pieces
Packit c32a2d
		   of memory they are about! */
Packit c32a2d
		if(device && !(ao->device = compat_strdup(device)))
Packit c32a2d
		{
Packit c32a2d
			if(!AOQUIET) error("OOM device name copy");
Packit c32a2d
			return out123_seterr(ao, OUT123_DOOM);
Packit c32a2d
		}
Packit c32a2d
Packit c32a2d
		if(!(modnames = compat_strdup(names)))
Packit c32a2d
		{
Packit c32a2d
			out123_close(ao); /* Frees ao->device, too. */
Packit c32a2d
			if(!AOQUIET) error("OOM driver names");
Packit c32a2d
			return out123_seterr(ao, OUT123_DOOM);
Packit c32a2d
		}
Packit c32a2d
Packit c32a2d
		/* Now loop over the list of possible modules to find one that works. */
Packit c32a2d
		nextname = strtok(modnames, ",");
Packit c32a2d
		while(!ao->open && nextname)
Packit c32a2d
		{
Packit c32a2d
			char *curname = nextname;
Packit c32a2d
			nextname = strtok(NULL, ",");
Packit c32a2d
			check_output_module(ao, curname, device, !nextname);
Packit c32a2d
			if(ao->open)
Packit c32a2d
			{
Packit c32a2d
				if(AOVERBOSE(2))
Packit c32a2d
					fprintf(stderr, "Chosen output module: %s\n", curname);
Packit c32a2d
				/* A bit redundant, but useful when it's a fake module. */
Packit c32a2d
				if(!(ao->driver = compat_strdup(curname)))
Packit c32a2d
				{
Packit c32a2d
					out123_close(ao);
Packit c32a2d
					if(!AOQUIET) error("OOM driver name");
Packit c32a2d
					return out123_seterr(ao, OUT123_DOOM);
Packit c32a2d
				}
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
Packit c32a2d
		free(modnames);
Packit c32a2d
Packit c32a2d
		if(!ao->open) /* At least an open() routine must be present. */
Packit c32a2d
		{
Packit c32a2d
			if(!AOQUIET)
Packit c32a2d
				error2("Found no driver out of [%s] working with device %s."
Packit c32a2d
				,	names, device ? device : "<default>");
Packit c32a2d
			/* Proper more detailed error code could be set already. */
Packit c32a2d
			if(ao->errcode == OUT123_OK)
Packit c32a2d
				ao->errcode = OUT123_BAD_DRIVER;
Packit c32a2d
			return OUT123_ERR;
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
	/* Got something. */
Packit c32a2d
	ao->state = play_stopped;
Packit c32a2d
	return OUT123_OK;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Be resilient, always do cleanup work regardless of state. */
Packit c32a2d
void attribute_align_arg out123_close(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug2("[%ld]out123_close(%p)", (long)getpid(), (void*)ao);
Packit c32a2d
	if(!ao)
Packit c32a2d
		return;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
Packit c32a2d
	out123_drain(ao);
Packit c32a2d
	out123_stop(ao);
Packit c32a2d
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
		buffer_close(ao);
Packit c32a2d
	else
Packit c32a2d
#endif
Packit c32a2d
	{
Packit c32a2d
		if(ao->deinit)
Packit c32a2d
			ao->deinit(ao);
Packit c32a2d
		if(ao->module)
Packit c32a2d
			close_module(ao->module, modverbose(ao, 0));
Packit c32a2d
		/* Null module methods and pointer. */
Packit c32a2d
		out123_clear_module(ao);
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	/* These copies exist in addition to the ones for the buffer. */
Packit c32a2d
	if(ao->driver)
Packit c32a2d
		free(ao->driver);
Packit c32a2d
	ao->driver = NULL;
Packit c32a2d
	if(ao->device)	
Packit c32a2d
		free(ao->device);
Packit c32a2d
	ao->device = NULL;
Packit c32a2d
	if(ao->realname)
Packit c32a2d
		free(ao->realname);
Packit c32a2d
	ao->realname = NULL;
Packit c32a2d
Packit c32a2d
	ao->state = play_dead;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int attribute_align_arg 
Packit c32a2d
out123_start(out123_handle *ao, long rate, int channels, int encoding)
Packit c32a2d
{
Packit c32a2d
	debug5( "[%ld]out123_start(%p, %li, %i, %i)", (long)getpid()
Packit c32a2d
	,	(void*)ao, rate, channels, encoding );
Packit c32a2d
	if(!ao)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
Packit c32a2d
	out123_stop(ao);
Packit c32a2d
	debug("out123_start() continuing");
Packit c32a2d
	if(ao->state != play_stopped)
Packit c32a2d
		return out123_seterr(ao, OUT123_NO_DRIVER);
Packit c32a2d
Packit c32a2d
	/* Stored right away as parameters for ao->open() and also for reference.
Packit c32a2d
	   framesize needed for out123_play(). */
Packit c32a2d
	ao->rate      = rate;
Packit c32a2d
	ao->channels  = channels;
Packit c32a2d
	ao->format    = encoding;
Packit c32a2d
	ao->framesize = out123_encsize(encoding)*channels;
Packit c32a2d
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
	{
Packit c32a2d
		if(!buffer_start(ao))
Packit c32a2d
		{
Packit c32a2d
			ao->state = play_live;
Packit c32a2d
			return OUT123_OK;
Packit c32a2d
		}
Packit c32a2d
		else
Packit c32a2d
			return OUT123_ERR;
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
#endif
Packit c32a2d
	{
Packit c32a2d
		if(aoopen(ao) < 0)
Packit c32a2d
			return out123_seterr(ao, OUT123_DEV_OPEN);
Packit c32a2d
		ao->state = play_live;
Packit c32a2d
		return OUT123_OK;
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void attribute_align_arg out123_pause(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug3( "[%ld]out123_pause(%p) %i", (long)getpid()
Packit c32a2d
	,	(void*)ao, ao ? (int)ao->state : -1 );
Packit c32a2d
	if(ao && ao->state == play_live)
Packit c32a2d
	{
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
		if(have_buffer(ao)){ debug("pause with buffer"); buffer_pause(ao); }
Packit c32a2d
		else
Packit c32a2d
#endif
Packit c32a2d
		{
Packit c32a2d
debug1("pause without buffer, sensitive=%d", SENSITIVE_OUTPUT(ao));
Packit c32a2d
			/* Close live devices to avoid underruns. */
Packit c32a2d
			if( SENSITIVE_OUTPUT(ao)
Packit c32a2d
			  && ao->close && ao->close(ao) && !AOQUIET )
Packit c32a2d
				error("trouble closing device");
Packit c32a2d
		}
Packit c32a2d
		ao->state = play_paused;
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void attribute_align_arg out123_continue(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug3( "[%ld]out123_continue(%p) %i", (long)getpid()
Packit c32a2d
	,	(void*)ao, ao ? (int)ao->state : -1 );
Packit c32a2d
	if(ao && ao->state == play_paused)
Packit c32a2d
	{
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
		if(have_buffer(ao)) buffer_continue(ao);
Packit c32a2d
		else
Packit c32a2d
#endif
Packit c32a2d
		/* Re-open live devices to avoid underruns. */
Packit c32a2d
		if(SENSITIVE_OUTPUT(ao) && aoopen(ao) < 0)
Packit c32a2d
		{
Packit c32a2d
			/* Will be overwritten by following out123_play() ... */
Packit c32a2d
			ao->errcode = OUT123_DEV_OPEN;
Packit c32a2d
			if(!AOQUIET)
Packit c32a2d
				error("failed re-opening of device after pause");
Packit c32a2d
			return;
Packit c32a2d
		}
Packit c32a2d
		ao->state = play_live;
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void attribute_align_arg out123_stop(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug2("[%ld]out123_stop(%p)", (long)getpid(), (void*)ao);
Packit c32a2d
	if(!ao)
Packit c32a2d
		return;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
	if(!(ao->state == play_paused || ao->state == play_live))
Packit c32a2d
		return;
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
		buffer_stop(ao);
Packit c32a2d
	else
Packit c32a2d
#endif
Packit c32a2d
	if(   ao->state == play_live
Packit c32a2d
	  || (ao->state == play_paused && !SENSITIVE_OUTPUT(ao)) )
Packit c32a2d
	{
Packit c32a2d
		if(ao->close && ao->close(ao) && !AOQUIET)
Packit c32a2d
			error("trouble closing device");
Packit c32a2d
	}
Packit c32a2d
	ao->state = play_stopped;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
size_t attribute_align_arg
Packit c32a2d
out123_play(out123_handle *ao, void *bytes, size_t count)
Packit c32a2d
{
Packit c32a2d
	size_t sum = 0;
Packit c32a2d
	int written;
Packit c32a2d
Packit c32a2d
	debug5( "[%ld]out123_play(%p, %p, %"SIZE_P") (%i)", (long)getpid()
Packit c32a2d
	,	(void*)ao, bytes, (size_p)count, ao ? (int)ao->state : -1 );
Packit c32a2d
	if(!ao)
Packit c32a2d
		return 0;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
	/* If paused, automatically continue. Other states are an error. */
Packit c32a2d
	if(ao->state != play_live)
Packit c32a2d
	{
Packit c32a2d
		if(ao->state == play_paused)
Packit c32a2d
			out123_continue(ao);
Packit c32a2d
		if(ao->state != play_live)
Packit c32a2d
		{
Packit c32a2d
			ao->errcode = OUT123_NOT_LIVE;
Packit c32a2d
			return 0;
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	/* Ensure that we are writing whole PCM frames. */
Packit c32a2d
	count -= count % ao->framesize;
Packit c32a2d
	if(!count) return 0;
Packit c32a2d
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
		return buffer_write(ao, bytes, count);
Packit c32a2d
	else
Packit c32a2d
#endif
Packit c32a2d
	do /* Playback in a loop to be able to continue after interruptions. */
Packit c32a2d
	{
Packit c32a2d
		errno = 0;
Packit c32a2d
		written = ao->write(ao, (unsigned char*)bytes, (int)count);
Packit c32a2d
		debug4( "written: %d errno: %i (%s), keep_on=%d"
Packit c32a2d
		,	written, errno, strerror(errno)
Packit c32a2d
		,	ao->flags & OUT123_KEEP_PLAYING );
Packit c32a2d
		if(written >= 0){ sum+=written; count -= written; }
Packit c32a2d
		else if(errno != EINTR)
Packit c32a2d
		{
Packit c32a2d
			ao->errcode = OUT123_DEV_PLAY;
Packit c32a2d
			if(!AOQUIET)
Packit c32a2d
				error1("Error in writing audio (%s?)!", strerror(errno));
Packit c32a2d
			/* If written < 0, this is a serious issue ending this playback round. */
Packit c32a2d
			break;
Packit c32a2d
		}
Packit c32a2d
	} while(count && ao->flags & OUT123_KEEP_PLAYING);
Packit c32a2d
Packit c32a2d
	debug3( "out123_play(%p, %p, ...) = %"SIZE_P
Packit c32a2d
	,	(void*)ao, bytes, (size_p)sum );
Packit c32a2d
	return sum;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Drop means to flush it down. Quickly. */
Packit c32a2d
void attribute_align_arg out123_drop(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug2("[%ld]out123_drop(%p)", (long)getpid(), (void*)ao);
Packit c32a2d
	if(!ao)
Packit c32a2d
		return;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
		buffer_drop(ao);
Packit c32a2d
	else
Packit c32a2d
#endif
Packit c32a2d
	if(ao->state == play_live)
Packit c32a2d
	{
Packit c32a2d
		if(ao->propflags & OUT123_PROP_LIVE && ao->flush)
Packit c32a2d
			ao->flush(ao);
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void attribute_align_arg out123_drain(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug2("[%ld]out123_drain(%p) ", (long)getpid(), (void*)ao);
Packit c32a2d
	if(!ao)
Packit c32a2d
		return;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
	/* If paused, automatically continue. */
Packit c32a2d
	if(ao->state != play_live)
Packit c32a2d
	{
Packit c32a2d
		if(ao->state == play_paused)
Packit c32a2d
			out123_continue(ao);
Packit c32a2d
		if(ao->state != play_live)
Packit c32a2d
			return;
Packit c32a2d
	}
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
		buffer_drain(ao);
Packit c32a2d
	else
Packit c32a2d
#endif
Packit c32a2d
	{
Packit c32a2d
		if(ao->drain)
Packit c32a2d
			ao->drain(ao);
Packit c32a2d
		out123_pause(ao);
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void attribute_align_arg out123_ndrain(out123_handle *ao, size_t bytes)
Packit c32a2d
{
Packit c32a2d
	debug3("[%ld]out123_ndrain(%p, %"SIZE_P")", (long)getpid(), (void*)ao, (size_p)bytes);
Packit c32a2d
	if(!ao)
Packit c32a2d
		return;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
	/* If paused, automatically continue. */
Packit c32a2d
	if(ao->state != play_live)
Packit c32a2d
	{
Packit c32a2d
		if(ao->state == play_paused)
Packit c32a2d
			out123_continue(ao);
Packit c32a2d
		if(ao->state != play_live)
Packit c32a2d
			return;
Packit c32a2d
	}
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
		buffer_ndrain(ao, bytes);
Packit c32a2d
	else
Packit c32a2d
#endif
Packit c32a2d
	{
Packit c32a2d
		if(ao->drain)
Packit c32a2d
			ao->drain(ao);
Packit c32a2d
		out123_pause(ao);
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
/* A function that does nothing and returns nothing. */
Packit c32a2d
static void builtin_nothing(out123_handle *ao){}
Packit c32a2d
static int test_open(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug("test_open");
Packit c32a2d
	return OUT123_OK;
Packit c32a2d
}
Packit c32a2d
static int test_get_formats(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug("test_get_formats");
Packit c32a2d
	return MPG123_ENC_ANY;
Packit c32a2d
}
Packit c32a2d
static int test_write(out123_handle *ao, unsigned char *buf, int len)
Packit c32a2d
{
Packit c32a2d
	debug2("test_write: %i B from %p", len, (void*)buf);
Packit c32a2d
	return len;
Packit c32a2d
}
Packit c32a2d
static void test_flush(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug("test_flush");
Packit c32a2d
}
Packit c32a2d
static void test_drain(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug("test_drain");
Packit c32a2d
}
Packit c32a2d
static int test_close(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug("test_drain");
Packit c32a2d
	return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Open one of our builtin driver modules. */
Packit c32a2d
static int open_fake_module(out123_handle *ao, const char *driver)
Packit c32a2d
{
Packit c32a2d
	if(!strcmp("test", driver))
Packit c32a2d
	{
Packit c32a2d
		ao->propflags &= ~OUT123_PROP_LIVE;
Packit c32a2d
		ao->open  = test_open;
Packit c32a2d
		ao->get_formats = test_get_formats;
Packit c32a2d
		ao->write = test_write;
Packit c32a2d
		ao->flush = test_flush;
Packit c32a2d
		ao->drain = test_drain;
Packit c32a2d
		ao->close = test_close;
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	if(!strcmp("raw", driver))
Packit c32a2d
	{
Packit c32a2d
		ao->propflags &= ~OUT123_PROP_LIVE;
Packit c32a2d
		ao->open  = raw_open;
Packit c32a2d
		ao->get_formats = raw_formats;
Packit c32a2d
		ao->write = wav_write;
Packit c32a2d
		ao->flush = builtin_nothing;
Packit c32a2d
		ao->drain = wav_drain;
Packit c32a2d
		ao->close = raw_close;
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	if(!strcmp("wav", driver))
Packit c32a2d
	{
Packit c32a2d
		ao->propflags &= ~OUT123_PROP_LIVE;
Packit c32a2d
		ao->open = wav_open;
Packit c32a2d
		ao->get_formats = wav_formats;
Packit c32a2d
		ao->write = wav_write;
Packit c32a2d
		ao->flush = builtin_nothing;
Packit c32a2d
		ao->drain = wav_drain;
Packit c32a2d
		ao->close = wav_close;
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	if(!strcmp("cdr", driver))
Packit c32a2d
	{
Packit c32a2d
		ao->propflags &= ~OUT123_PROP_LIVE;
Packit c32a2d
		ao->open  = cdr_open;
Packit c32a2d
		ao->get_formats = cdr_formats;
Packit c32a2d
		ao->write = wav_write;
Packit c32a2d
		ao->flush = builtin_nothing;
Packit c32a2d
		ao->drain = wav_drain;
Packit c32a2d
		ao->close = raw_close;
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	if(!strcmp("au", driver))
Packit c32a2d
	{
Packit c32a2d
		ao->propflags &= ~OUT123_PROP_LIVE;
Packit c32a2d
		ao->open  = au_open;
Packit c32a2d
		ao->get_formats = au_formats;
Packit c32a2d
		ao->write = wav_write;
Packit c32a2d
		ao->flush = builtin_nothing;
Packit c32a2d
		ao->drain = wav_drain;
Packit c32a2d
		ao->close = au_close;
Packit c32a2d
	}
Packit c32a2d
	else return OUT123_ERR;
Packit c32a2d
Packit c32a2d
	return OUT123_OK;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Check if given output module is loadable and has a working device.
Packit c32a2d
   final flag triggers printing and storing of errors. */
Packit c32a2d
static void check_output_module( out123_handle *ao
Packit c32a2d
,	const char *name, const char *device, int final )
Packit c32a2d
{
Packit c32a2d
	int result;
Packit c32a2d
Packit c32a2d
	debug3("check_output_module %p %p %p", (void*)ao, (void*)device, (void*)ao->device);
Packit c32a2d
	if(AOVERBOSE(1))
Packit c32a2d
		fprintf( stderr, "Trying output module: %s, device: %s\n"
Packit c32a2d
		,	name, ao->device ? ao->device : "<nil>" );
Packit c32a2d
Packit c32a2d
	/* Use internal code. */
Packit c32a2d
	if(open_fake_module(ao, name) == OUT123_OK)
Packit c32a2d
		return;
Packit c32a2d
Packit c32a2d
	/* Open the module, initial check for availability+libraries. */
Packit c32a2d
	ao->module = open_module( "output", name, modverbose(ao, final), ao->bindir);
Packit c32a2d
	if(!ao->module)
Packit c32a2d
		return;
Packit c32a2d
	/* Check if module supports output */
Packit c32a2d
	if(!ao->module->init_output)
Packit c32a2d
	{
Packit c32a2d
		if(final)
Packit c32a2d
			error1("Module '%s' does not support audio output.", name);
Packit c32a2d
		goto check_output_module_cleanup;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	/* Should I do funny stuff with stderr file descriptor instead? */
Packit c32a2d
	if(final)
Packit c32a2d
	{
Packit c32a2d
		if(AOVERBOSE(2))
Packit c32a2d
			fprintf(stderr
Packit c32a2d
			,	"Note: %s is the last output option... showing you any error messages now.\n"
Packit c32a2d
			,	name);
Packit c32a2d
	}
Packit c32a2d
	else ao->auxflags |= OUT123_QUIET; /* Probing, so don't spill stderr with errors. */
Packit c32a2d
	result = ao->module->init_output(ao);
Packit c32a2d
	if(result == 0)
Packit c32a2d
	{ /* Try to open the device. I'm only interested in actually working modules. */
Packit c32a2d
		ao->format = -1;
Packit c32a2d
		result = aoopen(ao);
Packit c32a2d
		debug1("ao->open() = %i", result);
Packit c32a2d
		if(result >= 0) /* Opening worked, close again. */
Packit c32a2d
			ao->close(ao);
Packit c32a2d
		else if(ao->deinit)
Packit c32a2d
			ao->deinit(ao); /* Failed, ensure that cleanup after init_output() occurs. */
Packit c32a2d
	}
Packit c32a2d
	else if(!AOQUIET)
Packit c32a2d
		error2("Module '%s' init failed: %i", name, result);
Packit c32a2d
Packit c32a2d
	ao->auxflags &= ~OUT123_QUIET;
Packit c32a2d
Packit c32a2d
	if(result >= 0)
Packit c32a2d
		return;
Packit c32a2d
Packit c32a2d
check_output_module_cleanup:
Packit c32a2d
	/* Only if module did not check out we get to clean up here. */
Packit c32a2d
	close_module(ao->module, modverbose(ao, final));
Packit c32a2d
	out123_clear_module(ao);
Packit c32a2d
	return;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
static void audio_output_dump(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	fprintf(stderr, "ao->fn=%d\n", ao->fn);
Packit c32a2d
	fprintf(stderr, "ao->userptr=%p\n", ao->userptr);
Packit c32a2d
	fprintf(stderr, "ao->rate=%ld\n", ao->rate);
Packit c32a2d
	fprintf(stderr, "ao->gain=%ld\n", ao->gain);
Packit c32a2d
	fprintf(stderr, "ao->device='%s'\n", ao->device);
Packit c32a2d
	fprintf(stderr, "ao->channels=%d\n", ao->channels);
Packit c32a2d
	fprintf(stderr, "ao->format=%d\n", ao->format);
Packit c32a2d
}
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
int attribute_align_arg
Packit c32a2d
out123_drivers(out123_handle *ao, char ***names, char ***descr)
Packit c32a2d
{
Packit c32a2d
	char **tmpnames;
Packit c32a2d
	char **tmpdescr;
Packit c32a2d
	int count;
Packit c32a2d
	int i;
Packit c32a2d
Packit c32a2d
	if(!ao)
Packit c32a2d
		return -1;
Packit c32a2d
Packit c32a2d
	debug3("out123_drivers(%p, %p, %p)", (void*)ao, (void*)names, (void*)descr);
Packit c32a2d
	/* Wrap the call to isolate the lower levels from the user not being
Packit c32a2d
	   interested in both lists. it's a bit wasteful, but the code looks
Packit c32a2d
	   ugly enough already down there. */
Packit c32a2d
	count = list_modules("output", &tmpnames, &tmpdescr, modverbose(ao, 0), ao->bindir);
Packit c32a2d
	debug1("list_modules()=%i", count);
Packit c32a2d
	if(count < 0)
Packit c32a2d
	{
Packit c32a2d
		if(!AOQUIET)
Packit c32a2d
			error("Dynamic module search failed.");
Packit c32a2d
		count = 0;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(
Packit c32a2d
		stringlists_add( &tmpnames, &tmpdescr
Packit c32a2d
		,	"raw", "raw headerless stream (builtin)", &count )
Packit c32a2d
	||	stringlists_add( &tmpnames, &tmpdescr
Packit c32a2d
		,	"cdr", "compact disc digital audio stream (builtin)", &count )
Packit c32a2d
	||	stringlists_add( &tmpnames, &tmpdescr
Packit c32a2d
		,	"wav", "RIFF WAVE file (builtin)", &count )
Packit c32a2d
	||	stringlists_add( &tmpnames, &tmpdescr
Packit c32a2d
		,	"au", "Sun AU file (builtin)", &count )
Packit c32a2d
	||	stringlists_add( &tmpnames, &tmpdescr
Packit c32a2d
		,	"test", "output into the void (builtin)", &count )
Packit c32a2d
	)
Packit c32a2d
		if(!AOQUIET)
Packit c32a2d
			error("OOM");
Packit c32a2d
Packit c32a2d
	/* Return or free gathered lists of names or descriptions. */
Packit c32a2d
	if(names)
Packit c32a2d
		*names = tmpnames;
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		for(i=0; i
Packit c32a2d
			free(tmpnames[i]);
Packit c32a2d
		free(tmpnames);
Packit c32a2d
	}
Packit c32a2d
	if(descr)
Packit c32a2d
		*descr = tmpdescr;
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		for(i=0; i
Packit c32a2d
			free(tmpdescr[i]);
Packit c32a2d
		free(tmpdescr);
Packit c32a2d
	}
Packit c32a2d
	return count;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* We always have ao->driver and ao->device set, also with buffer.
Packit c32a2d
   The latter can be positively NULL, though. */
Packit c32a2d
int attribute_align_arg
Packit c32a2d
out123_driver_info(out123_handle *ao, char **driver, char **device)
Packit c32a2d
{
Packit c32a2d
	debug3( "out123_driver_info(%p, %p, %p)"
Packit c32a2d
	,	(void*)ao, (void*)driver, (void*)device );
Packit c32a2d
	if(!ao)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	if(!ao->driver)
Packit c32a2d
		return out123_seterr(ao, OUT123_NO_DRIVER);
Packit c32a2d
Packit c32a2d
	if(driver)
Packit c32a2d
		*driver = ao->driver;
Packit c32a2d
	if(device)
Packit c32a2d
		*device = ao->device;
Packit c32a2d
	return OUT123_OK;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int attribute_align_arg
Packit c32a2d
out123_encodings(out123_handle *ao, long rate, int channels)
Packit c32a2d
{
Packit c32a2d
	debug4( "[%ld]out123_encodings(%p, %li, %i)", (long)getpid()
Packit c32a2d
	,	(void*)ao, rate, channels );
Packit c32a2d
	if(!ao)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	ao->errcode = OUT123_OK;
Packit c32a2d
Packit c32a2d
	out123_stop(ao); /* That brings the buffer into waiting state, too. */
Packit c32a2d
Packit c32a2d
	if(ao->state != play_stopped)
Packit c32a2d
		return out123_seterr(ao, OUT123_NO_DRIVER);
Packit c32a2d
Packit c32a2d
	ao->channels = channels;
Packit c32a2d
	ao->rate     = rate;
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
		return buffer_encodings(ao);
Packit c32a2d
	else
Packit c32a2d
#endif
Packit c32a2d
	{
Packit c32a2d
		int enc = 0;
Packit c32a2d
		/* This tells outputs to choose a fitting format so that ao->open() succeeds
Packit c32a2d
		   They possibly set a sample rate and channel count they like best.
Packit c32a2d
		   We should add API to retrieve those defaults, too. */
Packit c32a2d
		ao->format   = -1;
Packit c32a2d
		if(aoopen(ao) >= 0)
Packit c32a2d
		{
Packit c32a2d
			/* Need to reset those since the choose-your-format open
Packit c32a2d
			   call might have changed them. */
Packit c32a2d
			ao->channels = channels;
Packit c32a2d
			ao->rate     = rate;
Packit c32a2d
			enc = ao->get_formats(ao);
Packit c32a2d
			ao->close(ao);
Packit c32a2d
			return enc;
Packit c32a2d
		}
Packit c32a2d
		else
Packit c32a2d
			return out123_seterr(ao, (ao->errcode != OUT123_OK
Packit c32a2d
			?	ao->errcode
Packit c32a2d
			:	OUT123_DEV_OPEN));
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int attribute_align_arg out123_encsize(int encoding)
Packit c32a2d
{
Packit c32a2d
	return MPG123_SAMPLESIZE(encoding);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int attribute_align_arg
Packit c32a2d
out123_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
	debug7( "[%ld]out123_formats(%p, %p, %i, %i, %i, %p)", (long)getpid()
Packit c32a2d
	,	(void*)ao, (void*)rates, ratecount, minchannels, maxchannels
Packit c32a2d
	,	(void*)fmtlist );
Packit c32a2d
	if(!ao)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	ao->errcode = OUT123_OK;
Packit c32a2d
Packit c32a2d
	out123_stop(ao); /* That brings the buffer into waiting state, too. */
Packit c32a2d
Packit c32a2d
	if(ao->state != play_stopped)
Packit c32a2d
		return out123_seterr(ao, OUT123_NO_DRIVER);
Packit c32a2d
Packit c32a2d
	if(ratecount > 0 && !rates)
Packit c32a2d
		return out123_seterr(ao, OUT123_ARG_ERROR);
Packit c32a2d
	if(!fmtlist || minchannels > maxchannels)
Packit c32a2d
		return out123_seterr(ao, OUT123_ARG_ERROR);
Packit c32a2d
	*fmtlist = NULL; /* Initialize so free(fmtlist) is always allowed. */
Packit c32a2d
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
		return buffer_formats( ao, rates, ratecount
Packit c32a2d
		                     , minchannels, maxchannels, fmtlist );
Packit c32a2d
	else
Packit c32a2d
#endif
Packit c32a2d
	{
Packit c32a2d
		/* This tells outputs to choose a fitting format so that ao->open()
Packit c32a2d
		   succeeds. */
Packit c32a2d
		ao->format   = -1;
Packit c32a2d
		ao->rate     = -1;
Packit c32a2d
		ao->channels = -1;
Packit c32a2d
		if(aoopen(ao) >= 0)
Packit c32a2d
		{
Packit c32a2d
			struct mpg123_fmt *fmts;
Packit c32a2d
			int ri, ch;
Packit c32a2d
			int fi = 0;
Packit c32a2d
			int fmtcount = 1; /* Always the default format. */
Packit c32a2d
			if(ratecount > 0)
Packit c32a2d
				fmtcount += ratecount*(maxchannels-minchannels+1);
Packit c32a2d
			if(!(fmts = malloc(sizeof(*fmts)*fmtcount)))
Packit c32a2d
			{
Packit c32a2d
				ao->close(ao);
Packit c32a2d
				return out123_seterr(ao, OUT123_DOOM);
Packit c32a2d
			}
Packit c32a2d
			/* Store default format if present. */
Packit c32a2d
			if(ao->format > 0 && ao->channels > 0 && ao->rate > 0)
Packit c32a2d
			{
Packit c32a2d
				fmts[0].rate     = ao->rate;
Packit c32a2d
				fmts[0].channels = ao->channels;
Packit c32a2d
				fmts[0].encoding = ao->format;
Packit c32a2d
			}
Packit c32a2d
			else
Packit c32a2d
			{ /* Ensure consistent -1 in all entries. */
Packit c32a2d
				fmts[0].rate     = -1;
Packit c32a2d
				fmts[0].channels = -1;
Packit c32a2d
				fmts[0].encoding = -1;
Packit c32a2d
			}
Packit c32a2d
			/* Test all combinations of rate and channel count. */
Packit c32a2d
			for(ri=0; ri
Packit c32a2d
			for(ch=minchannels; ch<=maxchannels; ++ch)
Packit c32a2d
			{
Packit c32a2d
				++fi;
Packit c32a2d
				ao->rate = rates[ri];
Packit c32a2d
				ao->channels = ch;
Packit c32a2d
				fmts[fi].rate     = ao->rate;
Packit c32a2d
				fmts[fi].channels = ao->channels;
Packit c32a2d
				fmts[fi].encoding = ao->get_formats(ao);
Packit c32a2d
			}
Packit c32a2d
			ao->close(ao);
Packit c32a2d
Packit c32a2d
			*fmtlist = fmts;
Packit c32a2d
			return fmtcount;
Packit c32a2d
		}
Packit c32a2d
		else
Packit c32a2d
			return out123_seterr(ao, (ao->errcode != OUT123_OK
Packit c32a2d
			?	ao->errcode
Packit c32a2d
			:	OUT123_DEV_OPEN));
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
size_t attribute_align_arg out123_buffered(out123_handle *ao)
Packit c32a2d
{
Packit c32a2d
	debug2("[%ld]out123_buffered(%p)", (long)getpid(), (void*)ao);
Packit c32a2d
	if(!ao)
Packit c32a2d
		return 0;
Packit c32a2d
	ao->errcode = 0;
Packit c32a2d
#ifndef NOXFERMEM
Packit c32a2d
	if(have_buffer(ao))
Packit c32a2d
	{
Packit c32a2d
		size_t fill = buffer_fill(ao);
Packit c32a2d
		debug2("out123_buffered(%p) = %"SIZE_P, (void*)ao, (size_p)fill);
Packit c32a2d
		return fill;
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
#endif
Packit c32a2d
		return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int attribute_align_arg out123_getformat( out123_handle *ao
Packit c32a2d
,	long *rate, int *channels, int *encoding, int *framesize )
Packit c32a2d
{
Packit c32a2d
	if(!ao)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
Packit c32a2d
	if(!(ao->state == play_paused || ao->state == play_live))
Packit c32a2d
		return out123_seterr(ao, OUT123_NOT_LIVE);
Packit c32a2d
Packit c32a2d
	if(rate)
Packit c32a2d
		*rate = ao->rate;
Packit c32a2d
	if(channels)
Packit c32a2d
		*channels = ao->channels;
Packit c32a2d
	if(encoding)
Packit c32a2d
		*encoding = ao->format;
Packit c32a2d
	if(framesize)
Packit c32a2d
		*framesize = ao->framesize;
Packit c32a2d
	return OUT123_OK;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
struct enc_desc
Packit c32a2d
{
Packit c32a2d
	int code; /* MPG123_ENC_SOMETHING */
Packit c32a2d
	const char *longname; /* signed bla bla */
Packit c32a2d
	const char *name; /* sXX, short name */
Packit c32a2d
};
Packit c32a2d
Packit c32a2d
static const struct enc_desc encdesc[] =
Packit c32a2d
{
Packit c32a2d
	{ MPG123_ENC_SIGNED_16,   "signed 16 bit",   "s16"  }
Packit c32a2d
,	{ MPG123_ENC_UNSIGNED_16, "unsigned 16 bit", "u16"  }
Packit c32a2d
,	{ MPG123_ENC_SIGNED_32,   "signed 32 bit",   "s32"  }
Packit c32a2d
,	{ MPG123_ENC_UNSIGNED_32, "unsigned 32 bit", "u32"  }
Packit c32a2d
,	{ MPG123_ENC_SIGNED_24,   "signed 24 bit",   "s24"  }
Packit c32a2d
,	{ MPG123_ENC_UNSIGNED_24, "unsigned 24 bit", "u24"  }
Packit c32a2d
,	{ MPG123_ENC_FLOAT_32,    "float (32 bit)",  "f32"  }
Packit c32a2d
,	{ MPG123_ENC_FLOAT_64,    "float (64 bit)",  "f64"  }
Packit c32a2d
,	{ MPG123_ENC_SIGNED_8,    "signed 8 bit",    "s8"   }
Packit c32a2d
,	{ MPG123_ENC_UNSIGNED_8,  "unsigned 8 bit",  "u8"   }
Packit c32a2d
,	{ MPG123_ENC_ULAW_8,      "mu-law (8 bit)",  "ulaw" }
Packit c32a2d
,	{ MPG123_ENC_ALAW_8,      "a-law (8 bit)",   "alaw" }
Packit c32a2d
};
Packit c32a2d
#define KNOWN_ENCS (sizeof(encdesc)/sizeof(struct enc_desc))
Packit c32a2d
Packit c32a2d
int attribute_align_arg out123_enc_list(int **enclist)
Packit c32a2d
{
Packit c32a2d
	int i;
Packit c32a2d
	if(!enclist)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	*enclist = malloc(sizeof(int)*KNOWN_ENCS);
Packit c32a2d
	if(!(*enclist))
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	for(i=0;i
Packit c32a2d
		(*enclist)[i] = encdesc[i].code;
Packit c32a2d
	return KNOWN_ENCS;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int attribute_align_arg out123_enc_byname(const char *name)
Packit c32a2d
{
Packit c32a2d
	int i;
Packit c32a2d
	if(!name)
Packit c32a2d
		return OUT123_ERR;
Packit c32a2d
	for(i=0; i
Packit c32a2d
		!strcasecmp(encdesc[i].name, name)
Packit c32a2d
	||	!strcasecmp(encdesc[i].longname, name)
Packit c32a2d
	)
Packit c32a2d
		return encdesc[i].code;
Packit c32a2d
	return OUT123_ERR;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
const char* attribute_align_arg out123_enc_name(int encoding)
Packit c32a2d
{
Packit c32a2d
	int i;
Packit c32a2d
	for(i=0; i
Packit c32a2d
		return encdesc[i].name;
Packit c32a2d
	return NULL;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
const char* attribute_align_arg out123_enc_longname(int encoding)
Packit c32a2d
{
Packit c32a2d
	int i;
Packit c32a2d
	for(i=0; i
Packit c32a2d
		return encdesc[i].longname;
Packit c32a2d
	return NULL;
Packit c32a2d
}