|
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 |
}
|