Built SDL2_image and _mixer static

This commit is contained in:
2022-09-30 15:49:16 -04:00
parent e2605bf6c1
commit 1dec4347e0
4473 changed files with 1964551 additions and 9 deletions

View File

@@ -0,0 +1,59 @@
# Module for non-recursive mpg123 build system.
include src/libout123/modules/Makemodule.am
# Precursor to the proper libout123.
# For now only wrapping the module loader or the legacy module.
noinst_LTLIBRARIES += src/libout123/libmodule.la
lib_LTLIBRARIES += src/libout123/libout123.la
src_libout123_libout123_la_SOURCES = \
src/libout123/libout123.c \
src/libout123/stringlists.h \
src/libout123/stringlists.c \
src/libout123/out123_int.h \
src/libout123/wav.c \
src/libout123/wav.h \
src/libout123/wavhead.h
if BUILD_BUFFER
src_libout123_libout123_la_SOURCES += \
src/libout123/buffer.c \
src/libout123/buffer.h \
src/libout123/xfermem.c \
src/libout123/xfermem.h
endif
src_libout123_libout123_la_LDFLAGS = \
-no-undefined -version-info @LIBOUT123_VERSION@ -export-symbols-regex '^out123_'
src_libout123_libout123_la_LIBADD = \
src/libout123/libmodule.la \
src/compat/libcompat.la
if !HAVE_MODULES
src_libout123_libout123_la_LIBADD += \
src/libout123/modules/libdefaultmodule.la
endif
src_libout123_libmodule_la_SOURCES = src/libout123/module.h
if HAVE_MODULES
src_libout123_libmodule_la_SOURCES += src/libout123/module.c
else
src_libout123_libmodule_la_SOURCES += src/libout123/legacy_module.c
endif
# The sfifo code is directly used in some modules.
EXTRA_DIST += \
src/libout123/out123.h.in \
src/libout123/sfifo.c \
src/libout123/sfifo.h
nodist_include_HEADERS += \
src/libout123/out123.h

View File

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

View File

@@ -0,0 +1,66 @@
/*
buffer.h: output buffer
copyright 1999-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Daniel Kobras / Oliver Fromme
*/
/*
* Application specific interaction between main and buffer
* process. This is much less generic than the functions in
* xfermem so I chose to put it in buffer.[hc].
* 01/28/99 [dk]
*/
#ifndef _MPG123_BUFFER_H_
#define _MPG123_BUFFER_H_
#include "out123_int.h"
#include "compat.h"
int buffer_init(out123_handle *ao, size_t bytes);
void buffer_exit(out123_handle *ao);
/* Messages with payload. */
int buffer_sync_param(out123_handle *ao);
int buffer_open(out123_handle *ao, const char* driver, const char* device);
int buffer_encodings(out123_handle *ao);
int buffer_formats( out123_handle *ao, const long *rates, int ratecount
, int minchannels, int maxchannels
, struct mpg123_fmt **fmtlist );
int buffer_start(out123_handle *ao);
void buffer_ndrain(out123_handle *ao, size_t bytes);
/* Simple messages to be deal with after playback. */
void buffer_stop(out123_handle *ao);
void buffer_close(out123_handle *ao);
void buffer_continue(out123_handle *ao);
/* Still undecided if that one is to be used anywhere. */
void buffer_ignore_lowmem(out123_handle *ao);
void buffer_drain(out123_handle *ao);
void buffer_end(out123_handle *ao);
/* Simple messages with interruption of playback. */
void buffer_pause(out123_handle *ao);
void buffer_drop(out123_handle *ao);
/* The actual work: Hand over audio data. */
size_t buffer_write(out123_handle *ao, void *buffer, size_t bytes);
/* Thin wrapper over xfermem giving the current buffer fill. */
size_t buffer_fill(out123_handle *ao);
/* Special handler to safely read values from command channel with
an additional buffer handed in. Exported for read_parameters(). */
int read_buf(int fd, void *addr, size_t size
, byte *prebuf, int *preoff, int presize);
/* Read/write strings from/to command channel. 0 on success. */
int xfer_write_string(out123_handle *ao, int who, const char *buf);
int xfer_read_string(out123_handle *ao, int who, char* *buf);
#endif

View File

@@ -0,0 +1,96 @@
/*
legacy_module.c: dummy interface to modular code loader for legacy build system
copyright 2008 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Nicholas J Humfrey
*/
#include "out123_int.h"
#include "debug.h"
/* A single module is staticly compiled in for each type */
extern mpg123_module_t mpg123_output_module_info;
/* extern mpg123_module_t mpg123_input_module_info; */
/* Open a module */
mpg123_module_t*
open_module(const char* type, const char* name, int verbose, const char *bindir)
{
mpg123_module_t *mod = NULL;
/* Select the module info structure, based on the desired type */
if (strcmp(type, "output")==0) {
mod = &mpg123_output_module_info;
/*
} else if (strcmp(type, "input")==0) {
mod = &mpg123_input_module_info;
*/
} else {
if(verbose >= 0)
error1("Unable to open module type '%s'.", type);
return NULL;
}
/* Check the module compiled in is the module requested */
if (strcmp(name, mod->name)!=0) {
if(verbose >= 0)
{
error1("Unable to open requested module '%s'.", name);
error1("The only available statically compiled module is '%s'."
, mod->name);
}
return NULL;
}
/* Debugging info */
debug1("Details of static module type '%s':", type);
debug1(" api_version=%d", mod->api_version);
debug1(" name=%s", mod->name);
debug1(" description=%s", mod->description);
debug1(" revision=%s", mod->revision);
debug1(" handle=%p", (void*)mod->handle);
return mod;
}
void close_module(mpg123_module_t* module, int verbose)
{
debug("close_module()");
/* Module was never really 'loaded', so nothing to do here. */
}
int list_modules(const char *type, char ***names, char ***descr, int verbose
, const char *bindir)
{
debug("list_modules()" );
*names = NULL;
*descr = NULL;
if(
(*names=malloc(sizeof(char*)))
&& !((*names)[0]=NULL) /* for safe cleanup */
&& ((*names)[0]=compat_strdup(mpg123_output_module_info.name))
&& (*descr=malloc(sizeof(char*)))
&& !((*descr)[0]=NULL) /* for safe cleanup */
&& ((*descr)[0]=compat_strdup(mpg123_output_module_info.description))
)
return 1;
else
{
if(*names)
free((*names)[0]);
free(*names);
if(*descr)
free((*descr)[0]);
free(*descr);
return -1;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,303 @@
/*
module.c: modular code loader
copyright 1995-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Nicholas J Humfrey
*/
/* Need snprintf(). */
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#include "config.h"
#include "intsym.h"
#include "stringlists.h"
#include "compat.h"
#include <errno.h>
#include "module.h"
#include "debug.h"
#ifndef USE_MODULES
#error This is a build without modules. Why am I here?
#endif
#define MODULE_SYMBOL_PREFIX "mpg123_"
#define MODULE_SYMBOL_SUFFIX "_module_info"
/* Windows code can convert these from UTF-8 (or ASCII, does not matter)
to wide and then replace / by \. No need to define another list. */
static const char* modulesearch[] =
{
"../lib/mpg123"
,"plugins"
,"libout123/modules/.libs"
,"libout123/modules"
,"../libout123/modules/.libs"
,"../libout123/modules"
};
static char *get_module_dir(int verbose, const char* bindir)
{
char *moddir = NULL;
char *defaultdir;
/* First the environment override, then relative to bindir, then installation prefix. */
defaultdir = compat_getenv("MPG123_MODDIR");
if(defaultdir)
{
if(verbose > 1)
fprintf(stderr, "Trying module directory from environment: %s\n", defaultdir);
if(compat_isdir(defaultdir))
moddir = defaultdir;
else
free(defaultdir);
}
else
{
if(bindir) /* Search relative to binary. */
{
size_t i;
if(verbose > 1)
fprintf(stderr, "Module dir search relative to: %s\n", bindir);
for(i=0; i<sizeof(modulesearch)/sizeof(char*); ++i)
{
moddir = compat_catpath(bindir, modulesearch[i]);
if(!moddir)
continue;
if(verbose > 1)
fprintf(stderr, "Looking for module dir: %s\n", moddir);
if(compat_isdir(moddir))
break; /* found it! */
else
{
free(moddir);
moddir=NULL;
}
}
}
if(!moddir) /* Resort to installation prefix. */
{
if(compat_isdir(PKGLIBDIR))
{
if(verbose > 1)
fprintf(stderr, "Using default module dir: %s\n", PKGLIBDIR);
moddir = compat_strdup(PKGLIBDIR);
}
}
}
if(verbose > 1)
fprintf(stderr, "Module dir: %s\n", moddir != NULL ? moddir : "<nil>");
return moddir;
}
/* Open a module in given directory. */
mpg123_module_t* open_module_here( const char *dir, const char* type
, const char* name, int verbose )
{
void *handle = NULL;
mpg123_module_t *module = NULL;
char *module_file = NULL;
size_t module_file_len = 0;
char *module_symbol = NULL;
size_t module_symbol_len = 0;
char *module_path = NULL;
/* Work out the path of the module to open */
module_file_len = strlen(type) + 1 + strlen(name) + strlen(LT_MODULE_EXT) + 1;
module_file = malloc(module_file_len);
if(!module_file)
{
if(verbose > -1)
error1( "Failed to allocate memory for module name: %s", strerror(errno) );
return NULL;
}
snprintf(module_file, module_file_len, "%s_%s%s", type, name, LT_MODULE_EXT);
module_path = compat_catpath(dir, module_file);
free(module_file);
if(!module_path)
{
if(verbose > -1)
error("Failed to construct full path (out of memory?).");
return NULL;
}
if(verbose > 1)
fprintf(stderr, "Module path: %s\n", module_path );
/* Open the module */
handle = compat_dlopen(module_path);
free(module_path);
if (handle==NULL)
{
if(verbose > -1)
error1("Failed to open module %s.", name);
return NULL;
}
/* Work out the symbol name */
module_symbol_len = strlen( MODULE_SYMBOL_PREFIX ) +
strlen( type ) +
strlen( MODULE_SYMBOL_SUFFIX ) + 1;
module_symbol = malloc(module_symbol_len);
if (module_symbol == NULL) {
if(verbose > -1)
error1( "Failed to allocate memory for module symbol: %s", strerror(errno) );
return NULL;
}
snprintf( module_symbol, module_symbol_len, "%s%s%s", MODULE_SYMBOL_PREFIX, type, MODULE_SYMBOL_SUFFIX );
debug1( "Module symbol: %s", module_symbol );
/* Get the information structure from the module */
module = (mpg123_module_t*)compat_dlsym(handle, module_symbol);
free( module_symbol );
if (module==NULL) {
if(verbose > -1)
error("Failed to get module symbol.");
return NULL;
}
/* Check the API version */
if (MPG123_MODULE_API_VERSION != module->api_version)
{
if(verbose > -1)
error2( "API version of module does not match (got %i, expected %i).", module->api_version, MPG123_MODULE_API_VERSION);
compat_dlclose(handle);
return NULL;
}
/* Store handle in the data structure */
module->handle = handle;
return module;
}
/* Open a module, including directory search. */
mpg123_module_t* open_module( const char* type, const char* name, int verbose
, const char* bindir )
{
mpg123_module_t *module = NULL;
char *moddir = NULL;
moddir = get_module_dir(verbose, bindir);
if(!moddir)
{
if(verbose > -1)
error("Failure getting module directory! (Perhaps set MPG123_MODDIR?)");
return NULL;
}
module = open_module_here(moddir, type, name, verbose);
free(moddir);
return module;
}
void close_module( mpg123_module_t* module, int verbose )
{
compat_dlclose(module->handle);
}
int list_modules( const char *type, char ***names, char ***descr, int verbose
, const char* bindir )
{
char *moddir = NULL;
int count = 0;
struct compat_dir *dir;
char *filename;
debug1("verbose:%i", verbose);
*names = NULL;
*descr = NULL;
moddir = get_module_dir(verbose, bindir);
if(moddir == NULL)
{
if(verbose > -1)
error("Failure getting module directory! (Perhaps set MPG123_MODDIR?)");
return -1;
}
debug1("module dir: %s", moddir);
/* Open the module directory */
dir = compat_diropen(moddir);
if (dir==NULL) {
if(verbose > -1)
error2("Failed to open the module directory (%s): %s\n"
, moddir, strerror(errno));
free(moddir);
return -1;
}
while((filename=compat_nextfile(dir)))
{
/* Pointers to the pieces. */
char *module_name = NULL;
char *module_type = NULL;
char *uscore_pos = NULL;
mpg123_module_t *module = NULL;
char* ext;
size_t name_len;
/* Various checks as loop shortcuts, avoiding too much nesting. */
debug1("checking entry: %s", filename);
name_len = strlen(filename);
if(name_len < strlen(LT_MODULE_EXT))
goto list_modules_continue;
ext = filename
+ name_len
- strlen(LT_MODULE_EXT);
if(strcmp(ext, LT_MODULE_EXT))
goto list_modules_continue;
debug("has suffix");
/* Extract the module type and name */
uscore_pos = strchr( filename, '_' );
if( uscore_pos==NULL
|| (uscore_pos>=filename+name_len+1) )
{
debug("no underscore");
goto list_modules_continue;
}
*uscore_pos = '\0';
module_type = filename;
module_name = uscore_pos+1;
/* Only list modules of desired type. */
if(strcmp(type, module_type))
{
debug("wrong type");
goto list_modules_continue;
}
debug("has type");
/* Extract the short name of the module */
name_len -= uscore_pos - filename + 1;
if(name_len <= strlen(LT_MODULE_EXT))
{
debug("name too short");
goto list_modules_continue;
}
name_len -= strlen(LT_MODULE_EXT);
module_name[name_len] = '\0';
debug("opening module");
/* Open the module
Yes, this re-builds the file name we chopped to pieces just now. */
if((module=open_module_here(moddir, module_type, module_name, verbose)))
{
if( stringlists_add( names, descr
, module->name, module->description, &count) )
if(verbose > -1)
error("OOM");
/* Close the module again */
close_module(module, verbose);
}
list_modules_continue:
free(filename);
}
compat_dirclose(dir);
return count;
}

View File

@@ -0,0 +1,44 @@
/*
module: module loading and listing interface
copyright ?-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Nicholas J. Humphfrey
*/
#ifndef _MPG123_MODULE_H_
#define _MPG123_MODULE_H_
#include "out123.h"
/* TODO: put that into out123_int.h instead? */
#define MPG123_MODULE_API_VERSION (2)
/* The full structure is delared in audio.h */
struct audio_output_struct;
typedef struct mpg123_module_struct {
const int api_version; /* module API version number */
const char* name; /* short name of the module */
const char* description; /* description of what the module does */
const char* revision; /* source code revision */
void* handle; /* dynamic loader handle */
/* Initialisers - set to NULL if unsupported by module */
int (*init_output)(out123_handle *ao); /* audio output - returns 0 on success */
} mpg123_module_t;
/* ------ Declarations from "module.c" ------ */
mpg123_module_t* open_module( const char* type, const char* name, int verbose
, const char* bindir );
void close_module(mpg123_module_t* module, int verbose);
int list_modules( const char *type, char ***names, char ***descr, int verbose
, const char* bindir );
#endif

View File

@@ -0,0 +1,791 @@
# Module for non-recursive mpg123 build system.
# Gah! Not even re-defining that variable is allowed in automake!
# I WANT TO USE PROPER MAKE!
# makedir := src/libout123/modules
# Experiment: Does automake pick that up in a Make variable?
# Damn, no! It complains wildly.
# I just want to use GNU Make and be done with it!
# Perhaps the next build system rewrite ...
#makenam=src_libout123_modules
# Optionally containing the one static module to use.
if !HAVE_MODULES
noinst_LTLIBRARIES += src/libout123/modules/libdefaultmodule.la
endif
# Do not include uneeded headers from mpg123app.h .
libout123_mod_cppflags = -DBUILDING_OUTPUT_MODULES=1
# These are not tested and _very_ likely need work: aix alib hp os2 sgi mint
# Use that sh/perl script to generate the module entries:
# Confused as to when to use _LIBADD and when _LDADD.
# _LDADD gives errors from autotools.
#echo \
#dummy tinyalsa alsa qsa coreaudio esd jack nas oss portaudio \
#pulse sdl sndio sun win32 win32_wasapi aix alib arts hp os2 \
#sgi mint openal \
#| tr ' ' '\n' |
#perl -ne 'chomp; $big = uc($_); print <<EOT;
#
#if HAVE_MODULES
#if HAVE_$big
#pkglib_LTLIBRARIES += \src/libout123/modules/output_$_.la
#src_libout123_modules_output_${_}_la_SOURCES = \\
# src/libout123/modules/$_.c
#src_libout123_modules_output_${_}_la_LDFLAGS = \\
# -module -no-undefined -avoid-version \\
# -export-dynamic -export-symbols-regex '"'"'^mpg123_'"'"' \\
# \@${big}_LDFLAGS\@
#src_libout123_modules_output_${_}_la_CFLAGS = \@${big}_CFLAGS\@
#src_libout123_modules_output_${_}_la_LIBADD = \\
# src/compat/libcompat_str.la \\
# \@${big}_LIBS\@
#src_libout123_modules_outout_${_}_la_CPPFLAGS = \\
# \$(AM_CPPFLAGS) \\
# \$(libout123_mod_cppflags)
#endif
#else
#if BUILD_$big
#src_libout123_modules_libdefaultmodule_la_SOURCES = \\
# src/libout123/modules/$_.c
#src_libout123_modules_libdefaultmodule_la_CFLAGS = \@${big}_CFLAGS\@
#src_libout123_modules_libdefaultmodule_la_LDFLAGS = \@${big}_LDFLAGS\@
#src_libout123_modules_libdefaultmodule_la_LIBADD = \@${big}_LIBS\@
#src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \\
# \$(AM_CPPFLAGS) \\
# \$(libout123_mod_cppflags)
#endif
#endif
#EOT
#'
if HAVE_MODULES
if HAVE_DUMMY
pkglib_LTLIBRARIES += src/libout123/modules/output_dummy.la
src_libout123_modules_output_dummy_la_SOURCES = \
src/libout123/modules/dummy.c
src_libout123_modules_output_dummy_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@DUMMY_LDFLAGS@
src_libout123_modules_output_dummy_la_CFLAGS = @DUMMY_CFLAGS@
src_libout123_modules_output_dummy_la_LIBADD = \
src/compat/libcompat_str.la \
@DUMMY_LIBS@
src_libout123_modules_outout_dummy_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_DUMMY
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/dummy.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @DUMMY_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @DUMMY_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @DUMMY_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_TINYALSA
pkglib_LTLIBRARIES += src/libout123/modules/output_tinyalsa.la
src_libout123_modules_output_tinyalsa_la_SOURCES = \
src/libout123/modules/tinyalsa.c
src_libout123_modules_output_tinyalsa_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@TINYALSA_LDFLAGS@
src_libout123_modules_output_tinyalsa_la_CFLAGS = @TINYALSA_CFLAGS@
src_libout123_modules_output_tinyalsa_la_LIBADD = \
src/compat/libcompat_str.la \
@TINYALSA_LIBS@
src_libout123_modules_outout_tinyalsa_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_TINYALSA
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/tinyalsa.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @TINYALSA_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @TINYALSA_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @TINYALSA_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_ALSA
pkglib_LTLIBRARIES += src/libout123/modules/output_alsa.la
src_libout123_modules_output_alsa_la_SOURCES = \
src/libout123/modules/alsa.c
src_libout123_modules_output_alsa_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@ALSA_LDFLAGS@
src_libout123_modules_output_alsa_la_CFLAGS = @ALSA_CFLAGS@
src_libout123_modules_output_alsa_la_LIBADD = \
src/compat/libcompat_str.la \
@ALSA_LIBS@
src_libout123_modules_outout_alsa_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_ALSA
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/alsa.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @ALSA_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @ALSA_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @ALSA_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_QSA
pkglib_LTLIBRARIES += src/libout123/modules/output_qsa.la
src_libout123_modules_output_qsa_la_SOURCES = \
src/libout123/modules/qsa.c
src_libout123_modules_output_qsa_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@QSA_LDFLAGS@
src_libout123_modules_output_qsa_la_CFLAGS = @QSA_CFLAGS@
src_libout123_modules_output_qsa_la_LIBADD = \
src/compat/libcompat_str.la \
@QSA_LIBS@
src_libout123_modules_outout_qsa_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_QSA
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/qsa.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @QSA_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @QSA_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @QSA_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_COREAUDIO
pkglib_LTLIBRARIES += src/libout123/modules/output_coreaudio.la
src_libout123_modules_output_coreaudio_la_SOURCES = \
src/libout123/modules/coreaudio.c
src_libout123_modules_output_coreaudio_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@COREAUDIO_LDFLAGS@
src_libout123_modules_output_coreaudio_la_CFLAGS = @COREAUDIO_CFLAGS@
src_libout123_modules_output_coreaudio_la_LIBADD = \
src/compat/libcompat_str.la \
@COREAUDIO_LIBS@
src_libout123_modules_outout_coreaudio_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_COREAUDIO
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/coreaudio.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @COREAUDIO_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @COREAUDIO_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @COREAUDIO_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_ESD
pkglib_LTLIBRARIES += src/libout123/modules/output_esd.la
src_libout123_modules_output_esd_la_SOURCES = \
src/libout123/modules/esd.c
src_libout123_modules_output_esd_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@ESD_LDFLAGS@
src_libout123_modules_output_esd_la_CFLAGS = @ESD_CFLAGS@
src_libout123_modules_output_esd_la_LIBADD = \
src/compat/libcompat_str.la \
@ESD_LIBS@
src_libout123_modules_outout_esd_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_ESD
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/esd.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @ESD_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @ESD_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @ESD_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_JACK
pkglib_LTLIBRARIES += src/libout123/modules/output_jack.la
src_libout123_modules_output_jack_la_SOURCES = \
src/libout123/modules/jack.c
src_libout123_modules_output_jack_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@JACK_LDFLAGS@
src_libout123_modules_output_jack_la_CFLAGS = @JACK_CFLAGS@
src_libout123_modules_output_jack_la_LIBADD = \
src/compat/libcompat_str.la \
@JACK_LIBS@
src_libout123_modules_outout_jack_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_JACK
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/jack.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @JACK_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @JACK_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @JACK_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_NAS
pkglib_LTLIBRARIES += src/libout123/modules/output_nas.la
src_libout123_modules_output_nas_la_SOURCES = \
src/libout123/modules/nas.c
src_libout123_modules_output_nas_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@NAS_LDFLAGS@
src_libout123_modules_output_nas_la_CFLAGS = @NAS_CFLAGS@
src_libout123_modules_output_nas_la_LIBADD = \
src/compat/libcompat_str.la \
@NAS_LIBS@
src_libout123_modules_outout_nas_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_NAS
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/nas.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @NAS_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @NAS_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @NAS_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_OSS
pkglib_LTLIBRARIES += src/libout123/modules/output_oss.la
src_libout123_modules_output_oss_la_SOURCES = \
src/libout123/modules/oss.c
src_libout123_modules_output_oss_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@OSS_LDFLAGS@
src_libout123_modules_output_oss_la_CFLAGS = @OSS_CFLAGS@
src_libout123_modules_output_oss_la_LIBADD = \
src/compat/libcompat_str.la \
@OSS_LIBS@
src_libout123_modules_outout_oss_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_OSS
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/oss.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @OSS_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @OSS_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @OSS_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_PORTAUDIO
pkglib_LTLIBRARIES += src/libout123/modules/output_portaudio.la
src_libout123_modules_output_portaudio_la_SOURCES = \
src/libout123/modules/portaudio.c
src_libout123_modules_output_portaudio_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@PORTAUDIO_LDFLAGS@
src_libout123_modules_output_portaudio_la_CFLAGS = @PORTAUDIO_CFLAGS@
src_libout123_modules_output_portaudio_la_LIBADD = \
src/compat/libcompat_str.la \
@PORTAUDIO_LIBS@
src_libout123_modules_outout_portaudio_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_PORTAUDIO
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/portaudio.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @PORTAUDIO_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @PORTAUDIO_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @PORTAUDIO_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_PULSE
pkglib_LTLIBRARIES += src/libout123/modules/output_pulse.la
src_libout123_modules_output_pulse_la_SOURCES = \
src/libout123/modules/pulse.c
src_libout123_modules_output_pulse_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@PULSE_LDFLAGS@
src_libout123_modules_output_pulse_la_CFLAGS = @PULSE_CFLAGS@
src_libout123_modules_output_pulse_la_LIBADD = \
src/compat/libcompat_str.la \
@PULSE_LIBS@
src_libout123_modules_outout_pulse_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_PULSE
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/pulse.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @PULSE_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @PULSE_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @PULSE_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_SDL
pkglib_LTLIBRARIES += src/libout123/modules/output_sdl.la
src_libout123_modules_output_sdl_la_SOURCES = \
src/libout123/modules/sdl.c
src_libout123_modules_output_sdl_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@SDL_LDFLAGS@
src_libout123_modules_output_sdl_la_CFLAGS = @SDL_CFLAGS@
src_libout123_modules_output_sdl_la_LIBADD = \
src/compat/libcompat_str.la \
@SDL_LIBS@
src_libout123_modules_outout_sdl_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_SDL
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/sdl.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @SDL_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @SDL_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @SDL_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_SNDIO
pkglib_LTLIBRARIES += src/libout123/modules/output_sndio.la
src_libout123_modules_output_sndio_la_SOURCES = \
src/libout123/modules/sndio.c
src_libout123_modules_output_sndio_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@SNDIO_LDFLAGS@
src_libout123_modules_output_sndio_la_CFLAGS = @SNDIO_CFLAGS@
src_libout123_modules_output_sndio_la_LIBADD = \
src/compat/libcompat_str.la \
@SNDIO_LIBS@
src_libout123_modules_outout_sndio_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_SNDIO
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/sndio.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @SNDIO_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @SNDIO_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @SNDIO_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_SUN
pkglib_LTLIBRARIES += src/libout123/modules/output_sun.la
src_libout123_modules_output_sun_la_SOURCES = \
src/libout123/modules/sun.c
src_libout123_modules_output_sun_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@SUN_LDFLAGS@
src_libout123_modules_output_sun_la_CFLAGS = @SUN_CFLAGS@
src_libout123_modules_output_sun_la_LIBADD = \
src/compat/libcompat_str.la \
@SUN_LIBS@
src_libout123_modules_outout_sun_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_SUN
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/sun.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @SUN_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @SUN_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @SUN_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_WIN32
pkglib_LTLIBRARIES += src/libout123/modules/output_win32.la
src_libout123_modules_output_win32_la_SOURCES = \
src/libout123/modules/win32.c
src_libout123_modules_output_win32_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@WIN32_LDFLAGS@
src_libout123_modules_output_win32_la_CFLAGS = @WIN32_CFLAGS@
src_libout123_modules_output_win32_la_LIBADD = \
src/compat/libcompat_str.la \
@WIN32_LIBS@
src_libout123_modules_outout_win32_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_WIN32
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/win32.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @WIN32_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @WIN32_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @WIN32_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_WIN32_WASAPI
pkglib_LTLIBRARIES += src/libout123/modules/output_win32_wasapi.la
src_libout123_modules_output_win32_wasapi_la_SOURCES = \
src/libout123/modules/win32_wasapi.c
src_libout123_modules_output_win32_wasapi_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@WIN32_WASAPI_LDFLAGS@
src_libout123_modules_output_win32_wasapi_la_CFLAGS = @WIN32_WASAPI_CFLAGS@
src_libout123_modules_output_win32_wasapi_la_LIBADD = \
src/compat/libcompat_str.la \
@WIN32_WASAPI_LIBS@
src_libout123_modules_outout_win32_wasapi_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_WIN32_WASAPI
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/win32_wasapi.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @WIN32_WASAPI_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @WIN32_WASAPI_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @WIN32_WASAPI_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_AIX
pkglib_LTLIBRARIES += src/libout123/modules/output_aix.la
src_libout123_modules_output_aix_la_SOURCES = \
src/libout123/modules/aix.c
src_libout123_modules_output_aix_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@AIX_LDFLAGS@
src_libout123_modules_output_aix_la_CFLAGS = @AIX_CFLAGS@
src_libout123_modules_output_aix_la_LIBADD = \
src/compat/libcompat_str.la \
@AIX_LIBS@
src_libout123_modules_outout_aix_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_AIX
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/aix.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @AIX_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @AIX_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @AIX_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_ALIB
pkglib_LTLIBRARIES += src/libout123/modules/output_alib.la
src_libout123_modules_output_alib_la_SOURCES = \
src/libout123/modules/alib.c
src_libout123_modules_output_alib_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@ALIB_LDFLAGS@
src_libout123_modules_output_alib_la_CFLAGS = @ALIB_CFLAGS@
src_libout123_modules_output_alib_la_LIBADD = \
src/compat/libcompat_str.la \
@ALIB_LIBS@
src_libout123_modules_outout_alib_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_ALIB
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/alib.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @ALIB_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @ALIB_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @ALIB_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_ARTS
pkglib_LTLIBRARIES += src/libout123/modules/output_arts.la
src_libout123_modules_output_arts_la_SOURCES = \
src/libout123/modules/arts.c
src_libout123_modules_output_arts_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@ARTS_LDFLAGS@
src_libout123_modules_output_arts_la_CFLAGS = @ARTS_CFLAGS@
src_libout123_modules_output_arts_la_LIBADD = \
src/compat/libcompat_str.la \
@ARTS_LIBS@
src_libout123_modules_outout_arts_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_ARTS
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/arts.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @ARTS_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @ARTS_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @ARTS_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_HP
pkglib_LTLIBRARIES += src/libout123/modules/output_hp.la
src_libout123_modules_output_hp_la_SOURCES = \
src/libout123/modules/hp.c
src_libout123_modules_output_hp_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@HP_LDFLAGS@
src_libout123_modules_output_hp_la_CFLAGS = @HP_CFLAGS@
src_libout123_modules_output_hp_la_LIBADD = \
src/compat/libcompat_str.la \
@HP_LIBS@
src_libout123_modules_outout_hp_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_HP
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/hp.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @HP_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @HP_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @HP_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_OS2
pkglib_LTLIBRARIES += src/libout123/modules/output_os2.la
src_libout123_modules_output_os2_la_SOURCES = \
src/libout123/modules/os2.c
src_libout123_modules_output_os2_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@OS2_LDFLAGS@
src_libout123_modules_output_os2_la_CFLAGS = @OS2_CFLAGS@
src_libout123_modules_output_os2_la_LIBADD = \
src/compat/libcompat_str.la \
@OS2_LIBS@
src_libout123_modules_outout_os2_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_OS2
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/os2.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @OS2_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @OS2_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @OS2_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_SGI
pkglib_LTLIBRARIES += src/libout123/modules/output_sgi.la
src_libout123_modules_output_sgi_la_SOURCES = \
src/libout123/modules/sgi.c
src_libout123_modules_output_sgi_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@SGI_LDFLAGS@
src_libout123_modules_output_sgi_la_CFLAGS = @SGI_CFLAGS@
src_libout123_modules_output_sgi_la_LIBADD = \
src/compat/libcompat_str.la \
@SGI_LIBS@
src_libout123_modules_outout_sgi_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_SGI
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/sgi.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @SGI_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @SGI_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @SGI_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_MINT
pkglib_LTLIBRARIES += src/libout123/modules/output_mint.la
src_libout123_modules_output_mint_la_SOURCES = \
src/libout123/modules/mint.c
src_libout123_modules_output_mint_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@MINT_LDFLAGS@
src_libout123_modules_output_mint_la_CFLAGS = @MINT_CFLAGS@
src_libout123_modules_output_mint_la_LIBADD = \
src/compat/libcompat_str.la \
@MINT_LIBS@
src_libout123_modules_outout_mint_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_MINT
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/mint.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @MINT_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @MINT_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @MINT_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
if HAVE_OPENAL
pkglib_LTLIBRARIES += src/libout123/modules/output_openal.la
src_libout123_modules_output_openal_la_SOURCES = \
src/libout123/modules/openal.c
src_libout123_modules_output_openal_la_LDFLAGS = \
-module -no-undefined -avoid-version \
-export-dynamic -export-symbols-regex '^mpg123_' \
@OPENAL_LDFLAGS@
src_libout123_modules_output_openal_la_CFLAGS = @OPENAL_CFLAGS@
src_libout123_modules_output_openal_la_LIBADD = \
src/compat/libcompat_str.la \
@OPENAL_LIBS@
src_libout123_modules_outout_openal_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
else
if BUILD_OPENAL
src_libout123_modules_libdefaultmodule_la_SOURCES = \
src/libout123/modules/openal.c
src_libout123_modules_libdefaultmodule_la_CFLAGS = @OPENAL_CFLAGS@
src_libout123_modules_libdefaultmodule_la_LDFLAGS = @OPENAL_LDFLAGS@
src_libout123_modules_libdefaultmodule_la_LIBADD = @OPENAL_LIBS@
src_libout123_modules_libdefaultmodule_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(libout123_mod_cppflags)
endif
endif
if HAVE_MODULES
# Get rid of .la files, at least _after_ install.
install-exec-hook:
cd $(DESTDIR)$(pkglibdir) && rm -f @output_modules_la@
# The above breaks uninstall of module .so files?
uninstall-hook:
for m in @output_modules_la@; do eval $$(grep dlname= src/libout123/modules/$$m) && rm -f $(DESTDIR)$(pkglibdir)/$$dlname; done
endif

View File

@@ -0,0 +1,304 @@
/*
aix: Driver for IBM RS/6000 with AIX Ultimedia Services
copyright ?-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Juergen Schoew and Tomas Oegren
*/
#include "out123_int.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/audio.h>
#include <stropts.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/param.h>
#include "debug.h"
/* use AUDIO_BSIZE to set the msec for audio buffering in Ultimedia library
*/
/* #define AUDIO_BSIZE AUDIO_IGNORE */
#define AUDIO_BSIZE ( ao->device_buffer > 0. \
? (long)(ao->device_buffer*1000) \
: 200 )
static int rate_best_match(out123_handle *ao)
{
static long valid [ ] = { 5510, 6620, 8000, 9600, 11025, 16000, 18900,
22050, 27420, 32000, 33075, 37800, 44100, 48000, 0 };
int i = 0;
long best = 8000;
if(!ao || ao->fn < 0 || ao->rate < 0) {
return -1;
}
while (valid [i]) {
if (abs(valid[i] - ao->rate) < abs(best - ao->rate))
{
best = valid [i];
}
i = i + 1;
}
ao->rate = best;
return best;
}
static int reset_parameters(out123_handle *ao)
{
audio_control acontrol;
audio_change achange;
audio_init ainit;
int ret;
memset ( & achange, '\0', sizeof (achange));
memset ( & acontrol, '\0', sizeof (acontrol));
achange.balance = 0x3fff0000;
achange.balance_delay = 0;
achange.volume = (long) (0x7fff << 16);
achange.volume_delay = 0;
achange.input = AUDIO_IGNORE;
if (ao->flags == -1) achange.output = INTERNAL_SPEAKER;
else achange.output = 0;
if(ao->flags & OUT123_INTERNAL_SPEAKER)
achange.output |= INTERNAL_SPEAKER;
if(ao->flags & OUT123_HEADPHONES)
achange.output |= EXTERNAL_SPEAKER;
if(ao->flags & OUT123_LINE_OUT)
achange.output |= OUTPUT_1;
if(ao->flags == 0)
achange.output = AUDIO_IGNORE;
achange.treble = AUDIO_IGNORE;
achange.bass = AUDIO_IGNORE;
achange.pitch = AUDIO_IGNORE;
achange.monitor = AUDIO_IGNORE;
achange.dev_info = (char *) NULL;
acontrol.ioctl_request = AUDIO_CHANGE;
acontrol.position = 0;
acontrol.request_info = (char *) & achange;
ret = ioctl (ao->fn, AUDIO_CONTROL, & acontrol);
if (ret < 0)
return ret;
/* Init Device for new values */
if (ao->rate >0) {
memset ( & ainit, '\0', sizeof (ainit));
ainit.srate = rate_best_match(ao);
if (ao->channels > 0)
ainit.channels = ao->channels;
else
ainit.channels = 1;
switch (ao->format) {
default :
ainit.mode = PCM;
ainit.bits_per_sample = 8;
ainit.flags = BIG_ENDIAN | TWOS_COMPLEMENT;
break;
case MPG123_ENC_SIGNED_16:
ainit.mode = PCM;
ainit.bits_per_sample = 16;
ainit.flags = BIG_ENDIAN | TWOS_COMPLEMENT;
break;
case MPG123_ENC_SIGNED_8:
ainit.mode = PCM;
ainit.bits_per_sample = 8;
ainit.flags = BIG_ENDIAN | TWOS_COMPLEMENT;
break;
case MPG123_ENC_UNSIGNED_16:
ainit.mode = PCM;
ainit.bits_per_sample = 16;
ainit.flags = BIG_ENDIAN | TWOS_COMPLEMENT | SIGNED;
break;
case MPG123_ENC_UNSIGNED_8:
ainit.mode = PCM;
ainit.bits_per_sample = 8;
ainit.flags = BIG_ENDIAN | TWOS_COMPLEMENT | SIGNED;
break;
case MPG123_ENC_ULAW_8:
ainit.mode = MU_LAW;
ainit.bits_per_sample = 8;
ainit.flags = BIG_ENDIAN | TWOS_COMPLEMENT;
break;
case MPG123_ENC_ALAW_8:
ainit.mode = A_LAW;
ainit.bits_per_sample = 8;
ainit.flags = BIG_ENDIAN | TWOS_COMPLEMENT;
break;
}
ainit.operation = PLAY;
ainit.bsize = AUDIO_BSIZE;
ret = ioctl (ao->fn, AUDIO_INIT, & ainit);
if (ret < 0) {
error("Can't set new audio parameters!");
return ret;
}
}
acontrol.ioctl_request = AUDIO_START;
acontrol.request_info = NULL;
acontrol.position = 0;
ret = ioctl (ao->fn, AUDIO_CONTROL, & acontrol);
if (ret < 0) {
error("Can't reset audio!");
return ret;
}
return 0;
}
static int open_aix(out123_handle *ao)
{
audio_init ainit;
int ret;
const char *dev = ao->device;
if(!dev) {
if(getenv("AUDIODEV")) {
dev = getenv("AUDIODEV");
ao->fn = open(dev,O_WRONLY);
} else {
dev = "/dev/paud0/1"; /* paud0 for PCI */
ao->fn = open(dev,O_WRONLY);
if ((ao->fn == -1) & (errno == ENOENT)) {
dev = "/dev/baud0/1"; /* baud0 for MCA */
ao->fn = open(dev,O_WRONLY);
}
}
} else ao->fn = open(dev,O_WRONLY);
if(ao->fn < 0) {
error("Can't open audio device!");
return ao->fn;
}
/* Init to default values */
memset ( & ainit, '\0', sizeof (ainit));
ainit.srate = 44100;
ainit.channels = 2;
ainit.mode = PCM;
ainit.bits_per_sample = 16;
ainit.flags = BIG_ENDIAN | TWOS_COMPLEMENT;
ainit.operation = PLAY;
ainit.bsize = AUDIO_BSIZE;
ret = ioctl (ao->fn, AUDIO_INIT, & ainit);
if (ret < 0) return ret;
reset_parameters(ao);
return ao->fn;
}
static int get_formats_aix(out123_handle *ao)
{
/* ULTIMEDIA DOCUMENTATION SAYS:
The Ultimedia Audio Adapter supports fourteen sample rates you can use to
capture and playback audio data. The rates are (in kHz): 5.51, 6.62, 8.0,
9.6, 11.025, 16.0, 18.9, 22.050, 27.42, 32.0, 33.075, 37.8, 44.1, and 48.0.
These rates are supported for mono and stereo PCM (8- and 16-bit), mu-law,
and A-law.
*/
long rate;
rate = ao->rate;
rate_best_match(ao);
if (ao->rate == rate)
return (MPG123_ENC_SIGNED_16|MPG123_ENC_UNSIGNED_16|
MPG123_ENC_UNSIGNED_8|MPG123_ENC_SIGNED_8|
MPG123_ENC_ULAW_8|MPG123_ENC_ALAW_8);
else
return 0;
}
static int write_aix(out123_handle *ao,unsigned char *buf,int len)
{
return write(ao->fn,buf,len);
}
static int close_aix(out123_handle *ao)
{
audio_control acontrol;
audio_buffer abuffer;
int ret,i;
/* Don't close the audio-device until it's played all its contents */
memset ( & acontrol, '\0', sizeof ( acontrol ) );
acontrol.request_info = &abuffer;
acontrol.position = 0;
i=50; /* Don't do this forever on a bad day :-) */
while (i-- > 0) {
if ((ioctl(ao->fn, AUDIO_BUFFER, &acontrol))< 0) {
error1("buffer read failed: %d", errno);
break;
} else {
if (abuffer.flags <= 0) break;
}
usleep(200000); /* sleep 0.2 sec */
}
memset ( & acontrol, '\0', sizeof ( acontrol ) );
acontrol.ioctl_request = AUDIO_STOP;
acontrol.request_info = NULL;
acontrol.position = 0;
ret = ioctl ( ao->fn, AUDIO_CONTROL, & acontrol );
if (ret < 0) error("Can't close audio!");
ret = close (ao->fn);
if (ret < 0) error("Can't close audio!");
return 0;
}
static void flush_aix(out123_handle *ao)
{
}
static int init_aix(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_aix;
ao->flush = flush_aix;
ao->write = write_aix;
ao->get_formats = get_formats_aix;
ao->close = close_aix;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "aix",
/* description */ "Output audio on IBM RS/6000 with AIX Ultimedia Services.",
/* revision */ "$Rev: 932 $",
/* handle */ NULL,
/* init_output */ init_aix,
};

View File

@@ -0,0 +1,209 @@
/*
alib: audio output for HP-UX using alib
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Erwan Ducroquet
based on source code from HP (Audio SDK)
*/
/*
*
* for mpg123 :
* hpux:
* $(MAKE) \
* CC=cc \
* LDFLAGS=-L/opt/audio/lib \
* AUDIO_LIB=-lAlib \
* OBJECTS=decode.o dct64.o \
* CFLAGS=-Ae +O3 -DREAL_IS_FLOAT -D_HPUX_SOURCE -DHPUX -I/opt/audio/include \
* mpg123
*/
/*
* For the user :
* If you launch mpg123 on a XTerm with sound capabilities, it's OK
* Else, you have to set the environment variable "AUDIO" to the name of
* an HP Xterm with sound card.
*/
/**************************************************************************/
#include "out123_int.h"
#include <fcntl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <Alib.h> /* /opt/audio/include */
#include <CUlib.h> /* /opt/audio/include */
#include "debug.h"
/**************************************************************************/
/* FIXME: These globals should be moved into a structure */
static Audio *audioServer = (Audio *) NULL;
static struct protoent *tcpProtocolEntry;
static ATransID xid;
static void printAudioError(Audio *audio,char *message,int errorCode) {
char errorbuff[132];
AGetErrorText(audio, errorCode, errorbuff, 131);
error2("%s: %s", message, errorbuff);
}
static long myHandler(Audio *audio,AErrorEvent *err_event) {
printAudioError( audio, "Audio error", err_event->error_code );
/* we cannot just do random exists, that messes terminal up
need proper error propagation in that case for future, setting intflag or such */
/* exit(1); */
}
/**************************************************************************/
/*
* Set the fn element of ai
* Use ao->rate and ao->channels
* Doesn't set any volume
*/
/* return on error leaves stuff dirty here... */
static int open_alib(out123_handle *ao)
{
AudioAttributes Attribs;
AudioAttrMask AttribsMask;
AGainEntry gainEntry[4];
SSPlayParams playParams;
SStream audioStream;
AErrorHandler prevHandler;
char server[1];
int i;
long status;
if (audioServer) {
error("openAudio: audio already open");
return -1;
}
prevHandler = ASetErrorHandler(myHandler);
server[0] = '\0';
audioServer = AOpenAudio( server, NULL );
if (audioServer==NULL) {
error("Error: could not open audio\n");
return -1;
}
ao->fn = socket( AF_INET, SOCK_STREAM, 0 );
if(ao->fn<0) {
error("Socket creation failed");
return -1;
}
Attribs.type = ATSampled;
Attribs.attr.sampled_attr.sampling_rate = ao->rate;
Attribs.attr.sampled_attr.channels = ao->channels;
Attribs.attr.sampled_attr.data_format = ADFLin16;
AttribsMask = ASSamplingRateMask | ASChannelsMask | ASDataFormatMask;
gainEntry[0].gain = AUnityGain;
gainEntry[0].u.o.out_ch = AOCTMono;
gainEntry[0].u.o.out_dst = AODTDefaultOutput;
playParams.gain_matrix.type = AGMTOutput; /* gain matrix */
playParams.gain_matrix.num_entries = 1;
playParams.gain_matrix.gain_entries = gainEntry;
playParams.play_volume = AUnityGain; /* play volume */
playParams.priority = APriorityNormal; /* normal priority */
playParams.event_mask = 0; /* don't solicit any events */
xid=APlaySStream(audioServer,AttribsMask,&Attribs,
&playParams,&audioStream,NULL);
status=connect(ao->fn,
(struct sockaddr *) &audioStream.tcp_sockaddr,
sizeof(struct sockaddr_in) );
if (status<0) {
error("Connect failed");
return -1;
}
i=-1;
tcpProtocolEntry=getprotobyname("tcp");
setsockopt(ao->fn,tcpProtocolEntry->p_proto,TCP_NODELAY,&i,sizeof(i));
return ao->fn;
}
/**************************************************************************/
static int close_alib(out123_handle *ao)
{
close(ao->fn);
ASetCloseDownMode( audioServer, AKeepTransactions, NULL );
ACloseAudio( audioServer, NULL );
audioServer = (Audio *) NULL;
return 0;
}
/**************************************************************************/
/*
* very simple
* deserv to be inline
*/
static int write_alib(out123_handle *ao,unsigned char *buf,int len)
{
return write(ao->fn,buf,len*2);
}
/**************************************************************************/
static int get_formats_alib(out123_handle *ao)
{
return MPG123_ENC_SIGNED_16;
}
static void flush_alib(out123_handle *ao)
{
}
static int init_alib(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_alib;
ao->flush = flush_alib;
ao->write = write_alib;
ao->get_formats = get_formats_alib;
ao->close = close_alib;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "alib",
/* description */ "Output audio HP-UX using alib.",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_alib,
};

View File

@@ -0,0 +1,309 @@
/*
alsa: sound output with Advanced Linux Sound Architecture 1.x API
copyright 2006-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Clemens Ladisch <clemens@ladisch.de>
*/
/* ALSA headers define struct timeval if no POSIX macro is set,
nicely in conflict with definitions in system headers. They had
a discussion about that a long time ago:
http://mailman.alsa-project.org/pipermail/alsa-devel/2007-June/001684.html
... seems like the conclusion was not carried through.
*/
#define _POSIX_SOURCE
/* Things are still missing if _DEFAULT_SOURCE is not defined (for recent
glibc, I presume. */
#define _DEFAULT_SOURCE
#include "out123_int.h"
#include <errno.h>
/* make ALSA 0.9.x compatible to the 1.0.x API */
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alloca.h> /* GCC complains about missing declaration of alloca. */
#include <alsa/asoundlib.h>
#include "debug.h"
/* Total buffer size in seconds, 0.2 is more true to what ALSA maximally uses
here (8192 samples). The earlier default of 0.5 was never true. */
#define BUFFER_LENGTH (ao->device_buffer > 0. ? ao->device_buffer : 0.2)
static const struct {
snd_pcm_format_t alsa;
int mpg123;
} format_map[] = {
{ SND_PCM_FORMAT_S16, MPG123_ENC_SIGNED_16 },
{ SND_PCM_FORMAT_U16, MPG123_ENC_UNSIGNED_16 },
{ SND_PCM_FORMAT_U8, MPG123_ENC_UNSIGNED_8 },
{ SND_PCM_FORMAT_S8, MPG123_ENC_SIGNED_8 },
{ SND_PCM_FORMAT_A_LAW, MPG123_ENC_ALAW_8 },
{ SND_PCM_FORMAT_MU_LAW, MPG123_ENC_ULAW_8 },
{ SND_PCM_FORMAT_S32, MPG123_ENC_SIGNED_32 },
{ SND_PCM_FORMAT_U32, MPG123_ENC_UNSIGNED_32 },
#ifdef WORDS_BIGENDIAN
{ SND_PCM_FORMAT_S24_3BE, MPG123_ENC_SIGNED_24 },
{ SND_PCM_FORMAT_U24_3BE, MPG123_ENC_UNSIGNED_24 },
#else
{ SND_PCM_FORMAT_S24_3LE, MPG123_ENC_SIGNED_24 },
{ SND_PCM_FORMAT_U24_3LE, MPG123_ENC_UNSIGNED_24 },
#endif
{ SND_PCM_FORMAT_FLOAT, MPG123_ENC_FLOAT_32 },
{ SND_PCM_FORMAT_FLOAT64, MPG123_ENC_FLOAT_64 }
};
#define NUM_FORMATS (sizeof format_map / sizeof format_map[0])
static int rates_match(long int desired, unsigned int actual)
{
return actual * 100 > desired * (100 - AUDIO_RATE_TOLERANCE) &&
actual * 100 < desired * (100 + AUDIO_RATE_TOLERANCE);
}
static int initialize_device(out123_handle *ao)
{
snd_pcm_hw_params_t *hw=NULL;
snd_pcm_sw_params_t *sw=NULL;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
snd_pcm_format_t format;
snd_pcm_t *pcm=(snd_pcm_t*)ao->userptr;
unsigned int rate;
int i;
snd_pcm_hw_params_alloca(&hw); /* Ignore GCC warning here... alsa-lib>=1.0.16 doesn't trigger that anymore, too. */
if (snd_pcm_hw_params_any(pcm, hw) < 0) {
if(!AOQUIET) error("initialize_device(): no configuration available");
return -1;
}
if (snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
if(!AOQUIET) error("initialize_device(): device does not support interleaved access");
return -1;
}
format = SND_PCM_FORMAT_UNKNOWN;
for (i = 0; i < NUM_FORMATS; ++i) {
if (ao->format == format_map[i].mpg123) {
format = format_map[i].alsa;
break;
}
}
if (format == SND_PCM_FORMAT_UNKNOWN) {
if(!AOQUIET) error1("initialize_device(): invalid sample format %d", ao->format);
errno = EINVAL;
return -1;
}
if (snd_pcm_hw_params_set_format(pcm, hw, format) < 0) {
if(!AOQUIET) error1("initialize_device(): cannot set format %s", snd_pcm_format_name(format));
return -1;
}
if (snd_pcm_hw_params_set_channels(pcm, hw, ao->channels) < 0) {
if(!AOQUIET) error1("initialize_device(): cannot set %d channels", ao->channels);
return -1;
}
rate = ao->rate;
if (snd_pcm_hw_params_set_rate_near(pcm, hw, &rate, NULL) < 0) {
if(!AOQUIET) error1("initialize_device(): cannot set rate %u", rate);
return -1;
}
if (!rates_match(ao->rate, rate)) {
if(!AOQUIET) error2("initialize_device(): rate %ld not available, using %u", ao->rate, rate);
/* return -1; */
}
buffer_size = rate * BUFFER_LENGTH;
if (snd_pcm_hw_params_set_buffer_size_near(pcm, hw, &buffer_size) < 0) {
if(!AOQUIET) error("initialize_device(): cannot set buffer size");
return -1;
}
debug1("buffer_size=%lu", (unsigned long)buffer_size);
period_size = buffer_size / 3; /* 3 periods is so much more common. */
if (snd_pcm_hw_params_set_period_size_near(pcm, hw, &period_size, NULL) < 0) {
if(!AOQUIET) error("initialize_device(): cannot set period size");
return -1;
}
debug1("period_size=%lu", (unsigned long)period_size);
if (snd_pcm_hw_params(pcm, hw) < 0) {
if(!AOQUIET) error("initialize_device(): cannot set hw params");
return -1;
}
snd_pcm_sw_params_alloca(&sw);
if (snd_pcm_sw_params_current(pcm, sw) < 0) {
if(!AOQUIET) error("initialize_device(): cannot get sw params");
return -1;
}
/* start playing right away */
if (snd_pcm_sw_params_set_start_threshold(pcm, sw, 1) < 0) {
if(!AOQUIET) error("initialize_device(): cannot set start threshold");
return -1;
}
/* wake up on every interrupt */
if (snd_pcm_sw_params_set_avail_min(pcm, sw, 1) < 0) {
if(!AOQUIET) error("initialize_device(): cannot set min available");
return -1;
}
#if SND_LIB_VERSION < ((1<<16)|16)
/* Always write as many frames as possible (deprecated since alsa-lib 1.0.16) */
if (snd_pcm_sw_params_set_xfer_align(pcm, sw, 1) < 0) {
if(!AOQUIET) error("initialize_device(): cannot set transfer alignment");
return -1;
}
#endif
if (snd_pcm_sw_params(pcm, sw) < 0) {
if(!AOQUIET) error("initialize_device(): cannot set sw params");
return -1;
}
return 0;
}
#ifndef DEBUG
static void error_ignorer(const char *file, int line, const char *function, int err, const char *fmt,...)
{
/* I can make ALSA silent. */
}
#endif
static int open_alsa(out123_handle *ao)
{
const char *pcm_name;
snd_pcm_t *pcm=NULL;
debug1("open_alsa with %p", ao->userptr);
#ifndef DEBUG
if(AOQUIET) snd_lib_error_set_handler(error_ignorer);
#endif
pcm_name = ao->device ? ao->device : "default";
if (snd_pcm_open(&pcm, pcm_name, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
if(!AOQUIET) error1("cannot open device %s", pcm_name);
return -1;
}
ao->userptr = pcm;
if (ao->format != -1) {
/* we're going to play: initalize sample format */
return initialize_device(ao);
} else {
/* query mode; sample format will be set for each query */
return 0;
}
}
static int get_formats_alsa(out123_handle *ao)
{
snd_pcm_t *pcm=(snd_pcm_t*)ao->userptr;
snd_pcm_hw_params_t *hw;
unsigned int rate;
int supported_formats, i;
snd_pcm_hw_params_alloca(&hw);
if (snd_pcm_hw_params_any(pcm, hw) < 0) {
if(!AOQUIET) error("get_formats_alsa(): no configuration available");
return -1;
}
if (snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
return -1;
if (snd_pcm_hw_params_set_channels(pcm, hw, ao->channels) < 0)
return 0;
rate = ao->rate;
if (snd_pcm_hw_params_set_rate_near(pcm, hw, &rate, NULL) < 0)
return -1;
if (!rates_match(ao->rate, rate))
return 0;
supported_formats = 0;
for (i = 0; i < NUM_FORMATS; ++i) {
if (snd_pcm_hw_params_test_format(pcm, hw, format_map[i].alsa) == 0)
supported_formats |= format_map[i].mpg123;
}
return supported_formats;
}
static int write_alsa(out123_handle *ao, unsigned char *buf, int bytes)
{
snd_pcm_t *pcm=(snd_pcm_t*)ao->userptr;
snd_pcm_uframes_t frames;
snd_pcm_sframes_t written;
frames = snd_pcm_bytes_to_frames(pcm, bytes);
while
( /* Try to write, recover if error, try again if recovery successful. */
(written = snd_pcm_writei(pcm, buf, frames)) < 0
&& snd_pcm_recover(pcm, (int)written, 0) == 0
)
{
debug2("recovered from alsa issue %i while trying to write %lu frames", (int)written, (unsigned long)frames);
}
if(written < 0)
{
error1("Fatal problem with alsa output, error %i.", (int)written);
return -1;
}
else return snd_pcm_frames_to_bytes(pcm, written);
}
static void flush_alsa(out123_handle *ao)
{
snd_pcm_t *pcm=(snd_pcm_t*)ao->userptr;
/* is this the optimal solution? - we should figure out what we really whant from this function */
debug("alsa drop");
snd_pcm_drop(pcm);
debug("alsa prepare");
snd_pcm_prepare(pcm);
debug("alsa flush done");
}
static void drain_alsa(out123_handle *ao)
{
snd_pcm_t *pcm=(snd_pcm_t*)ao->userptr;
debug1("drain_alsa with %p", ao->userptr);
snd_pcm_drain(pcm);
}
static int close_alsa(out123_handle *ao)
{
snd_pcm_t *pcm=(snd_pcm_t*)ao->userptr;
debug1("close_alsa with %p", ao->userptr);
if(pcm != NULL) /* be really generous for being called without any device opening */
{
ao->userptr = NULL; /* Should alsa do this or the module wrapper? */
return snd_pcm_close(pcm);
}
else return 0;
}
static int init_alsa(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_alsa;
ao->flush = flush_alsa;
ao->drain = drain_alsa;
ao->write = write_alsa;
ao->get_formats = get_formats_alsa;
ao->close = close_alsa;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "alsa",
/* description */ "Output audio using Advanced Linux Sound Architecture (ALSA).",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_alsa,
};

View File

@@ -0,0 +1,117 @@
/*
arts: audio output via aRts Sound Daemon
copyright 2007-8 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Stefan Lenselink (Stefan@lenselink.org)
*/
#include "out123_int.h"
#include <artsc.h>
#include "debug.h"
typedef struct
{
arts_stream_t arse; /* That's short for ARts StrEam;-) */
} mpg123_arts_t;
static int open_arts(out123_handle *ao)
{
short bits = 0;
if(!ao) return -1;
if(ao->format < 0)
{
ao->format = MPG123_ENC_SIGNED_16;
ao->rate = 44100;
ao->channels = 2;
}
/* Trial and error revealed these two formats to work with aRts. */
if(ao->format == MPG123_ENC_SIGNED_16) bits = 16;
else if(ao->format == MPG123_ENC_UNSIGNED_8) bits = 8;
else return -1;
/* Initialize the aRts lib*/
arts_init();
/* Open a stream to the aRts server */
((mpg123_arts_t*)ao->userptr)->arse = arts_play_stream( ao->rate, bits, ao->channels, "mpg123" );
/* Yeah, black box and all... it's still a pointer that is NULL on error. */
return (void*)((mpg123_arts_t*)ao->userptr)->arse == NULL ? -1 : 0;
}
static int get_formats_arts(out123_handle *ao)
{
/* aRts runs not everything, but any rate. */
return MPG123_ENC_SIGNED_16|MPG123_ENC_UNSIGNED_8;
}
static int write_arts(out123_handle *ao,unsigned char *buf,int len)
{
/* PIPE the PCM forward to the aRts Sound Daemon */
return arts_write( ((mpg123_arts_t*)ao->userptr)->arse , buf, len);
}
static int close_arts(out123_handle *ao)
{
/* Close the connection! */
arts_close_stream( ((mpg123_arts_t*)ao->userptr)->arse );
/* Free the memory allocated*/
arts_free();
return 0;
}
static void flush_arts(out123_handle *ao)
{
/* aRts doesn't have a flush statement! */
}
static int deinit_arts(out123_handle* ao)
{
if(ao->userptr)
{
free(ao->userptr);
ao->userptr = NULL;
}
arts_free();
return 0;
}
static int init_arts(out123_handle* ao)
{
if (ao==NULL) return -1;
ao->userptr = malloc(sizeof(mpg123_arts_t));
if(ao->userptr == NULL)
{
error("Out of memory!");
return -1;
}
/* clear it to have a consistent state */
memset(ao->userptr, 0, sizeof(mpg123_arts_t));
/* Set callbacks */
ao->open = open_arts;
ao->flush = flush_arts;
ao->write = write_arts;
ao->get_formats = get_formats_arts;
ao->close = close_arts;
ao->deinit = deinit_arts;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "arts",
/* description */ "Output audio using aRts Sound Daemon",
/* revision */ "$Rev: $",
/* handle */ NULL,
/* init_output */ init_arts,
};

View File

@@ -0,0 +1,435 @@
/*
coreaudio: audio output on MacOS X
copyright ?-2016 by the mpg123 project - free software under the terms of the GPL 2
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Guillaume Outters
modified by Nicholas J Humfrey to use SFIFO code
modified by Taihei Monma to use AudioUnit and AudioConverter APIs
*/
#include "out123_int.h"
/* has been around since at least 10.4 */
#include <AvailabilityMacros.h>
/* Use AudioComponents API when compiling for >= 10.6, otherwise fall back to
* Components Manager, which is deprecated since 10.8.
* MAC_OS_X_VERSION_MIN_REQUIRED defaults to the host system version and can be
* governed by MACOSX_DEPLOYMENT_TARGET environment variable and
* -mmacosx-version-min= when running the compiler. */
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 || __IPHONE_OS_VERSION_MIN_REQUIRED >= 20000
#define HAVE_AUDIOCOMPONENTS 1
#endif
#if HAVE_AUDIOCOMPONENTS
#define MPG123_AUDIOCOMPONENTDESCRIPTION AudioComponentDescription
#define MPG123_AUDIOCOMPONENT AudioComponent
#define MPG123_AUDIOCOMPONENTFINDNEXT AudioComponentFindNext
/* Funky API twist: AudioUnit is actually typedef'd AudioComponentInstance */
#define MPG123_AUDIOCOMPONENTINSTANCENEW AudioComponentInstanceNew
#define MPG123_AUDIOCOMPONENTINSTANCEDISPOSE AudioComponentInstanceDispose
#else
#include <CoreServices/CoreServices.h>
#define MPG123_AUDIOCOMPONENTDESCRIPTION ComponentDescription
#define MPG123_AUDIOCOMPONENT Component
#define MPG123_AUDIOCOMPONENTFINDNEXT FindNextComponent
#define MPG123_AUDIOCOMPONENTINSTANCENEW OpenAComponent
#define MPG123_AUDIOCOMPONENTINSTANCEDISPOSE CloseComponent
#endif
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
#include <errno.h>
/* Including the sfifo code locally, to avoid module linkage issues. */
#define SFIFO_STATIC
#include "sfifo.c"
#include "debug.h"
/* Duration of the ring buffer in seconds.
Is that all that there is to tunable latency?
Size of 200 ms should be enough for a default value, rare is the
hardware that actually allows such large buffers. */
#define FIFO_DURATION (ao->device_buffer > 0. ? ao->device_buffer : 0.2)
typedef struct mpg123_coreaudio
{
AudioConverterRef converter;
AudioUnit outputUnit;
int open;
char play;
int channels;
int bps;
int play_done;
int decode_done;
/* Convertion buffer */
unsigned char * buffer;
size_t buffer_size;
/* Ring buffer */
sfifo_t fifo;
} mpg123_coreaudio_t;
static OSStatus playProc(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *outOutputData,
AudioStreamPacketDescription **outDataPacketDescription,
void* inClientData)
{
out123_handle *ao = (out123_handle*)inClientData;
mpg123_coreaudio_t *ca = (mpg123_coreaudio_t *)ao->userptr;
long n;
/* This is not actually a loop. See the early break. */
for(n = 0; n < outOutputData->mNumberBuffers; n++)
{
unsigned int wanted = *ioNumberDataPackets * ca->channels * ca->bps;
unsigned char *dest;
unsigned int read;
int avail;
/* Any buffer count > 1 would wreck havoc with this code. */
if(n > 0)
break;
if(ca->buffer_size < wanted) {
debug1("Allocating %d byte sample conversion buffer", wanted);
ca->buffer = realloc( ca->buffer, wanted);
ca->buffer_size = wanted;
}
dest = ca->buffer;
if(!dest)
return -1;
/* Only play if we have data left */
while((avail=sfifo_used( &ca->fifo )) < wanted && !ca->decode_done)
{
int ms = (wanted-avail)/ao->framesize*1000/ao->rate;
debug3("waiting for more input, %d ms missing (%i < %u)"
, ms, avail, wanted);
usleep(ms*100); /* Wait for 1/10th of the missing duration. Might want to adjust. */
}
if(avail > wanted)
avail = wanted;
else if(ca->decode_done)
ca->play_done = 1;
/* Read audio from FIFO to CoreAudio's buffer */
read = sfifo_read(&ca->fifo, dest, avail);
if(read!=avail)
warning2("Error reading from the ring buffer (avail=%u, read=%u).\n", avail, read);
outOutputData->mBuffers[n].mDataByteSize = read;
outOutputData->mBuffers[n].mData = dest;
}
return noErr;
}
static OSStatus convertProc(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
UInt32 inNumFrames, AudioBufferList *ioData)
{
AudioStreamPacketDescription* outPacketDescription = NULL;
out123_handle *ao = (out123_handle*)inRefCon;
mpg123_coreaudio_t *ca = (mpg123_coreaudio_t *)ao->userptr;
OSStatus err= noErr;
err = AudioConverterFillComplexBuffer(ca->converter, playProc, inRefCon, &inNumFrames, ioData, outPacketDescription);
return err;
}
static int open_coreaudio(out123_handle *ao)
{
mpg123_coreaudio_t* ca = (mpg123_coreaudio_t*)ao->userptr;
UInt32 size;
MPG123_AUDIOCOMPONENTDESCRIPTION desc;
MPG123_AUDIOCOMPONENT comp;
AudioStreamBasicDescription inFormat;
AudioStreamBasicDescription outFormat;
AURenderCallbackStruct renderCallback;
Boolean outWritable;
/* Initialize our environment */
ca->play = 0;
ca->buffer = NULL;
ca->buffer_size = 0;
ca->play_done = 0;
ca->decode_done = 0;
/* Get the default audio output unit */
desc.componentType = kAudioUnitType_Output;
#if TARGET_OS_IPHONE
desc.componentSubType = kAudioUnitSubType_RemoteIO;
#else
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
#endif
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
comp = MPG123_AUDIOCOMPONENTFINDNEXT(NULL, &desc);
if(comp == NULL)
{
if(!AOQUIET)
error("AudioComponentFindNext failed");
return(-1);
}
if(MPG123_AUDIOCOMPONENTINSTANCENEW(comp, &(ca->outputUnit)))
{
if(!AOQUIET)
error("AudioComponentInstanceNew failed");
return (-1);
}
if(AudioUnitInitialize(ca->outputUnit))
{
if(!AOQUIET)
error("AudioUnitInitialize failed");
return (-1);
}
/* Specify the output PCM format */
AudioUnitGetPropertyInfo(ca->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &size, &outWritable);
if(AudioUnitGetProperty(ca->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &outFormat, &size))
{
if(!AOQUIET)
error("AudioUnitGetProperty(kAudioUnitProperty_StreamFormat) failed");
return (-1);
}
if(AudioUnitSetProperty(ca->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outFormat, size))
{
if(!AOQUIET)
error("AudioUnitSetProperty(kAudioUnitProperty_StreamFormat) failed");
return (-1);
}
/* Specify the input PCM format */
ca->channels = ao->channels;
inFormat.mSampleRate = ao->rate;
inFormat.mChannelsPerFrame = ao->channels;
inFormat.mFormatID = kAudioFormatLinearPCM;
#ifdef _BIG_ENDIAN
inFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian;
#else
inFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked;
#endif
switch(ao->format)
{
case MPG123_ENC_SIGNED_16:
inFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
ca->bps = 2;
break;
case MPG123_ENC_SIGNED_8:
inFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
ca->bps = 1;
break;
case MPG123_ENC_UNSIGNED_8:
ca->bps = 1;
break;
case MPG123_ENC_SIGNED_32:
inFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
ca->bps = 4;
break;
case MPG123_ENC_FLOAT_32:
inFormat.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
ca->bps = 4;
break;
}
inFormat.mBitsPerChannel = ca->bps << 3;
inFormat.mBytesPerPacket = ca->bps*inFormat.mChannelsPerFrame;
inFormat.mFramesPerPacket = 1;
inFormat.mBytesPerFrame = ca->bps*inFormat.mChannelsPerFrame;
/* Add our callback - but don't start it yet */
memset(&renderCallback, 0, sizeof(AURenderCallbackStruct));
renderCallback.inputProc = convertProc;
renderCallback.inputProcRefCon = ao;
if(AudioUnitSetProperty(ca->outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderCallback, sizeof(AURenderCallbackStruct)))
{
if(!AOQUIET)
error("AudioUnitSetProperty(kAudioUnitProperty_SetRenderCallback) failed");
return(-1);
}
/* Open an audio I/O stream and create converter */
if (ao->rate > 0 && ao->channels >0 ) {
int ringbuffer_len;
if(AudioConverterNew(&inFormat, &outFormat, &(ca->converter)))
{
if(!AOQUIET)
error("AudioConverterNew failed");
return(-1);
}
if(ao->channels == 1) {
SInt32 channelMap[2] = { 0, 0 };
if(AudioConverterSetProperty(ca->converter, kAudioConverterChannelMap, sizeof(channelMap), channelMap))
{
if(!AOQUIET)
error("AudioConverterSetProperty(kAudioConverterChannelMap) failed");
return(-1);
}
}
/* Initialise FIFO */
ringbuffer_len = ao->rate * FIFO_DURATION * ca->bps * ao->channels;
debug2( "Allocating %d byte ring-buffer (%f seconds)", ringbuffer_len, (float)FIFO_DURATION);
sfifo_init( &ca->fifo, ringbuffer_len );
}
return(0);
}
static int get_formats_coreaudio(out123_handle *ao)
{
return MPG123_ENC_SIGNED_16|MPG123_ENC_SIGNED_8|MPG123_ENC_UNSIGNED_8|MPG123_ENC_SIGNED_32|MPG123_ENC_FLOAT_32;
}
static int write_coreaudio(out123_handle *ao, unsigned char *buf, int len)
{
mpg123_coreaudio_t* ca = (mpg123_coreaudio_t*)ao->userptr;
int len_remain = len;
/* Some busy waiting, but feed what is possible. */
while(len_remain) /* Note: input len is multiple of framesize! */
{
int block = sfifo_space(&ca->fifo);
block -= block % ao->framesize;
if(block > len_remain)
block = len_remain;
if(block)
{
sfifo_write(&ca->fifo, buf, block);
len_remain -= block;
buf += block;
/* Start playback now that we have something to play */
if(!ca->play && (sfifo_used(&ca->fifo) > (sfifo_size(&ca->fifo)/2)))
{
if(AudioOutputUnitStart(ca->outputUnit))
{
if(!AOQUIET)
error("AudioOutputUnitStart failed");
return(-1);
}
ca->play = 1;
}
}
/* If there is no room, then sleep for a bit, but not too long. */
if(len_remain)
usleep( (0.1*FIFO_DURATION) * 1000000 );
}
return len;
}
static int close_coreaudio(out123_handle *ao)
{
mpg123_coreaudio_t* ca = (mpg123_coreaudio_t*)ao->userptr;
if (ca) {
ca->decode_done = 1;
while(!ca->play_done && ca->play)
usleep((0.1*FIFO_DURATION)*1000000);
/* No matter the error code, we want to close it (by brute force if necessary) */
AudioOutputUnitStop(ca->outputUnit);
AudioUnitUninitialize(ca->outputUnit);
MPG123_AUDIOCOMPONENTINSTANCEDISPOSE(ca->outputUnit);
AudioConverterDispose(ca->converter);
/* Free the ring buffer */
sfifo_close( &ca->fifo );
/* Free the conversion buffer */
if (ca->buffer) {
free( ca->buffer );
ca->buffer = NULL;
}
}
return 0;
}
static void flush_coreaudio(out123_handle *ao)
{
mpg123_coreaudio_t* ca = (mpg123_coreaudio_t*)ao->userptr;
/* Flush AudioConverter's buffer */
if(AudioConverterReset(ca->converter))
{
if(!AOQUIET)
error("AudioConverterReset failed");
}
/* Empty out the ring buffer */
sfifo_flush( &ca->fifo );
}
static int deinit_coreaudio(out123_handle* ao)
{
/* Free up memory */
if (ao->userptr) {
free( ao->userptr );
ao->userptr = NULL;
}
/* Success */
return 0;
}
static int init_coreaudio(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_coreaudio;
ao->flush = flush_coreaudio;
ao->write = write_coreaudio;
ao->get_formats = get_formats_coreaudio;
ao->close = close_coreaudio;
ao->deinit = deinit_coreaudio;
/* Allocate memory for data structure */
ao->userptr = malloc( sizeof( mpg123_coreaudio_t ) );
if (ao->userptr==NULL)
{
if(!AOQUIET)
error("failed to malloc memory for 'mpg123_coreaudio_t'");
return -1;
}
memset( ao->userptr, 0, sizeof(mpg123_coreaudio_t) );
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "coreaudio",
/* description */ "Output audio using Mac OS X's CoreAudio.",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_coreaudio,
};

View File

@@ -0,0 +1,84 @@
/*
dummy: dummy audio output
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
*/
#include "out123_int.h"
#include "debug.h"
static int open_dummy(out123_handle *ao)
{
debug("open_dummy()");
if(ao->format < 0)
{
ao->rate = 44100;
ao->channels = 2;
ao->format = MPG123_ENC_SIGNED_16;
}
return 0;
}
static int get_formats_dummy(out123_handle *ao)
{
debug("get_formats_dummy()");
return MPG123_ENC_SIGNED_16;
}
static int write_dummy(out123_handle *ao,unsigned char *buf,int len)
{
debug("write_dummy()");
return len;
}
static void flush_dummy(out123_handle *ao)
{
debug("flush_dummy()");
}
static int close_dummy(out123_handle *ao)
{
debug("close_dummy()");
return 0;
}
static int deinit_dummy(out123_handle *ao)
{
debug("deinit_dummy()");
return 0;
}
static int init_dummy(out123_handle* ao)
{
if (ao==NULL) return -1;
debug("init_dummy()");
/* Set callbacks */
ao->open = open_dummy;
ao->flush = flush_dummy;
ao->write = write_dummy;
ao->get_formats = get_formats_dummy;
ao->close = close_dummy;
ao->deinit = deinit_dummy;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "dummy",
/* description */ "Dummy audio output - does not output audio.",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_dummy,
};

View File

@@ -0,0 +1,169 @@
/*
esd: audio output for ESounD (highly untested nowadays (?))
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Eric B. Mitchell ("esd port" should be this file...)
*/
/* First the common header, including config.h
...this is important for stuff like _FILE_OFFSET_BITS */
#include "out123_int.h"
#include <esd.h>
#include <errno.h>
#include <assert.h>
#ifdef SOLARIS
#include <stropts.h>
#include <sys/conf.h>
#endif
#ifdef NETBSD
#include <sys/ioctl.h>
#include <sys/audioio.h>
#endif
#include "debug.h"
static unsigned esd_rate = 0, esd_format = 0, esd_channels = 0;
static int open_esound(out123_handle *ao)
{
esd_format_t format = ESD_STREAM | ESD_PLAY;
if (!esd_rate)
{
int esd;
esd_server_info_t *info;
esd_format_t fmt;
if ((esd = esd_open_sound(NULL)) >= 0)
{
info = esd_get_server_info(esd);
esd_rate = info->rate;
fmt = info->format;
esd_free_server_info(info);
esd_close(esd);
}
else
{
esd_rate = esd_audio_rate;
fmt = esd_audio_format;
}
esd_format = MPG123_ENC_UNSIGNED_8;
if ((fmt & ESD_MASK_BITS) == ESD_BITS16)
esd_format |= MPG123_ENC_SIGNED_16;
esd_channels = fmt & ESD_MASK_CHAN;
}
if (ao->format == -1)
ao->format = esd_format;
else if (!(ao->format & esd_format))
{
if(!AOQUIET)
error1("Unsupported audio format: %d\n", ao->format);
errno = EINVAL;
return -1;
}
if (ao->format & MPG123_ENC_SIGNED_16)
format |= ESD_BITS16;
else if (ao->format & MPG123_ENC_UNSIGNED_8)
format |= ESD_BITS8;
else assert(0);
if (ao->channels == -1) ao->channels = 2;
else if (ao->channels <= 0 || ao->channels > esd_channels)
{
if(!AOQUIET)
error1("Unsupported no of channels: %d\n", ao->channels);
errno = EINVAL;
return -1;
}
if (ao->channels == 1)
format |= ESD_MONO;
else if (ao->channels == 2)
format |= ESD_STEREO;
else assert(0);
if (ao->rate == -1) ao->rate = esd_rate;
else if (ao->rate > esd_rate)
return -1;
ao->fn = esd_play_stream_fallback(format, ao->rate, ao->device, "mpg123");
return (ao->fn);
}
static int get_formats_esound (out123_handle *ao)
{
if (0 < ao->channels && ao->channels <= esd_channels
&& 0 < ao->rate && ao->rate <= esd_rate)
{
return esd_format;
} else {
return -1;
}
}
static int write_esound(out123_handle *ao,unsigned char *buf,int len)
{
return write(ao->fn,buf,len);
}
static int close_esound(out123_handle *ao)
{
close (ao->fn);
return 0;
}
#ifdef SOLARIS
static void flush_esound (out123_handle *ao)
{
ioctl (ao->fn, I_FLUSH, FLUSHRW);
}
#else
#ifdef NETBSD
static void flush_esound (out123_handle *ao)
{
ioctl (ao->fn, AUDIO_FLUSH, 0);
}
#else
/* Dunno what to do on Linux and Cygwin, but the func must be at least defined! */
static void flush_esound (out123_handle *ao)
{
}
#endif
#endif
static int init_esound(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_esound;
ao->flush = flush_esound;
ao->write = write_esound;
ao->get_formats = get_formats_esound;
ao->close = close_esound;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "esd",
/* description */ "Output audio using ESounD (The Enlightened Sound Daemon).",
/* revision */ "$Rev: 3915 $",
/* handle */ NULL,
/* init_output */ init_esound,
};

View File

@@ -0,0 +1,184 @@
/*
hp: audio output for HP-UX
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp
*/
#include "out123_int.h"
#include <fcntl.h>
#include <sys/audio.h>
#include "debug.h"
static int set_rate(out123_handle *ao)
{
if(ao->rate >= 0) {
return ioctl(ao->fn,AUDIO_SET_SAMPLE_RATE,ao->rate);
} else {
return 0;
}
}
static int set_channels(out123_handle *ao)
{
if(ao->channels<0) return 0;
return ioctl(ao->fn,AUDIO_SET_CHANNELS,ao->channels);
}
static int set_format(out123_handle *ao)
{
int fmt;
switch(ao->format) {
case -1:
case MPG123_ENC_SIGNED_16:
default:
fmt = MPG123_ENC_LINEAR16BIT;
break;
case MPG123_ENC_UNSIGNED_8:
error("unsigned 8 bit linear not supported");
return -1;
case MPG123_ENC_SIGNED_8:
error("signed 8 bit linear not supported");
return -1;
case MPG123_ENC_ALAW_8:
fmt = MPG123_ENC_ALAW;
break;
case MPG123_ENC_ULAW_8:
fmt = MPG123_ENC_ULAW;
break;
}
return ioctl(ao->fn,AUDIO_SET_DATA_FORMAT,fmt);
}
static int get_formats(out123_handle *ao)
{
return MPG123_ENC_SIGNED_16;
}
static int reset_parameters(out123_handle *ao)
{
int ret;
ret = set_format(ai);
if(ret >= 0)
ret = set_channels_hp(ai);
if(ret >= 0)
ret = set_rate_hp(ai);
return ret;
}
static int open_hp(out123_handle *ao)
{
struct audio_describe ades;
struct audio_gain again;
int i,audio;
ao->fn = open("/dev/audio",O_RDWR);
if(ao->fn < 0)
return -1;
ioctl(ao->fn,AUDIO_DESCRIBE,&ades);
if(ao->gain != -1)
{
if(ao->gain > ades.max_transmit_gain)
{
error("your gainvalue was to high -> set to maximum.");
ao->gain = ades.max_transmit_gain;
}
if(ao->gain < ades.min_transmit_gain)
{
error("your gainvalue was to low -> set to minimum.");
ao->gain = ades.min_transmit_gain;
}
again.channel_mask = AUDIO_CHANNEL_0 | AUDIO_CHANNEL_1;
ioctl(ao->fn,AUDIO_GET_GAINS,&again);
again.cgain[0].transmit_gain = ao->gain;
again.cgain[1].transmit_gain = ao->gain;
again.channel_mask = AUDIO_CHANNEL_0 | AUDIO_CHANNEL_1;
ioctl(ao->fn,AUDIO_SET_GAINS,&again);
}
if(ao->flags != -1)
{
if(ao->flags & OUT123_INTERNAL_SPEAKER)
ioctl(ao->fn,AUDIO_SET_OUTPUT,OUT123_SPEAKER);
else if(ao->flags & OUT123_HEADPHONES)
ioctl(ao->fn,AUDIO_SET_OUTPUT,OUT123_HEADPHONE);
else if(ao->flags & OUT123_LINE_OUT)
ioctl(ao->fn,AUDIO_SET_OUTPUT,OUT123_LINE);
}
if(ao->rate == -1)
ao->rate = 44100;
for(i=0;i<ades.nrates;i++)
{
if(ao->rate == ades.sample_rate[i])
break;
}
if(i == ades.nrates)
{
error1("Can't set sample-rate to %ld.\n",ao->rate);
i = 0;
}
if(reset_parameters(ai) < 0)
return -1;
return ao->fn;
}
static int write_hp(out123_handle *ao,unsigned char *buf,int len)
{
return write(ao->fn,buf,len);
}
static int close_hp(out123_handle *ao)
{
close (ao->fn);
return 0;
}
static void flush_hp(out123_handle *ao)
{
}
static int init_hp(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_hp;
ao->flush = flush_hp;
ao->write = write_hp;
ao->get_formats = get_formats_hp;
ao->close = close_hp;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "hp",
/* description */ "Output audio HP-UX",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_hp,
};

View File

@@ -0,0 +1,618 @@
/*
jack: audio output via JACK Audio Connection Kit
copyright 2006-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Nicholas J. Humfrey
I reworked the processing logic. Only one ringbuffer, deinterleaving in the
processing callback. A semaphore to avoid using usleep() for waiting. Up
to 99 channels. Only float input (ensures that libmpg123 selects f32
encoding). This is still a hack to shoehorn the JACK API into our model.
Damn. I'm wary of he semaphore. I'm sure I constructed a deadlock there.
There's always a deadlock. --ThOr
*/
#include "out123_int.h"
#include <math.h>
#include <jack/jack.h>
#include <jack/ringbuffer.h>
/* Using some pthread to provide synchronization between process callback
and writer part. The JACK API is not meant for this. Libpthread is
pulled in as libjack dependency anyway. */
#include <semaphore.h>
#include <sys/errno.h>
#include "debug.h"
typedef struct {
int alive;
sem_t sem; /* semaphore to avoid busy waiting */
int channels;
int encoding;
int framesize;
jack_default_audio_sample_t **ports_buf;
jack_port_t **ports;
jack_ringbuffer_t *rb;
size_t rb_size; /* in bytes */
jack_client_t *client;
char *procbuf;
size_t procbuf_frames; /* in PCM frames */
} jack_handle_t, *jack_handle_ptr;
static jack_handle_t* alloc_jack_handle(out123_handle *ao)
{
jack_handle_t *handle=NULL;
int i;
handle = malloc(sizeof(jack_handle_t));
if (!handle)
return NULL;
handle->channels = ao->channels;
handle->encoding = ao->format;
handle->framesize = ao->framesize;
handle->rb = NULL;
handle->ports_buf = malloc( sizeof(jack_default_audio_sample_t*)
* ao->channels );
handle->ports = malloc(sizeof(jack_port_t*)*ao->channels);
if(!handle->ports_buf || !handle->ports)
{
if(handle->ports_buf)
free(handle->ports_buf);
if(handle->ports)
free(handle->ports);
free(handle);
return NULL;
}
for(i=0; i<ao->channels; ++i)
{
handle->ports_buf[i] = NULL;
handle->ports[i] = NULL;
}
if(sem_init(&handle->sem, 0, 0))
{
if(!AOQUIET)
error("Semaphore init failed.");
free(handle->ports_buf);
free(handle->ports);
free(handle);
return NULL;
}
handle->alive = 0;
handle->client = NULL;
handle->procbuf = NULL;
handle->rb_size = 0;
handle->procbuf_frames = 0;
return handle;
}
static void free_jack_handle( jack_handle_t* handle )
{
int i;
if(handle->ports)
{
if(handle->client)
for(i=0; i<handle->channels; i++)
{
/* Close the port for channel*/
if(handle->ports[i])
jack_port_unregister(handle->client, handle->ports[i]);
}
free(handle->ports);
}
if(handle->ports_buf)
free(handle->ports_buf);
/* Free up the ring buffer for channel*/
if(handle->rb)
jack_ringbuffer_free(handle->rb);
if (handle->client)
jack_client_close(handle->client);
if (handle->procbuf)
free(handle->procbuf);
sem_destroy(&handle->sem);
free(handle);
}
static int process_callback( jack_nframes_t nframes, void *arg )
{
int c;
jack_handle_t* handle = (jack_handle_t*)arg;
size_t to_read = nframes;
for(c=0; c<handle->channels; ++c)
handle->ports_buf[c] =
jack_port_get_buffer(handle->ports[c], nframes);
/* One ringbuffer to rule them all, getting interleaved data piecewise
and appending to non-interleaved buffers. */
while(to_read)
{
/* Need to read into temporary storage, then deinterleave to JACK
buffers. */
size_t got_piece;
size_t avail_piece;
size_t piece = to_read > handle->procbuf_frames
? handle->procbuf_frames
: to_read;
/* Ensure we get only full PCM frames by checking available byte count
and reducing expectation. */
avail_piece = jack_ringbuffer_read_space(handle->rb)/handle->framesize;
got_piece = jack_ringbuffer_read( handle->rb
, handle->procbuf, (avail_piece > piece ? piece : avail_piece)
* handle->framesize ) / handle->framesize;
debug2( "fetched %"SIZE_P" frames from ringbuffer (wanted %"SIZE_P")"
, (size_p)got_piece, (size_p)piece );
/* If this is the last piece, fill up, not time to wait. */
if(to_read > piece)
piece = got_piece; /* We got further loop cycle(s) to get the rest. */
else
{
if(piece > got_piece)
{
debug("filling up with zeros");
bzero( handle->procbuf+got_piece*handle->framesize
, (piece-got_piece)*handle->framesize );
}
}
/* Now extract the pieces for the channels. */
for (c=0; c < handle->channels; ++c)
{
size_t n;
jack_default_audio_sample_t *dst = handle->ports_buf[c];
if(handle->encoding == MPG123_ENC_FLOAT_32)
{
float* src = (float*)handle->procbuf;
for(n=0; n<piece; ++n)
*(dst++) = src[(n*handle->channels)+c];
}
else /* MPG123_ENC_FLOAT_64 */
{
double* src = (double*)handle->procbuf;
for(n=0; n<piece; ++n)
*(dst++) = src[(n*handle->channels)+c];
}
/* Store output buffer offset. */
handle->ports_buf[c] = dst;
}
/* Give the writer a hint about the time passed. */
sem_post(&handle->sem);
to_read -= piece;
}
/* Success*/
return 0;
}
/* This is triggered on server shutdown and very much necessary to avoid
out123 hanging on the processor semaphore. */
static void shutdown_callback(void *arg)
{
jack_handle_t* handle = (jack_handle_t*)arg;
handle->alive = 0;
sem_post(&handle->sem);
debug("shutdown_callback()");
}
/* connect to jack ports named in the NULL-terminated wishlist */
static int real_connect_jack_ports(out123_handle *ao
, jack_handle_t* handle, const char** wishlist)
{
const char **wish = wishlist;
int ch, err;
int ch_wrap = 0, wish_wrap = 0;
if(!wish)
return 0;
if(wish != NULL && *wish == NULL)
return 1; /* success, nothing connected as wanted */
ch=0;
/* Connect things as long as there are sources or sinks left. */
while(!wish_wrap || !ch_wrap)
{
const char* in = jack_port_name(handle->ports[ch]);
if((err = jack_connect(handle->client, in, *wish)) != 0 && err != EEXIST)
{
if(!AOQUIET)
error4( "connect_jack_ports(): failed to jack_connect() ch%i (%s) to %s: %d"
, ch, in ? in : "<nil>", *wish, err );
return 0;
}
/*
Increment channel and wishlist, both possibly wrapping around, to
ensure we connected all channels to some output port and provided
some input to all ports in the wishlist. Both cases of less channels
than output ports (splitting) and more channels than output ports
(downmix) are sensible.
*/
if(++ch == handle->channels)
{
ch = 0;
++ch_wrap;
}
if(!*(++wish))
{
wish = wishlist;
++wish_wrap;
}
}
return 1;
}
/* crude way of automatically connecting up jack ports */
/* 0 on error */
static int autoconnect_jack_ports(out123_handle *ao, jack_handle_t* handle)
{
const char **all_ports;
unsigned int ch=0;
int err,i;
/* Get a list of all the jack ports*/
all_ports = jack_get_ports (handle->client, NULL, NULL, JackPortIsInput);
if(!all_ports)
{
if(!AOQUIET)
error("connect_jack_ports(): jack_get_ports() returned NULL.");
return 0;
}
/* Step through each port name*/
for (i = 0; all_ports[i]; ++i)
{
const char* in = jack_port_name( handle->ports[ch] );
const char* out = all_ports[i];
if ((err = jack_connect(handle->client, in, out)) != 0 && err != EEXIST)
{
if(!AOQUIET)
error1("connect_jack_ports(): failed to jack_connect() ports: %d",err);
return 0;
}
/* Found enough ports ?*/
if (++ch >= handle->channels) break;
}
jack_free(all_ports);
return 1;
}
static int connect_jack_ports(out123_handle *ao
, jack_handle_t* handle)
{
debug1("connect_jack_ports with dev=%s", ao->device ? ao->device : "<nil>");
if(ao->device==NULL || strcmp(ao->device, "auto")==0)
return autoconnect_jack_ports(ao, handle);
else
{
/* Parse device for a set of ports, comma separated. */
const char** wishlist; /* Channels and end marker. */
int wish_channels = 1; /* Numper of entries in wishlist. */
char *devcopy, *chr;
int ret;
int c;
size_t len;
len = strlen(ao->device);
/* We connect as many JACK ports as desired, possibly duplicating. */
for(chr=ao->device; *chr; ++chr)
if(*chr == ',')
++wish_channels;
debug1("wish_channels: %i", wish_channels);
wishlist = malloc(sizeof(char*)*(wish_channels+1));
devcopy = compat_strdup(ao->device);
if(devcopy == NULL || wishlist == NULL)
{
if(devcopy)
free(devcopy);
if(wishlist)
free(wishlist);
if(!AOQUIET)
error("OOM");
return 0;
}
for(c=0;c<=wish_channels;++c)
wishlist[c] = NULL;
if(len && strcmp(devcopy, "none"))
{
size_t i=0;
wishlist[0] = devcopy;
for(c=0;c<wish_channels;++c)
{
while(devcopy[i] != 0 && devcopy[i] != ',') ++i;
debug2("devcopy[%"SIZE_P"]=%i", i, devcopy[i]);
if(devcopy[i] == ',')
{
/* Terminate previous port name, assign next one. */
devcopy[i++] = 0;
debug2("terminaled wish %i: %s", c, wishlist[c]);
if(c+1 < wish_channels)
wishlist[c+1] = devcopy+i;
}
else
break;
}
}
if(wishlist[0] == NULL && !AOQUIET)
warning("Not connecting up jack ports as requested.");
ret = real_connect_jack_ports(ao, handle, wishlist);
free(devcopy);
free(wishlist);
return ret;
}
return 1;
}
static void drain_jack(out123_handle *ao)
{
jack_handle_t *handle = (jack_handle_t*)ao->userptr;
debug("drain_jack().");
do errno = 0;
while(sem_trywait(&handle->sem) == 0 || errno == EINTR);
/* For some reason, a single byte is reserved by JACK?! */
while( handle && handle->alive && handle->rb
&& jack_ringbuffer_write_space(handle->rb)+1 < handle->rb_size )
{
debug2( "JACK close wait %"SIZE_P" < %"SIZE_P"\n"
, (size_p)jack_ringbuffer_write_space(handle->rb)
, (size_p)handle->rb_size );
sem_wait(&handle->sem);
}
}
static int close_jack(out123_handle *ao)
{
jack_handle_t *handle = (jack_handle_t*)ao->userptr;
debug("close_jack().");
/* Close and shutdown*/
if(handle)
{
free_jack_handle(handle);
ao->userptr = NULL;
}
return 0;
}
static int open_jack(out123_handle *ao)
{
jack_handle_t *handle=NULL;
jack_options_t jopt = JackNullOption|JackNoStartServer;
jack_status_t jstat = 0;
unsigned int i;
char *realname;
debug("jack open");
if(!ao)
return -1;
/* Return if already open*/
if(ao->userptr)
{
if(!AOQUIET)
error("audio device is already open.");
return -1;
}
/* The initial open lets me choose the settings. */
if (ao->format==-1)
{
ao->format = MPG123_ENC_FLOAT_32;
ao->channels = 2;
/* Really need a framesize defined for callback. */
ao->framesize = 2*4;
}
else if(!(ao->format & MPG123_ENC_FLOAT))
{
if(!AOQUIET)
error("JACK only wants float!");
return -1;
}
/* Create some storage for ourselves*/
if((handle = alloc_jack_handle(ao)) == NULL)
return -1;
ao->userptr = (void*)handle;
/* Register with Jack*/
if((handle->client = jack_client_open(ao->name, jopt, &jstat)) == 0)
{
if(!AOQUIET)
error1("Failed to open jack client: 0x%x", jstat);
close_jack(ao);
return -1;
}
realname = jack_get_client_name(handle->client);
/* Display the unique client name allocated to us */
if(AOVERBOSE(1))
fprintf( stderr, "Registered as JACK client %s.\n"
, realname ? realname : "<nil>" );
/* Just make sure. */
ao->rate = jack_get_sample_rate(handle->client);
/* Check the sample rate is correct*/
if (jack_get_sample_rate( handle->client ) != (jack_nframes_t)ao->rate)
{
if(!AOQUIET)
error("JACK Sample Rate is different to sample rate of file.");
close_jack(ao);
return -1;
}
/* Register ports with Jack*/
if(handle->channels > 0 && handle->channels < 100)
{
for(i=0;i<handle->channels;++i)
{
char numbuf[3]; /* two digits, zero byte */
sprintf(numbuf, "%d", i+1);
if( !(handle->ports[i] = jack_port_register( handle->client
, numbuf, JACK_DEFAULT_AUDIO_TYPE
, JackPortIsOutput, 0 )) )
{
if(!AOQUIET)
error1("Cannot register JACK output port '%s'.", numbuf);
close_jack(ao);
return -1;
}
}
}
else
{
if(!AOQUIET)
error1("excessive number of output channels (%d).", handle->channels);
close_jack(ao);
return -1;
}
/* Use device_buffer parameter for ring buffer, but ensure that two
JACK buffers fit in there. We do not support that buffer increasing
later on. */
handle->rb_size = (size_t)( ao->device_buffer
* jack_get_sample_rate(handle->client)
+ 0.5 ); /* PCM frames */
handle->procbuf_frames = jack_get_buffer_size(handle->client);
if(handle->rb_size < 2*handle->procbuf_frames)
handle->rb_size = 2*handle->procbuf_frames;
debug1("JACK ringbuffer for %"SIZE_P" PCM frames", (size_p)handle->rb_size);
/* Convert to bytes. */
handle->rb_size *= handle->framesize;
handle->rb = jack_ringbuffer_create(handle->rb_size);
handle->procbuf = malloc(handle->procbuf_frames*handle->framesize);
if(!handle->rb || !handle->procbuf)
{
if(!AOQUIET)
error("failed to allocate buffers");
close_jack(ao);
return -1;
}
/* Set the callbacks*/
jack_set_process_callback(handle->client, process_callback, (void*)handle);
jack_on_shutdown(handle->client, shutdown_callback, (void*)handle);
handle->alive = 1;
/* Activate client*/
if(jack_activate(handle->client))
{
if(!AOQUIET)
error("Can't activate client.");
close_jack(ao);
return -1;
}
/* Connect up the portsm, return */
if(!connect_jack_ports(ao, handle))
{
/* deregistering of ports will not work but should just fail, then,
and let the rest clean up */
close_jack(ao);
return -1;
}
debug("Jack open successful.\n");
ao->realname = compat_strdup(realname);
return 0;
}
/* Jack prefers floats, I actually assume it does _only_ float/double
(as it is nowadays)! */
static int get_formats_jack(out123_handle *ao)
{
jack_handle_t *handle = (jack_handle_t*)ao->userptr;
if(jack_get_sample_rate(handle->client) != (jack_nframes_t)ao->rate)
return 0;
else
return MPG123_ENC_FLOAT_32|MPG123_ENC_FLOAT_64;
}
static int write_jack(out123_handle *ao, unsigned char *buf, int len)
{
jack_handle_t *handle = (jack_handle_t*)ao->userptr;
size_t bytes_left;
unsigned int strike = 0;
bytes_left = len;
while(bytes_left && handle->alive)
{
size_t piece;
debug("writing to ringbuffer");
/* No help: piece1 = jack_ringbuffer_write_space(handle->rb); */
piece = jack_ringbuffer_write(handle->rb, (char*)buf, bytes_left);
debug1("wrote %"SIZE_P" B", (size_p)piece);
buf += piece;
bytes_left -= piece;
/* Allow nothing being written some times, but not too often.
Don't know how often in a row that would be supposed to happen. */
if(!piece)
{
if(++strike > 100)
{
if(!AOQUIET)
error("Cannot write to ringbuffer.");
break;
}
/* Avoid busy waiting and semaphore accumulation:
Wait once on the semaphore, then clear it. We count on it being
posted by the process callback and we are going to push new data
so that that one gets the chance. */
sem_wait(&handle->sem);
do errno = 0;
while(sem_trywait(&handle->sem) == 0 || errno == EINTR);
}
else
strike = 0;
}
return len-bytes_left;
}
static void flush_jack(out123_handle *ao)
{
jack_handle_t *handle = (jack_handle_t*)ao->userptr;
/* Reset the ring buffers*/
jack_ringbuffer_reset(handle->rb);
}
static int init_jack(out123_handle* ao)
{
if (ao==NULL)
return -1;
/* Set callbacks */
ao->open = open_jack;
ao->flush = flush_jack;
ao->drain = drain_jack;
ao->write = write_jack;
ao->get_formats = get_formats_jack;
ao->close = close_jack;
ao->propflags |= OUT123_PROP_PERSISTENT;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "jack",
/* description */ "Output audio using JACK (JACK Audio Connection Kit).",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_jack
};

View File

@@ -0,0 +1,199 @@
/*
mint: audio output for MINT
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Petr Stehlik
*/
#include "out123_int.h"
/* derived from LINUX, VOXWARE and SUN for MiNT Audio Device by Petr Stehlik */
#include <fcntl.h>
#include <ioctl.h>
#include <audios.h>
#include "debug.h"
/* Globals */
extern int outburst;
int real_rate_printed = 0;
static int rate_best_match(out123_handle *ao)
{
int ret,dsp_rate;
if(!ai || ao->fn < 0 || ao->rate < 0)
return -1;
dsp_rate = ao->rate;
ret = ioctl(ao->fn,AIOCSSPEED, (void *)dsp_rate);
ret = ioctl(ao->fn,AIOCGSPEED,&dsp_rate);
if(ret < 0) return ret;
ao->rate = dsp_rate;
return 0;
}
static int set_rate(out123_handle *ao)
{
int dsp_rate = ao->rate;
if(ao->rate >= 0) {
int ret, real_rate;
ret = ioctl(ao->fn, AIOCSSPEED, (void *)dsp_rate);
if (ret >= 0 && !real_rate_printed) {
ioctl(ao->fn,AIOCGSPEED,&real_rate);
if (real_rate != dsp_rate) {
fprintf(stderr, "Replay rate: %d Hz\n", real_rate);
real_rate_printed = 1;
}
}
return ret;
}
return 0;
}
static int set_channels(out123_handle *ao)
{
int chan = ao->channels;
if(ao->channels < 1) return 0;
return ioctl(ao->fn, AIOCSCHAN, (void *)chan);
}
static int set_format(out123_handle *ao)
{
int fmts;
if(ao->format == -1)
return 0;
switch(ao->format) {
case MPG123_ENC_SIGNED_16:
default:
fmts = AFMT_S16;
break;
case MPG123_ENC_UNSIGNED_8:
fmts = AFMT_U8;
break;
case MPG123_ENC_SIGNED_8:
fmts = AFMT_S8;
break;
case MPG123_ENC_ULAW_8:
fmts = AFMT_ULAW;
break;
}
return ioctl(ao->fn, AIOCSFMT, (void *)fmts);
}
static int reset_parameters(out123_handle *ao)
{
int ret;
ret = ioctl(ao->fn,AIOCRESET,NULL);
if(ret >= 0) ret = set_format(ai);
if(ret >= 0) ret = set_channels(ai);
if(ret >= 0) ret = set_rate(ai);
return ret;
}
static int open_mint(out123_handle *ao)
{
const char *dev = ao->device;
if(!ai) return -1;
if(!dev)
dev = "/dev/audio";
ao->fn = open(dev,O_WRONLY);
if(ao->fn < 0)
{
error1("Can't open %s!",dev);
return -1;
}
ioctl(ao->fn, AIOCGBLKSIZE, &outburst);
if(outburst > MAXOUTBURST)
outburst = MAXOUTBURST;
if(audio_reset_parameters(ai) < 0) {
close(ao->fn);
return -1;
}
return ao->fn;
}
static int get_formats_mint(out123_handle *ao)
{
int ret = 0;
int fmts;
if(ioctl(ao->fn,AIOCGFMTS,&fmts) < 0)
return -1;
if(fmts & AFMT_ULAW)
ret |= MPG123_ENC_ULAW_8;
if(fmts & AFMT_S16)
ret |= MPG123_ENC_SIGNED_16;
if(fmts & AFMT_U8)
ret |= MPG123_ENC_UNSIGNED_8;
if(fmts & AFMT_S8)
ret |= MPG123_ENC_SIGNED_8;
return ret;
}
static int write_mint(out123_handle *ao,unsigned char *buf,int len)
{
return write(ao->fn,buf,len);
}
static int close_mint(out123_handle *ao)
{
close (ao->fn);
return 0;
}
static void flush_mint(out123_handle *ao)
{
}
static int init_mint(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_mint;
ao->flush = flush_mint;
ao->write = write_mint;
ao->get_formats = get_formats_mint;
ao->close = close_mint;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "mint",
/* description */ "Audio output for MINT.",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_mint,
};

View File

@@ -0,0 +1,345 @@
/*
nas: audio output via NAS
copyright ?-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Martin Denn
*/
#include "out123_int.h"
#include <fcntl.h>
#include <audio/audiolib.h>
#include <audio/soundlib.h>
#include "debug.h"
typedef struct
{
AuServer *aud;
AuFlowID flow;
AuDeviceAttributes *da;
int numDevices;
char *buf;
AuUint32 buf_size;
AuUint32 buf_cnt;
AuBool data_sent;
AuBool finished;
} InfoRec, *InfoPtr;
/* seconds */
#define NAS_SOUND_PORT_DURATION (ao->device_buffer > 0. ? ao->device_buffer : 5)
#define NAS_SOUND_LOW_WATER_MARK 25 /* percent */
#define NAS_MAX_FORMAT 10 /* currently, there are 7 supported formats */
/* FIXME: stick this inside userptr inside out123_handle instead */
static InfoRec info;
/* NAS specific routines */
static void nas_sendData(AuServer *aud, InfoPtr i, AuUint32 numBytes)
{
if (numBytes < i->buf_cnt) {
AuWriteElement(aud, i->flow, 0, numBytes, i->buf, AuFalse, NULL);
memmove(i->buf, i->buf + numBytes, i->buf_cnt - numBytes);
i->buf_cnt = i->buf_cnt - numBytes;
}
else {
AuWriteElement(aud, i->flow, 0, i->buf_cnt, i->buf,
(numBytes > i->buf_cnt), NULL);
i->buf_cnt = 0;
}
i->data_sent = AuTrue;
}
static AuBool nas_eventHandler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *handler)
{
InfoPtr i = (InfoPtr) handler->data;
switch (ev->type)
{
case AuEventTypeMonitorNotify:
i->finished = AuTrue;
break;
case AuEventTypeElementNotify:
{
AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
switch (event->kind)
{
case AuElementNotifyKindLowWater:
nas_sendData(aud, i, event->num_bytes);
break;
case AuElementNotifyKindState:
switch (event->cur_state)
{
case AuStatePause:
if (event->reason != AuReasonUser)
nas_sendData(aud, i, event->num_bytes);
break;
case AuStateStop:
i->finished = AuTrue;
break;
}
}
}
}
return AuTrue;
}
/* 0 on error */
static int nas_createFlow(out123_handle *ao)
{
AuDeviceID device = AuNone;
AuElement elements[2];
unsigned char format;
AuUint32 buf_samples;
int i;
switch(ao->format) {
case MPG123_ENC_SIGNED_16:
default:
if (((char) *(short *)"x")=='x') /* ugly, but painless */
format = AuFormatLinearSigned16LSB; /* little endian */
else
format = AuFormatLinearSigned16MSB; /* big endian */
break;
case MPG123_ENC_UNSIGNED_8:
format = AuFormatLinearUnsigned8;
break;
case MPG123_ENC_SIGNED_8:
format = AuFormatLinearSigned8;
break;
case MPG123_ENC_ULAW_8:
format = AuFormatULAW8;
break;
}
/* look for an output device */
for (i = 0; i < AuServerNumDevices(info.aud); i++)
if (((AuDeviceKind(AuServerDevice(info.aud, i)) ==
AuComponentKindPhysicalOutput) &&
AuDeviceNumTracks(AuServerDevice(info.aud, i))
== ao->channels )) {
device = AuDeviceIdentifier(AuServerDevice(info.aud, i));
break;
}
if (device == AuNone) {
if(!AOQUIET)
error1( "Couldn't find an output device providing %d channels."
, ao->channels );
return 0;
}
/* set gain */
if(ao->gain >= 0) {
info.da = AuGetDeviceAttributes(info.aud, device, NULL);
if ((info.da)!=NULL) {
AuDeviceGain(info.da) = AuFixedPointFromSum(ao->gain, 0);
AuSetDeviceAttributes(info.aud, AuDeviceIdentifier(info.da),
AuCompDeviceGainMask, info.da, NULL);
}
else if(!AOQUIET)
error("audio/gain: setable Volume/PCM-Level not supported");
}
if (!(info.flow = AuCreateFlow(info.aud, NULL))) {
if(!AOQUIET)
error("Couldn't create flow");
return 0;
}
buf_samples = ao->rate * NAS_SOUND_PORT_DURATION;
AuMakeElementImportClient(&elements[0], /* element */
(unsigned short) ao->rate,
/* rate */
format, /* format */
ao->channels, /* channels */
AuTrue, /* ??? */
buf_samples, /* max samples */
(AuUint32) (buf_samples / 100
* NAS_SOUND_LOW_WATER_MARK),
/* low water mark */
0, /* num actions */
NULL); /* actions */
AuMakeElementExportDevice(&elements[1], /* element */
0, /* input */
device, /* device */
(unsigned short) ao->rate,
/* rate */
AuUnlimitedSamples, /* num samples */
0, /* num actions */
NULL); /* actions */
AuSetElements(info.aud, /* Au server */
info.flow, /* flow ID */
AuTrue, /* clocked */
2, /* num elements */
elements, /* elements */
NULL); /* return status */
AuRegisterEventHandler(info.aud, /* Au server */
AuEventHandlerIDMask, /* value mask */
0, /* type */
info.flow, /* id */
nas_eventHandler, /* callback */
(AuPointer) &info); /* data */
info.buf_size = buf_samples * ao->channels * AuSizeofFormat(format);
info.buf = (char *) malloc(info.buf_size);
if (info.buf == NULL) {
if(!AOQUIET)
error1("Unable to allocate input/output buffer of size %ld",
(long)info.buf_size);
return 0;
}
info.buf_cnt = 0;
info.data_sent = AuFalse;
info.finished = AuFalse;
AuStartFlow(info.aud, /* Au server */
info.flow, /* id */
NULL); /* status */
return 1; /* success */
}
static void flush_nas(out123_handle *ao)
{
AuEvent ev;
while ((!info.data_sent) && (!info.finished)) {
AuNextEvent(info.aud, AuTrue, &ev);
AuDispatchEvent(info.aud, &ev);
}
info.data_sent = AuFalse;
}
/* returning -1 on error, 0 on success... */
static int open_nas(out123_handle *ao)
{
if(!ao) return -1;
if (!(info.aud = AuOpenServer(ao->device, 0, NULL, 0, NULL, NULL)))
{
if (ao->device==NULL)
{
if(!AOQUIET)
error("could not open default NAS server");
} else
{
if(!AOQUIET)
error1("could not open NAS server %s\n", ao->device);
}
return -1;
}
info.buf_size = 0;
return 0;
}
static int get_formats_nas(out123_handle *ao)
{
int i, j, k, ret;
ret=0;
j = AuServerNumFormats(info.aud);
for (i=0; i<j; i++) {
k=AuServerFormat(info.aud,i);
switch (k)
{
case AuFormatULAW8:
ret |= MPG123_ENC_ULAW_8;
break;
case AuFormatLinearUnsigned8:
ret |= MPG123_ENC_UNSIGNED_8;
break;
case AuFormatLinearSigned8:
ret |= MPG123_ENC_SIGNED_8;
break;
case AuFormatLinearSigned16LSB:
ret |= MPG123_ENC_SIGNED_16;
break;
}
}
return ret;
}
static int write_nas(out123_handle *ao,unsigned char *buf,int len)
{
int buf_cnt = 0;
if (info.buf_size == 0)
if(!nas_createFlow(ao)) return -1;
while ((info.buf_cnt + (len - buf_cnt)) > info.buf_size) {
memcpy(info.buf + info.buf_cnt,
buf + buf_cnt,
(info.buf_size - info.buf_cnt));
buf_cnt += (info.buf_size - info.buf_cnt);
info.buf_cnt += (info.buf_size - info.buf_cnt);
flush_nas(ao);
}
memcpy(info.buf + info.buf_cnt,
buf + buf_cnt,
(len - buf_cnt));
info.buf_cnt += (len - buf_cnt);
return len;
}
static int close_nas(out123_handle *ao)
{
if (info.aud == NULL) {
return 0;
}
if (info.buf_size == 0) {
/* Au server opened, but not yet initialized */
AuCloseServer(info.aud);
return 0;
}
while (!info.finished) {
flush_nas(ao);
}
AuCloseServer(info.aud);
free(info.buf);
return 0;
}
static int init_nas(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_nas;
ao->flush = flush_nas;
ao->write = write_nas;
ao->get_formats = get_formats_nas;
ao->close = close_nas;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "nas",
/* description */ "Output audio using NAS (Network Audio System)",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_nas,
};

View File

@@ -0,0 +1,201 @@
/*
openal.c: audio output on OpenAL
copyright 1995-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Taihei Monma
*/
/* Need usleep(). */
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#include "out123_int.h"
#ifdef OPENAL_SUBDIR_OPENAL
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#elif defined(OPENAL_SUBDIR_AL)
#include <AL/al.h>
#include <AL/alc.h>
#else
#include <al.h>
#include <alc.h>
#endif
#include <errno.h>
#include <unistd.h>
#include "debug.h"
#define NUM_BUFFERS 16
#ifndef AL_FORMAT_MONO_FLOAT32
#define AL_FORMAT_MONO_FLOAT32 0x10010
#endif
#ifndef AL_FORMAT_STEREO_FLOAT32
#define AL_FORMAT_STEREO_FLOAT32 0x10011
#endif
typedef struct
{
ALCdevice *device;
ALCcontext *context;
ALuint source, buffer;
ALenum format;
ALsizei rate;
} mpg123_openal_t;
static int open_openal(out123_handle *ao)
{
mpg123_openal_t* al = (mpg123_openal_t*)ao->userptr;
al->device = alcOpenDevice(NULL);
al->context = alcCreateContext(al->device, NULL);
alcMakeContextCurrent(al->context);
alGenSources(1, &al->source);
al->rate = ao->rate;
if(ao->format == MPG123_ENC_SIGNED_16 && ao->channels == 2) al->format = AL_FORMAT_STEREO16;
else if(ao->format == MPG123_ENC_SIGNED_16 && ao->channels == 1) al->format = AL_FORMAT_MONO16;
else if(ao->format == MPG123_ENC_UNSIGNED_8 && ao->channels == 2) al->format = AL_FORMAT_STEREO8;
else if(ao->format == MPG123_ENC_UNSIGNED_8 && ao->channels == 1) al->format = AL_FORMAT_MONO8;
else if(ao->format == MPG123_ENC_FLOAT_32 && ao->channels == 2) al->format = AL_FORMAT_STEREO_FLOAT32;
else if(ao->format == MPG123_ENC_FLOAT_32 && ao->channels == 1) al->format = AL_FORMAT_MONO_FLOAT32;
return 0;
}
static int get_formats_openal(out123_handle *ao)
{
return MPG123_ENC_SIGNED_16|MPG123_ENC_UNSIGNED_8|((alIsExtensionPresent((ALubyte*)"AL_EXT_float32") == AL_TRUE) ? MPG123_ENC_FLOAT_32 : 0);
}
static int write_openal(out123_handle *ao, unsigned char *buf, int len)
{
ALint state, n;
mpg123_openal_t* al = (mpg123_openal_t*)ao->userptr;
alGetSourcei(al->source, AL_BUFFERS_QUEUED, &n);
if(n < NUM_BUFFERS)
{
alGenBuffers(1, &al->buffer);
}
else
{
alGetSourcei(al->source, AL_SOURCE_STATE, &state);
if(state != AL_PLAYING)
{
alSourcePlay(al->source);
}
while(alGetSourcei(al->source, AL_BUFFERS_PROCESSED, &n), n == 0)
{
usleep(10000);
}
alSourceUnqueueBuffers(al->source, 1, &al->buffer);
}
alBufferData(al->buffer, al->format, buf, len, al->rate);
alSourceQueueBuffers(al->source, 1, &al->buffer);
return len;
}
static int close_openal(out123_handle *ao)
{
ALint state, n;
mpg123_openal_t* al = (mpg123_openal_t*)ao->userptr;
if (al)
{
/* wait until all buffers are consumed */
while(alGetSourcei(al->source, AL_SOURCE_STATE, &state), state == AL_PLAYING)
{
usleep(10000);
}
/* free all processed buffers */
while(alGetSourcei(al->source, AL_BUFFERS_PROCESSED, &n), n > 0)
{
alSourceUnqueueBuffers(al->source, 1, &al->buffer);
alDeleteBuffers(1, &al->buffer);
}
alDeleteSources(1, &al->source);
alcMakeContextCurrent(NULL);
alcDestroyContext(al->context);
alcCloseDevice(al->device);
}
return 0;
}
static void flush_openal(out123_handle *ao)
{
ALint n;
mpg123_openal_t* al = (mpg123_openal_t*)ao->userptr;
if (al)
{
/* stop playing and flush all buffers */
alSourceStop(al->source);
while(alGetSourcei(al->source, AL_BUFFERS_PROCESSED, &n), n > 0)
{
alSourceUnqueueBuffers(al->source, 1, &al->buffer);
alDeleteBuffers(1, &al->buffer);
}
}
}
static int deinit_openal(out123_handle* ao)
{
/* Free up memory */
if(ao->userptr)
{
free( ao->userptr );
ao->userptr = NULL;
}
/* Success */
return 0;
}
static int init_openal(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_openal;
ao->flush = flush_openal;
ao->write = write_openal;
ao->get_formats = get_formats_openal;
ao->close = close_openal;
ao->deinit = deinit_openal;
/* Allocate memory for data structure */
ao->userptr = malloc( sizeof( mpg123_openal_t ) );
if(ao->userptr==NULL)
{
if(!AOQUIET)
error("failed to malloc memory for 'mpg123_openal_t'");
return -1;
}
memset( ao->userptr, 0, sizeof(mpg123_openal_t) );
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "openal",
/* description */ "Output audio using OpenAL.",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_openal,
};

View File

@@ -0,0 +1,664 @@
/*
os2: OS/2 RealTime DART Engine
copyright 1998-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Samuel Audet
*/
#include "out123_int.h"
#define INCL_OS2MM
#define INCL_DOS
#define INCL_VIO
#define INCL_KBD
#include <os2.h>
#include <os2me.h>
#include <stdlib.h>
#include <ctype.h>
#include "debug.h"
/* complementary audio parameters */
int numbuffers = 5; /* total audio buffers, _bare_ minimum = 4 (cuz of prio boost check) */
int audiobufsize = 4884;
int lockdevice = FALSE;
USHORT volume = 100;
char *boostprio = NULL;
char *normalprio = NULL;
unsigned char boostclass = 3, normalclass = 2;
signed char boostdelta = 0, normaldelta = 31;
unsigned char mmerror[160] = {0};
int playingframe;
/* audio buffers */
static ULONG ulMCIBuffers;
static MCI_AMP_OPEN_PARMS maop = {0};
static MCI_MIXSETUP_PARMS mmp = {0};
static MCI_BUFFER_PARMS mbp = {0};
static MCI_GENERIC_PARMS mgp = {0};
static MCI_SET_PARMS msp = {0};
static MCI_STATUS_PARMS mstatp = {0};
static MCI_MIX_BUFFER *MixBuffers = NULL;
typedef struct
{
MCI_MIX_BUFFER *NextBuffer;
int frameNum;
} BUFFERINFO;
BUFFERINFO *bufferinfo = NULL;
static HEV dataplayed = 0;
static ULONG resetcount;
static BOOL paused = FALSE;
static MCI_MIX_BUFFER *tobefilled, *playingbuffer = NULL, playedbuffer;
static void *pBufferplayed;
static BOOL nomoredata,nobuffermode,justflushed;
static TIB *mainthread; /* thread info to set thread priority */
ULONG keyboardtid;
static LONG APIENTRY DARTEvent(ULONG ulStatus, MCI_MIX_BUFFER *PlayedBuffer, ULONG ulFlags)
{
switch(ulFlags)
{
case MIX_STREAM_ERROR | MIX_WRITE_COMPLETE: /* error occur in device */
if ( ulStatus == ERROR_DEVICE_UNDERRUN)
/* Write buffers to rekick off the amp mixer. */
mmp.pmixWrite( mmp.ulMixHandle, MixBuffers, ulMCIBuffers );
break;
case MIX_WRITE_COMPLETE: /* for playback */
playingbuffer = ((BUFFERINFO *) PlayedBuffer->ulUserParm)->NextBuffer;
/* the next three lines are only useful to audio_playing_samples() */
playedbuffer = *PlayedBuffer;
playedbuffer.pBuffer = pBufferplayed;
memcpy(playedbuffer.pBuffer, PlayedBuffer->pBuffer, PlayedBuffer->ulBufferLength);
/* just too bad, the decoder fell behind... here we just keep the
buffer to be filled in front of the playing one so that when the
decoder kicks back in, we'll hear it in at the right time */
if(tobefilled == playingbuffer)
{
tobefilled = ((BUFFERINFO *) playingbuffer->ulUserParm)->NextBuffer;
nomoredata = TRUE;
}
else
{
playingframe = ((BUFFERINFO *) playingbuffer->ulUserParm)->frameNum;
/* if we're about to be short of decoder's data
(2nd ahead buffer not filled), let's boost its priority! */
if(tobefilled == ( (BUFFERINFO *) ((BUFFERINFO *) playingbuffer->ulUserParm)->NextBuffer->ulUserParm)->NextBuffer)
DosSetPriority(PRTYS_THREAD,boostclass,boostdelta,mainthread->tib_ptib2->tib2_ultid);
}
/* empty the played buffer in case it doesn't get filled back */
memset(PlayedBuffer->pBuffer,0,PlayedBuffer->ulBufferLength);
DosPostEventSem(dataplayed);
mmp.pmixWrite( mmp.ulMixHandle, PlayedBuffer, 1 );
break;
} /* end switch */
return( TRUE );
} /* end DARTEvent */
static void MciError(ULONG ulError)
{
unsigned char buffer[128];
ULONG rc;
rc = mciGetErrorString(ulError, buffer, sizeof(buffer));
if (rc == MCIERR_SUCCESS)
sprintf(mmerror,"MCI Error %d: %s",ULONG_LOWD(ulError),buffer);
else
sprintf(mmerror,"MCI Error %d: Cannot query error message.",ULONG_LOWD(rc));
error1("%s",mmerror);
}
static int set_volume(out123_handle *ao, USHORT setvolume)
{
if(setvolume > 100) setvolume = 100;
volume = setvolume; /* useful when device is closed and reopened */
if(maop.usDeviceID)
{
memset(&msp,0,sizeof(msp));
msp.ulAudio = MCI_SET_AUDIO_ALL;
msp.ulLevel = setvolume;
mciSendCommand(maop.usDeviceID, MCI_SET,
MCI_WAIT | MCI_SET_AUDIO | MCI_SET_VOLUME,
&msp, 0);
}
return setvolume;
}
int open_os2(out123_handle *ao)
{
ULONG rc,i;
char *temp;
ULONG openflags;
PPIB ppib;
USHORT bits;
const char *dev = ao->device;
if(maop.usDeviceID) return (maop.usDeviceID);
if(!ao) return -1;
if(!dev) dev = "0";
if(ao->rate < 0) ao->rate = 44100;
if(ao->channels < 0) ao->channels = 2;
if(ao->format < 0) ao->format = MPG123_ENC_SIGNED_16;
if(ao->format == MPG123_ENC_SIGNED_16)
bits = 16;
else if(ao->format == MPG123_ENC_UNSIGNED_8)
bits = 8;
else return -1;
/* open the mixer device */
memset (&maop, 0, sizeof(maop));
maop.usDeviceID = 0;
maop.pszDeviceType = (PSZ) MAKEULONG(MCI_DEVTYPE_AUDIO_AMPMIX, atoi(dev));
openflags = MCI_WAIT | MCI_OPEN_TYPE_ID;
if(!lockdevice) openflags |= MCI_OPEN_SHAREABLE;
rc = mciSendCommand(0, MCI_OPEN, openflags, &maop, 0);
if (ULONG_LOWD(rc) != MCIERR_SUCCESS)
{
MciError(rc);
maop.usDeviceID = 0;
return(-1);
}
/* volume in ao->gain ?? */
/* Set the MCI_MIXSETUP_PARMS data structure to match the audio stream. */
memset(&mmp, 0, sizeof(mmp));
mmp.ulBitsPerSample = bits;
mmp.ulFormatTag = MCI_WAVE_FORMAT_PCM;
mmp.ulSamplesPerSec = ao->rate;
mmp.ulChannels = ao->channels;
/* Setup the mixer for playback of wave data */
mmp.ulFormatMode = MCI_PLAY;
mmp.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
mmp.pmixEvent = DARTEvent;
rc = mciSendCommand( maop.usDeviceID,
MCI_MIXSETUP,
MCI_WAIT | MCI_MIXSETUP_INIT,
&mmp,
0 );
if ( ULONG_LOWD(rc) != MCIERR_SUCCESS )
{
MciError(rc);
maop.usDeviceID = 0;
return(-1);
}
volume = set_volume(ao,volume);
/* Set up the BufferParms data structure and allocate
* device buffers from the Amp-Mixer */
memset(&mbp, 0, sizeof(mbp));
free(MixBuffers);
free(bufferinfo);
if(numbuffers < 5) numbuffers = 5;
if(numbuffers > 200) numbuffers = 200;
MixBuffers = calloc(numbuffers, sizeof(*MixBuffers));
bufferinfo = calloc(numbuffers, sizeof(*bufferinfo));
ulMCIBuffers = numbuffers;
mbp.ulNumBuffers = ulMCIBuffers;
/* mbp.ulBufferSize = mmp.ulBufferSize; */
/* I don't like this... they must be smaller than 64KB or else the
engine needs major rewrite */
mbp.ulBufferSize = audiobufsize;
mbp.pBufList = MixBuffers;
rc = mciSendCommand( maop.usDeviceID,
MCI_BUFFER,
MCI_WAIT | MCI_ALLOCATE_MEMORY,
(PVOID) &mbp,
0 );
if ( ULONG_LOWD(rc) != MCIERR_SUCCESS )
{
MciError(rc);
maop.usDeviceID = 0;
return(-1);
}
pBufferplayed = playedbuffer.pBuffer = calloc(1,audiobufsize);
ulMCIBuffers = mbp.ulNumBuffers; /* never know! */
/* Fill all device buffers with zeros and set linked list */
for(i = 0; i < ulMCIBuffers; i++)
{
MixBuffers[i].ulFlags = 0;
MixBuffers[i].ulBufferLength = mbp.ulBufferSize;
memset(MixBuffers[i].pBuffer, 0, MixBuffers[i].ulBufferLength);
MixBuffers[i].ulUserParm = (ULONG) &bufferinfo[i];
bufferinfo[i].NextBuffer = &MixBuffers[i+1];
}
bufferinfo[i-1].NextBuffer = &MixBuffers[0];
/* Create a semaphore to know when data has been played by the DART thread */
DosCreateEventSem(NULL,&dataplayed,0,FALSE);
playingbuffer = &MixBuffers[0];
tobefilled = &MixBuffers[1];
playingframe = 0;
nomoredata = TRUE;
nobuffermode = FALSE;
justflushed = FALSE;
if(boostprio)
{
temp = alloca(strlen(boostprio)+1);
strcpy(temp,boostprio);
boostdelta = atoi(temp+1);
*(temp+1) = 0;
boostclass = atoi(temp);
}
if(boostclass > 4) boostdelta = 3;
if(boostdelta > 31) boostdelta = 31;
if(boostdelta < -31) boostdelta = -31;
if(normalprio)
{
temp = alloca(strlen(normalprio)+1);
strcpy(temp,normalprio);
normaldelta = atoi(temp+1);
*(temp+1) = 0;
normalclass = atoi(temp);
}
if(normalclass > 4) normaldelta = 3;
if(normaldelta > 31) normaldelta = 31;
if(normaldelta < -31) normaldelta = -31;
DosGetInfoBlocks(&mainthread,&ppib); /* ppib not needed, but makes some DOSCALLS.DLL crash */
DosSetPriority(PRTYS_THREAD,boostclass,boostdelta,mainthread->tib_ptib2->tib2_ultid);
/* Write buffers to kick off the amp mixer. see DARTEvent() */
rc = mmp.pmixWrite( mmp.ulMixHandle,
MixBuffers,
ulMCIBuffers );
return maop.usDeviceID;
}
static int write_os2(out123_handle *ao,unsigned char *buf,int len)
{
/* if we're too quick, let's wait */
if(nobuffermode)
{
MCI_MIX_BUFFER *temp = playingbuffer;
while(
(tobefilled != (temp = ((BUFFERINFO *) temp->ulUserParm)->NextBuffer)) &&
(tobefilled != (temp = ((BUFFERINFO *) temp->ulUserParm)->NextBuffer)) &&
(tobefilled != (temp = ((BUFFERINFO *) temp->ulUserParm)->NextBuffer)) )
{
DosResetEventSem(dataplayed,&resetcount);
DosWaitEventSem(dataplayed, -1);
temp = playingbuffer;
}
} else {
while(tobefilled == playingbuffer)
{
DosResetEventSem(dataplayed,&resetcount);
DosWaitEventSem(dataplayed, -1);
}
}
if (justflushed) {
justflushed = FALSE;
} else {
nomoredata = FALSE;
memcpy(tobefilled->pBuffer, buf, len);
tobefilled->ulBufferLength = len;
// ((BUFFERINFO *) tobefilled->ulUserParm)->frameNum = fr->frameNum;
/* if we're out of the water (3rd ahead buffer filled),
let's reduce our priority */
if(tobefilled == ( (BUFFERINFO *) ( (BUFFERINFO *) ((BUFFERINFO *) playingbuffer->ulUserParm)->NextBuffer->ulUserParm)->NextBuffer->ulUserParm)->NextBuffer)
DosSetPriority(PRTYS_THREAD,normalclass,normaldelta,mainthread->tib_ptib2->tib2_ultid);
tobefilled = ((BUFFERINFO *) tobefilled->ulUserParm)->NextBuffer;
}
return len;
}
#if 0
static int write_os2(out123_handle *ao,unsigned char *buf,int len)
{
if(len > audiobufsize || !playingbuffer) return -1;
if(mmp.ulBitsPerSample == 16)
ao->format = MPG123_ENC_SIGNED_16;
else if(mmp.ulBitsPerSample == 8)
ao->format = MPG123_ENC_UNSIGNED_8;
else return -1;
ao->rate = mmp.ulSamplesPerSec;
ao->channels = mmp.ulChannels;
if(buf && len)
{
ULONG rc;
int upto;
mstatp.ulItem = MCI_STATUS_POSITION;
rc = mciSendCommand( maop.usDeviceID,
MCI_STATUS,
MCI_STATUS_ITEM | MCI_WAIT,
&mstatp,
0 );
if ( ULONG_LOWD(rc) != MCIERR_SUCCESS )
{
MciError(rc);
maop.usDeviceID = 0;
return(-1);
}
/* this is hypocrite...
DART returns the value in ulReturn instead of ulValue,
also it returns in milliseconds and not MMTIME... arg */
upto = (mstatp.ulReturn-playedbuffer.ulTime) * mmp.ulSamplesPerSec / 1000;
upto *= mmp.ulChannels * (mmp.ulBitsPerSample>>3);
/* if a timing problem occurs, let's at least not crash */
if(upto > playingbuffer->ulBufferLength)
upto = playingbuffer->ulBufferLength;
if(len < upto) {
memcpy(buf,(char *) (playingbuffer->pBuffer)+upto-len, len);
} else {
memcpy(buf,(char *) playedbuffer.pBuffer+playedbuffer.ulBufferLength-(len-upto),len-upto);
memcpy(buf+(len-upto),playingbuffer->pBuffer,upto);
}
}
return 0;
}
#endif
/*
static int audio_nobuffermode(out123_handle *ao, int setnobuffermode)
{
nobuffermode = setnobuffermode;
return TRUE;
}
int audio_trash_buffers(out123_handle *ao)
{
int i;
justflushed = TRUE;
// Fill all device buffers with zeros
for(i = 0; i < ulMCIBuffers; i++)
memset(MixBuffers[i].pBuffer, 0, MixBuffers[i].ulBufferLength);
tobefilled = ((BUFFERINFO *) playingbuffer->ulUserParm)->NextBuffer;
nomoredata = TRUE;
return TRUE;
}
*/
static int close_os2(out123_handle *ao)
{
ULONG rc;
if(!maop.usDeviceID)
return 0;
while(!nomoredata)
{
DosResetEventSem(dataplayed,&resetcount);
DosWaitEventSem(dataplayed, -1);
}
playingbuffer = NULL;
DosCloseEventSem(dataplayed);
dataplayed = 0;
free(pBufferplayed);
rc = mciSendCommand( maop.usDeviceID,
MCI_BUFFER,
MCI_WAIT | MCI_DEALLOCATE_MEMORY,
&mbp,
0 );
if ( ULONG_LOWD(rc) != MCIERR_SUCCESS )
{
MciError(rc);
return(-1);
}
free(bufferinfo);
free(MixBuffers);
bufferinfo = NULL;
MixBuffers = NULL;
memset(&mbp, 0, sizeof(mbp));
rc = mciSendCommand( maop.usDeviceID,
MCI_CLOSE,
MCI_WAIT ,
&mgp,
0 );
if ( ULONG_LOWD(rc) != MCIERR_SUCCESS )
{
MciError(rc);
return(-1);
}
memset(&maop, 0, sizeof(maop));
return 0;
}
/*
* get formats for specific channel/rate parameters
*/
int get_formats_os2(out123_handle *ao)
{
int fmts = 0;
ULONG rc;
MCI_MIXSETUP_PARMS mmptemp = {0};
mmp.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
mmp.pmixEvent = DARTEvent;
mmptemp.ulFormatMode = MCI_PLAY;
mmptemp.ulSamplesPerSec = ao->rate;
mmptemp.ulChannels = ao->channels;
mmptemp.ulFormatTag = MCI_WAVE_FORMAT_PCM;
mmptemp.ulBitsPerSample = 16;
rc = mciSendCommand( maop.usDeviceID,
MCI_MIXSETUP,
MCI_WAIT | MCI_MIXSETUP_QUERYMODE,
&mmptemp,
0 );
if((ULONG_LOWD(rc) == MCIERR_SUCCESS) && (rc != 0x4000)) /* undocumented */
fmts = fmts | MPG123_ENC_SIGNED_16;
mmptemp.ulFormatTag = MCI_WAVE_FORMAT_PCM;
mmptemp.ulBitsPerSample = 8;
rc = mciSendCommand( maop.usDeviceID,
MCI_MIXSETUP,
MCI_WAIT | MCI_MIXSETUP_QUERYMODE,
&mmptemp,
0 );
if((ULONG_LOWD(rc) == MCIERR_SUCCESS) && (rc != 0x4000)) /* undocumented */
fmts = fmts | MPG123_ENC_UNSIGNED_8;
mmptemp.ulFormatTag = MCI_WAVE_FORMAT_ALAW;
mmptemp.ulBitsPerSample = 8;
rc = mciSendCommand( maop.usDeviceID,
MCI_MIXSETUP,
MCI_WAIT | MCI_MIXSETUP_QUERYMODE,
&mmptemp,
0 );
if((ULONG_LOWD(rc) == MCIERR_SUCCESS) && (rc != 0x4000)) /* undocumented */
fmts = fmts | MPG123_ENC_ALAW_8;
mmptemp.ulFormatTag = MCI_WAVE_FORMAT_MULAW;
mmptemp.ulBitsPerSample = 8;
rc = mciSendCommand( maop.usDeviceID,
MCI_MIXSETUP,
MCI_WAIT | MCI_MIXSETUP_QUERYMODE,
&mmptemp,
0 );
if((ULONG_LOWD(rc) == MCIERR_SUCCESS) && (rc != 0x4000)) /* undocumented */
fmts = fmts | MPG123_ENC_ULAW_8;
return fmts;
}
static int get_devices_os2(char *info, int deviceid)
{
char buffer[128];
MCI_SYSINFO_PARMS mip;
if(deviceid && info)
{
MCI_SYSINFO_LOGDEVICE mid;
mip.pszReturn = buffer;
mip.ulRetSize = sizeof(buffer);
mip.usDeviceType = MCI_DEVTYPE_AUDIO_AMPMIX;
mip.ulNumber = deviceid;
mciSendCommand(0,
MCI_SYSINFO,
MCI_WAIT | MCI_SYSINFO_INSTALLNAME,
&mip,
0);
mip.ulItem = MCI_SYSINFO_QUERY_DRIVER;
mip.pSysInfoParm = &mid;
strcpy(mid.szInstallName,buffer);
mciSendCommand(0,
MCI_SYSINFO,
MCI_WAIT | MCI_SYSINFO_ITEM,
&mip,
0);
strcpy(info,mid.szProductInfo);
return deviceid;
} else {
int number;
mip.pszReturn = buffer;
mip.ulRetSize = sizeof(buffer);
mip.usDeviceType = MCI_DEVTYPE_AUDIO_AMPMIX;
mciSendCommand(0,
MCI_SYSINFO,
MCI_WAIT | MCI_SYSINFO_QUANTITY,
&mip,
0);
number = atoi(mip.pszReturn);
return number;
}
}
static void flush_os2(out123_handle *ao)
{
}
static int init_os2(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_os2;
ao->flush = flush_os2;
ao->write = write_os2;
ao->get_formats = get_formats_os2;
ao->close = close_os2;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "os2",
/* description */ "Audio output for OS2.",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_os2,
};

View File

@@ -0,0 +1,321 @@
/*
oss: audio output via Open Sound System
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp
*/
#include "out123_int.h"
#include <sys/ioctl.h>
#include <fcntl.h>
#ifdef HAVE_LINUX_SOUNDCARD_H
#include <linux/soundcard.h>
#endif
#ifdef HAVE_SYS_SOUNDCARD_H
#include <sys/soundcard.h>
#endif
#ifdef HAVE_MACHINE_SOUNDCARD_H
#include <machine/soundcard.h>
#endif
#ifndef AFMT_S16_NE
# ifdef OSS_BIG_ENDIAN
# define AFMT_S16_NE AFMT_S16_BE
# else
# define AFMT_S16_NE AFMT_S16_LE
# endif
#endif
#ifndef AFMT_U16_NE
# ifdef OSS_BIG_ENDIAN
# define AFMT_U16_NE AFMT_U16_BE
# else
# define AFMT_U16_NE AFMT_U16_LE
# endif
#endif
#include "debug.h"
struct oss_stuff
{
int fragment; /* size of one fragment */
int nfrag; /* number of fragments */
};
static int rate_best_match_oss(out123_handle *ao)
{
int ret,dsp_rate;
if(!ao || ao->fn < 0 || ao->rate < 0) return -1;
dsp_rate = ao->rate;
ret = ioctl(ao->fn, SNDCTL_DSP_SPEED,&dsp_rate);
if(ret < 0) return ret;
ao->rate = dsp_rate;
return 0;
}
static int set_rate_oss(out123_handle *ao)
{
int dsp_rate;
int ret = 0;
if(ao->rate >= 0) {
dsp_rate = ao->rate;
ret = ioctl(ao->fn, SNDCTL_DSP_SPEED,&dsp_rate);
}
return ret;
}
static int set_channels_oss(out123_handle *ao)
{
int chan = ao->channels - 1;
int ret;
if(ao->channels < 0) return 0;
ret = ioctl(ao->fn, SNDCTL_DSP_STEREO, &chan);
if(chan != (ao->channels-1)) return -1;
return ret;
}
static int set_format_oss(out123_handle *ao)
{
int fmts;
int sf,ret;
if(ao->format == -1) return 0;
switch(ao->format) {
case MPG123_ENC_SIGNED_16:
default:
fmts = AFMT_S16_NE;
break;
case MPG123_ENC_UNSIGNED_8:
fmts = AFMT_U8;
break;
case MPG123_ENC_SIGNED_8:
fmts = AFMT_S8;
break;
case MPG123_ENC_ULAW_8:
fmts = AFMT_MU_LAW;
break;
case MPG123_ENC_ALAW_8:
fmts = AFMT_A_LAW;
break;
case MPG123_ENC_UNSIGNED_16:
fmts = AFMT_U16_NE;
break;
}
sf = fmts;
ret = ioctl(ao->fn, SNDCTL_DSP_SETFMT, &fmts);
if(sf != fmts) return -1;
return ret;
}
static int reset_parameters_oss(out123_handle *ao)
{
int ret;
ret = ioctl(ao->fn, SNDCTL_DSP_RESET, NULL);
if(ret < 0 && !AOQUIET) error("Can't reset audio!");
ret = set_format_oss(ao);
if (ret == -1) goto err;
ret = set_channels_oss(ao);
if (ret == -1) goto err;
ret = set_rate_oss(ao);
if (ret == -1) goto err;
/* Careful here. As per OSS v1.1, the next ioctl() commits the format
* set above, so we must issue SNDCTL_DSP_RESET before we're allowed to
* change it again. [dk]
*/
/* FIXME: this needs re-enabled (but not using global variables this time):
if (ioctl(ao->fn, SNDCTL_DSP_GETBLKSIZE, &outburst) == -1 ||
outburst > MAXOUTBURST)
outburst = MAXOUTBURST;
*/
err:
return ret;
}
static int open_oss(out123_handle *ao)
{
char usingdefdev = 0;
const char *dev;
if(!ao) return -1;
dev = ao->device;
if(!dev) {
dev = "/dev/dsp";
usingdefdev = 1;
}
ao->fn = open(dev,O_WRONLY);
if(ao->fn < 0)
{
if(usingdefdev) {
dev = "/dev/sound/dsp";
ao->fn = open(dev,O_WRONLY);
if(ao->fn < 0) {
if(!AOQUIET) error("Can't open default sound device!");
return -1;
}
} else {
if(!AOQUIET) error1("Can't open %s!",dev);
return -1;
}
}
if(reset_parameters_oss(ao) < 0) {
close(ao->fn);
return -1;
}
if(ao->gain >= 0) {
int e,mask;
e = ioctl(ao->fn , SOUND_MIXER_READ_DEVMASK ,&mask);
if(e < 0) {
if(!AOQUIET) error("audio/gain: Can't get audio device features list.");
}
else if(mask & SOUND_MASK_PCM) {
int gain = (ao->gain<<8)|(ao->gain);
e = ioctl(ao->fn, SOUND_MIXER_WRITE_PCM , &gain);
}
else if(!(mask & SOUND_MASK_VOLUME)) {
if(!AOQUIET) error1("audio/gain: setable Volume/PCM-Level not supported by your audio device: %#04x",mask);
}
else {
int gain = (ao->gain<<8)|(ao->gain);
e = ioctl(ao->fn, SOUND_MIXER_WRITE_VOLUME , &gain);
}
}
return ao->fn;
}
/*
* get formats for specific channel/rate parameters
*/
static int get_formats_oss(out123_handle *ao)
{
int fmt = 0;
int r = ao->rate;
int c = ao->channels;
int i;
static int fmts[] = {
MPG123_ENC_ULAW_8 , MPG123_ENC_SIGNED_16 ,
MPG123_ENC_UNSIGNED_8 , MPG123_ENC_SIGNED_8 ,
MPG123_ENC_UNSIGNED_16 , MPG123_ENC_ALAW_8
};
/* Reset is required before we're allowed to set the new formats. [dk] */
ioctl(ao->fn, SNDCTL_DSP_RESET, NULL);
for(i=0;i<6;i++) {
ao->format = fmts[i];
if(set_format_oss(ao) < 0) {
continue;
}
ao->channels = c;
if(set_channels_oss(ao) < 0) {
continue;
}
ao->rate = r;
if(rate_best_match_oss(ao) < 0) {
continue;
}
if( (ao->rate*100 > r*(100-AUDIO_RATE_TOLERANCE)) && (ao->rate*100 < r*(100+AUDIO_RATE_TOLERANCE)) ) {
fmt |= fmts[i];
}
}
#if 0
if(ioctl(ao->fn,SNDCTL_DSP_GETFMTS,&fmts) < 0) {
if(!AOQUIET) error("Failed to get SNDCTL_DSP_GETFMTS");
return -1;
}
if(fmts & AFMT_MU_LAW)
ret |= MPG123_ENC_ULAW_8;
if(fmts & AFMT_S16_NE)
ret |= MPG123_ENC_SIGNED_16;
if(fmts & AFMT_U8)
ret |= MPG123_ENC_UNSIGNED_8;
if(fmts & AFMT_S8)
ret |= MPG123_ENC_SIGNED_8;
if(fmts & AFMT_U16_NE)
ret |= MPG123_ENC_UNSIGNED_16;
if(fmts & AFMT_A_LAW)
ret |= MPG123_ENC_ALAW_8;
#endif
return fmt;
}
static int write_oss(out123_handle *ao,unsigned char *buf,int len)
{
return write(ao->fn,buf,len);
}
static int close_oss(out123_handle *ao)
{
close(ao->fn);
return 0;
}
static void flush_oss(out123_handle *ao)
{
}
static int init_oss(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_oss;
ao->flush = flush_oss;
ao->write = write_oss;
ao->get_formats = get_formats_oss;
ao->close = close_oss;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "oss",
/* description */ "Output audio using OSS",
/* revision */ "$Rev: 4021 $",
/* handle */ NULL,
/* init_output */ init_oss,
};

View File

@@ -0,0 +1,330 @@
/*
portaudio: audio output via PortAudio cross-platform audio API
copyright 2006-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Nicholas J. Humfrey
*/
/* Need usleep(). */
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#include "out123_int.h"
#include <math.h>
#include <portaudio.h>
#ifdef WIN32
#include <windows.h>
#endif
/* Including the sfifo code locally, to avoid module linkage issues. */
#define SFIFO_STATIC
#include "sfifo.c"
#include "debug.h"
#define SAMPLE_SIZE (2)
#define FRAMES_PER_BUFFER (256)
#define FIFO_DURATION (ao->device_buffer > 0. ? ao->device_buffer : 0.5f)
typedef struct {
PaStream *stream;
sfifo_t fifo;
int finished;
} mpg123_portaudio_t;
#ifdef PORTAUDIO18
#define PaTime PaTimestamp
#define Pa_IsStreamActive Pa_StreamActive
#endif
/* Some busy waiting. Proper stuff like semaphores might add
dependencies (POSIX) that the platform does not know. */
static void ms_sleep(int milliseconds)
{
#ifdef WIN32
Sleep(milliseconds);
#else
usleep(milliseconds*1000);
#endif
}
#ifdef PORTAUDIO18
static int paCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTime outTime, void *userData )
#else
static int paCallback(
const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
#endif
{
out123_handle *ao = userData;
mpg123_portaudio_t *pa = (mpg123_portaudio_t*)ao->userptr;
unsigned long bytes = framesPerBuffer * SAMPLE_SIZE * ao->channels;
int bytes_avail;
int bytes_read;
while((bytes_avail=sfifo_used(&pa->fifo))<bytes && !pa->finished)
{
int ms = (bytes-bytes_avail)/ao->framesize*1000/ao->rate;
debug3("waiting for more input, %d ms missing (%i < %lu)"
, ms, bytes_avail, bytes);
ms_sleep(ms/10);
}
if(bytes_avail > bytes)
bytes_avail = bytes;
bytes_read = sfifo_read(&pa->fifo, outputBuffer, bytes_avail);
if(bytes_read != bytes_avail)
warning2("Error reading from the FIFO (wanted=%d, bytes_read=%d).\n"
, bytes_avail, bytes_read);
if(bytes_read < 0)
bytes_read = 0;
/* Ensure that any remaining space is filled with zero bytes. */
if(bytes_read >= 0 && bytes_read < bytes)
memset((char*)outputBuffer+bytes_read, 0, bytes-bytes_read);
debug1("callback successfully passed along %i B", bytes_read);
return 0;
}
static int open_portaudio(out123_handle *ao)
{
mpg123_portaudio_t *pa = (mpg123_portaudio_t*)ao->userptr;
PaError err;
pa->finished = 0;
/* Open an audio I/O stream. */
if (ao->rate > 0 && ao->channels >0 ) {
err = Pa_OpenDefaultStream(
&pa->stream,
0, /* no input channels */
ao->channels, /* number of output channels */
paInt16, /* signed 16-bit samples */
ao->rate, /* sample rate */
FRAMES_PER_BUFFER, /* frames per buffer */
#ifdef PORTAUDIO18
0, /* number of buffers, if zero then use default minimum */
#endif
paCallback, /* no callback - use blocking IO */
ao );
if( err != paNoError ) {
if(!AOQUIET)
error1( "Failed to open PortAudio default stream: %s"
, Pa_GetErrorText(err) );
return -1;
}
/* Initialise FIFO */
sfifo_init( &pa->fifo, ao->rate * FIFO_DURATION * SAMPLE_SIZE *ao->channels );
}
return(0);
}
static int get_formats_portaudio(out123_handle *ao)
{
/* Only implemented Signed 16-bit audio for now */
return MPG123_ENC_SIGNED_16;
}
static int write_portaudio(out123_handle *ao, unsigned char *buf, int len)
{
mpg123_portaudio_t *pa = (mpg123_portaudio_t*)ao->userptr;
PaError err;
int len_remain = len;
/* Some busy waiting, but feed what is possible. */
while(len_remain) /* Note: input len is multiple of framesize! */
{
int block = sfifo_space(&pa->fifo);
block -= block % ao->framesize;
debug1("space for writing: %i", block);
if(block > len_remain)
block = len_remain;
if(block)
{
sfifo_write(&pa->fifo, buf, block);
len_remain -= block;
buf += block;
/* Start stream if not ative and 50 % full.*/
if(sfifo_used(&pa->fifo) > (sfifo_size(&pa->fifo)/2))
{
pa->finished = 0;
err = Pa_IsStreamActive( pa->stream );
if (err == 0) {
err = Pa_StartStream( pa->stream );
if( err != paNoError ) {
if(!AOQUIET)
error1( "Failed to start PortAudio stream: %s"
, Pa_GetErrorText(err) );
return -1; /* triggering exit here is not good, better handle that somehow... */
}
else
debug("started stream");
} else if (err < 0)
{
if(!AOQUIET)
error1( "Failed to check state of PortAudio stream: %s"
, Pa_GetErrorText(err) );
return -1;
}
else
debug("stream already active");
}
}
if(len_remain)
{
debug1("Still need to write %d bytes, sleeping a bit.", len_remain);
ms_sleep(0.1*FIFO_DURATION*1000);
}
}
return len;
}
static int close_portaudio(out123_handle *ao)
{
mpg123_portaudio_t *pa = (mpg123_portaudio_t*)ao->userptr;
PaError err;
int stuff;
debug1("close_portaudio with %d", sfifo_used(&pa->fifo));
pa->finished = 1;
/* Wait at least until the FIFO is empty. */
while((stuff = sfifo_used(&pa->fifo))>0)
{
int ms = stuff/ao->framesize*1000/ao->rate;
debug1("still stuff for about %i ms there", ms);
ms_sleep(ms/2);
}
if (pa->stream) {
/* stop the stream if it is active */
if (Pa_IsStreamActive( pa->stream ) == 1) {
err = Pa_StopStream( pa->stream );
if( err != paNoError )
{
if(!AOQUIET)
error1( "Failed to stop PortAudio stream: %s"
, Pa_GetErrorText(err) );
return -1;
}
}
/* and then close the stream */
err = Pa_CloseStream( pa->stream );
if( err != paNoError )
{
if(!AOQUIET)
error1( "Failed to close PortAudio stream: %s"
, Pa_GetErrorText(err) );
return -1;
}
pa->stream = NULL;
}
/* and free memory used by fifo */
sfifo_close( &pa->fifo );
return 0;
}
static void flush_portaudio(out123_handle *ao)
{
mpg123_portaudio_t *pa = (mpg123_portaudio_t*)ao->userptr;
/*PaError err;*/
/* throw away contents of FIFO */
sfifo_flush( &pa->fifo );
/* and empty out PortAudio buffers */
/*err = */
Pa_AbortStream( pa->stream );
}
static int deinit_portaudio(out123_handle* ao)
{
/* Free up memory */
if (ao->userptr) {
free( ao->userptr );
ao->userptr = NULL;
}
/* Shut down PortAudio */
Pa_Terminate();
/* Success */
return 0;
}
static int init_portaudio(out123_handle* ao)
{
int err = paNoError;
mpg123_portaudio_t *handle;
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_portaudio;
ao->flush = flush_portaudio;
ao->write = write_portaudio;
ao->get_formats = get_formats_portaudio;
ao->close = close_portaudio;
ao->deinit = deinit_portaudio;
/* Initialise PortAudio */
err = Pa_Initialize();
if( err != paNoError )
{
if(!AOQUIET)
error1( "Failed to initialise PortAudio: %s"
, Pa_GetErrorText(err) );
return -1;
}
/* Allocate memory for handle */
ao->userptr = handle = malloc( sizeof(mpg123_portaudio_t) );
if (ao->userptr==NULL)
{
if(!AOQUIET)
error( "Failed to allocated memory for driver structure" );
return -1;
}
handle->finished = 0;
handle->stream = NULL;
memset(&handle->fifo, 0, sizeof(sfifo_t));
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "portaudio",
/* description */ "Output audio using PortAudio",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_portaudio,
};

View File

@@ -0,0 +1,194 @@
/*
pulse: audio output using PulseAudio server
copyright 2006-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Nicholas J. Humfrey
*/
#include "out123_int.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include "debug.h"
static int open_pulse(out123_handle *ao)
{
int err;
pa_simple* pas = NULL;
pa_sample_spec ss;
/* Check if already open ? */
if (ao->userptr) {
if(!AOQUIET)
error("Pulse audio output is already open.");
return -1;
}
/* Open an audio I/O stream. */
/* When they are < 0, I shall set some default. */
if(ao->rate < 0 || ao->format < 0 || ao->channels < 0)
{
ao->rate = 44100;
ao->channels = 2;
ao->format = MPG123_ENC_SIGNED_16;
}
/* Fill out pulse audio's data structure */
ss.channels = ao->channels;
ss.rate = ao->rate;
switch(ao->format) {
case MPG123_ENC_SIGNED_16:
#ifdef WORDS_BIGENDIAN
ss.format=PA_SAMPLE_S16BE;
#else
ss.format=PA_SAMPLE_S16LE;
#endif
break;
case MPG123_ENC_SIGNED_24:
#ifdef WORDS_BIGENDIAN
ss.format=PA_SAMPLE_S24BE;
#else
ss.format=PA_SAMPLE_S24LE;
#endif
break;
case MPG123_ENC_SIGNED_32:
#ifdef WORDS_BIGENDIAN
ss.format=PA_SAMPLE_S32BE;
#else
ss.format=PA_SAMPLE_S32LE;
#endif
break;
case MPG123_ENC_FLOAT_32:
#ifdef WORDS_BIGENDIAN
ss.format=PA_SAMPLE_FLOAT32BE;
#else
ss.format=PA_SAMPLE_FLOAT32LE;
#endif
break;
case MPG123_ENC_ALAW_8:
ss.format=PA_SAMPLE_ALAW;
break;
case MPG123_ENC_ULAW_8:
ss.format=PA_SAMPLE_ULAW;
break;
case MPG123_ENC_UNSIGNED_8:
ss.format=PA_SAMPLE_U8;
break;
default:
if(!AOQUIET)
error1("Unsupported audio format: 0x%x", ao->format);
return -1;
break;
}
/* Perform the open */
pas = pa_simple_new(
NULL, /* Use the default server */
ao->name, /* Our application's name */
PA_STREAM_PLAYBACK,
ao->device, /* Use the default device if NULL */
"via out123", /* Description of our stream */
&ss, /* Our sample format */
NULL, /* Use default channel map */
NULL, /* Use default buffering attributes */
&err /* Error result code */
);
if(pas == NULL)
{
if(!AOQUIET)
error1("Failed to open pulse audio output: %s", pa_strerror(err));
return -1;
}
/* Store the pointer */
ao->userptr = (void*)pas;
return 0;
}
static int get_formats_pulse(out123_handle *ao)
{
/* Only implemented Signed 16-bit audio for now */
return MPG123_ENC_SIGNED_16;
}
static int write_pulse(out123_handle *ao, unsigned char *buf, int len)
{
pa_simple *pas = (pa_simple*)ao->userptr;
int ret, err;
/* Doesn't return number of bytes but just success or not. */
ret = pa_simple_write( pas, buf, len, &err );
if(ret<0)
{
if(!AOQUIET)
error1("Failed to write audio: %s", pa_strerror(err));
return -1;
}
return len; /* If successful, everything has been written. */
}
static int close_pulse(out123_handle *ao)
{
pa_simple *pas = (pa_simple*)ao->userptr;
if (pas) {
int err; /* Do we really want to handle errors here? End is the end. */
pa_simple_drain(pas, &err);
pa_simple_free(pas);
ao->userptr = NULL;
}
return 0;
}
static void flush_pulse(out123_handle *ao)
{
pa_simple *pas = (pa_simple*)ao->userptr;
if (pas) {
int err;
pa_simple_flush( pas, &err );
if(err && !AOQUIET)
error1("Failed to flush audio: %s", pa_strerror(err));
}
}
static int init_pulse(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_pulse;
ao->flush = flush_pulse;
ao->write = write_pulse;
ao->get_formats = get_formats_pulse;
ao->close = close_pulse;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "pulse",
/* description */ "Output audio using PulseAudio Server",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_pulse,
};

View File

@@ -0,0 +1,272 @@
/*
qsa: sound output with QNX Sound Architecture 0.5.2 API
copyright 2013 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
written by Mike Gorchak <mike.gorchak.qnx@gmail.com>
*/
#include "out123_int.h"
#include <errno.h>
#include <stdint.h>
#include <sys/asoundlib.h>
#include "debug.h"
typedef struct _qsa_mp_map
{
uint32_t qsa_format;
int mp_format;
} qsa_mp_map_t;
/* in order best format first */
qsa_mp_map_t format_map[]=
{
#ifdef WORDS_BIGENDIAN
{SND_PCM_SFMT_FLOAT64_BE, MPG123_ENC_FLOAT_64 },
{SND_PCM_SFMT_FLOAT_BE, MPG123_ENC_FLOAT_32 },
{SND_PCM_SFMT_S32_BE, MPG123_ENC_SIGNED_32 },
{SND_PCM_SFMT_U32_BE, MPG123_ENC_UNSIGNED_32 },
{SND_PCM_SFMT_S24_BE, MPG123_ENC_SIGNED_24 },
{SND_PCM_SFMT_U24_BE, MPG123_ENC_UNSIGNED_24 },
{SND_PCM_SFMT_S16_BE, MPG123_ENC_SIGNED_16 },
{SND_PCM_SFMT_U16_BE, MPG123_ENC_UNSIGNED_16 },
#else
{SND_PCM_SFMT_FLOAT64_LE, MPG123_ENC_FLOAT_64 },
{SND_PCM_SFMT_FLOAT_LE, MPG123_ENC_FLOAT_32 },
{SND_PCM_SFMT_S32_LE, MPG123_ENC_SIGNED_32 },
{SND_PCM_SFMT_U32_LE, MPG123_ENC_UNSIGNED_32 },
{SND_PCM_SFMT_S24_LE, MPG123_ENC_SIGNED_24 },
{SND_PCM_SFMT_U24_LE, MPG123_ENC_UNSIGNED_24 },
{SND_PCM_SFMT_S16_LE, MPG123_ENC_SIGNED_16 },
{SND_PCM_SFMT_U16_LE, MPG123_ENC_UNSIGNED_16 },
#endif
{SND_PCM_SFMT_U8, MPG123_ENC_UNSIGNED_8 },
{SND_PCM_SFMT_S8, MPG123_ENC_SIGNED_8 },
{SND_PCM_SFMT_A_LAW, MPG123_ENC_ALAW_8 },
{SND_PCM_SFMT_MU_LAW, MPG123_ENC_ULAW_8 },
{0, 0 },
};
typedef struct _qsa_internal
{
int cardno;
int deviceno;
snd_pcm_t* audio_handle;
snd_pcm_channel_params_t cpars;
} qsa_internal_t;
static int open_qsa(out123_handle* ao)
{
int status;
int cardno;
int deviceno;
int it;
snd_pcm_t* audio_handle;
qsa_internal_t* userptr;
ao->userptr=NULL;
status=snd_pcm_open_preferred(&audio_handle, &cardno, &deviceno, SND_PCM_OPEN_PLAYBACK);
if (status<0)
{
return FALSE;
}
status=snd_pcm_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP);
if (status<0)
{
return FALSE;
}
userptr=calloc(1, sizeof(qsa_internal_t));
if (userptr==NULL)
{
return FALSE;
}
ao->userptr=userptr;
userptr->audio_handle=audio_handle;
userptr->cardno=cardno;
userptr->deviceno=deviceno;
memset(&userptr->cpars, 0, sizeof(userptr->cpars));
userptr->cpars.channel=SND_PCM_CHANNEL_PLAYBACK;
userptr->cpars.mode=SND_PCM_MODE_BLOCK;
userptr->cpars.start_mode=SND_PCM_START_DATA;
userptr->cpars.stop_mode=SND_PCM_STOP_STOP;
userptr->cpars.format.format=0;
it=0;
do {
if ((format_map[it].qsa_format==0) && (format_map[it].mp_format==0))
{
break;
}
if (ao->format==format_map[it].mp_format)
{
userptr->cpars.format.format=format_map[it].qsa_format;
break;
}
it++;
} while(1);
userptr->cpars.format.interleave=1;
userptr->cpars.format.rate=ao->rate;
userptr->cpars.format.voices=ao->channels;
userptr->cpars.buf.block.frag_size=4096;
userptr->cpars.buf.block.frags_min=8;
userptr->cpars.buf.block.frags_max=16;
if ((ao->channels!=-1) && (ao->rate!=-1))
{
status=snd_pcm_plugin_params(userptr->audio_handle, &userptr->cpars);
if (status<0)
{
return FALSE;
}
status=snd_pcm_plugin_prepare(userptr->audio_handle, SND_PCM_CHANNEL_PLAYBACK);
if (status<0)
{
return FALSE;
}
}
return TRUE;
}
static int get_formats_qsa(out123_handle* ao)
{
qsa_internal_t* userptr;
int status;
int it=0;
userptr=ao->userptr;
if (userptr!=NULL);
{
userptr->cpars.format.rate=ao->rate;
userptr->cpars.format.voices=ao->channels;
it=0;
do {
if ((format_map[it].qsa_format==0) && (format_map[it].mp_format==0))
{
break;
}
userptr->cpars.format.format=format_map[it].qsa_format;
status=snd_pcm_plugin_params(userptr->audio_handle, &userptr->cpars);
if (status<0)
{
it++;
}
else
{
return format_map[it].mp_format;
}
} while(1);
}
return 0;
}
static int write_qsa(out123_handle* ao, unsigned char* buf, int bytes)
{
int written;
int status;
snd_pcm_channel_status_t cstatus;
qsa_internal_t* userptr;
userptr=ao->userptr;
if (userptr!=NULL);
{
written=snd_pcm_plugin_write(userptr->audio_handle, buf, bytes);
if (written!=bytes)
{
/* Check if samples playback got stuck somewhere in hardware or in */
/* the audio device driver */
if ((errno==EAGAIN) && (written==0))
{
return 0;
}
if ((errno==EINVAL) || (errno==EIO))
{
memset(&cstatus, 0, sizeof(cstatus));
cstatus.channel=SND_PCM_CHANNEL_PLAYBACK;
status=snd_pcm_plugin_status(userptr->audio_handle, &cstatus);
if (status>0)
{
return 0;
}
if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
(cstatus.status == SND_PCM_STATUS_READY))
{
status=snd_pcm_plugin_prepare(userptr->audio_handle, SND_PCM_CHANNEL_PLAYBACK);
if (status<0)
{
return 0;
}
}
}
}
}
return written;
}
static void flush_qsa(out123_handle* ao)
{
qsa_internal_t* userptr;
userptr=ao->userptr;
if (userptr!=NULL);
{
snd_pcm_playback_flush(userptr->audio_handle);
}
}
static int close_qsa(out123_handle* ao)
{
qsa_internal_t* userptr;
userptr=ao->userptr;
if (userptr!=NULL);
{
snd_pcm_close(userptr->audio_handle);
free(ao->userptr);
}
return TRUE;
}
static int deinit_qsa(out123_handle* ao)
{
return TRUE;
}
static int init_qsa(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_qsa;
ao->flush = flush_qsa;
ao->write = write_qsa;
ao->get_formats = get_formats_qsa;
ao->close = close_qsa;
ao->deinit = deinit_qsa;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "qsa",
/* description */ "Output audio using QNX Sound Architecture (QSA).",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_qsa,
};

View File

@@ -0,0 +1,302 @@
/*
sdl: audio output via SDL cross-platform API
copyright 2006-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Nicholas J. Humfrey
*/
#include "out123_int.h"
#include <math.h>
#include <SDL.h>
#ifdef WIN32
#include <windows.h>
#endif
/* Including the sfifo code locally, to avoid module linkage issues. */
#define SFIFO_STATIC
#include "sfifo.c"
#include "debug.h"
#define SAMPLE_SIZE (2)
#define FRAMES_PER_BUFFER (256)
/* Performance of SDL with ALSA is a bit of a mystery to me. Regardless
of buffer size here, I just cannot avoid buffer underruns on my system.
SDL always chooses 1024x2 periods, which seems to be just not quite
enough on the Thinkpad:-/ Choosing 0.2 s as a plentiful default instead
of 0.5 s which is just a lie. */
#define FIFO_DURATION (ao->device_buffer > 0. ? ao->device_buffer : 0.2)
#define BUFFER_SAMPLES ((FIFO_DURATION*ao->rate)/2)
struct handle
{
int finished; /* A flag for communicating end, one-way. */
sfifo_t fifo;
};
/* Some busy waiting. Proper stuff like semaphores might add
dependencies (POSIX) that the platform does not know. */
static void ms_sleep(int milliseconds)
{
#ifdef WIN32
Sleep(milliseconds);
#else
usleep(milliseconds*1000);
#endif
}
/* The audio function callback takes the following parameters:
stream: A pointer to the audio buffer to be filled
len: The length (in bytes) of the audio buffer
*/
static void audio_callback_sdl(void *udata, Uint8 *stream, int len)
{
out123_handle *ao = (out123_handle*)udata;
struct handle *sh = (struct handle*)ao->userptr;
sfifo_t *fifo = &sh->fifo;
int bytes_read;
int bytes_avail;
/* Until the finished flag is set, we will wait for more.
As the exact value does not matter and late detection of
a change is kindof OK, I do not see a thread safety problem here. */
while((bytes_avail = sfifo_used(fifo)) < len && !sh->finished)
{
int ms = (len-bytes_avail)/ao->framesize*1000/ao->rate;
debug1("waiting for more input, %d ms missing", ms);
ms_sleep(ms/10);
}
/* Read audio from FIFO to SDL's buffer */
if(bytes_avail > len)
bytes_avail = len;
bytes_read = sfifo_read( fifo, stream, bytes_avail );
if(bytes_read != bytes_avail)
warning2("Error reading from the FIFO (wanted=%d, bytes_read=%d).\n"
, bytes_avail, bytes_read);
if(bytes_read < 0)
bytes_read = 0;
/* Ensure that any remaining space is filled with zero bytes. */
if(bytes_read < len)
memset(stream+bytes_read, 0, len-bytes_read);
}
static int open_sdl(out123_handle *ao)
{
struct handle *sh = (struct handle*)ao->userptr;
sfifo_t *fifo = &sh->fifo;
/* Open an audio I/O stream. */
if (ao->rate > 0 && ao->channels >0 ) {
size_t ringbuffer_len;
SDL_AudioSpec wanted;
/* L16 uncompressed audio data, using 16-bit signed representation in twos
complement notation - system endian-ness. */
wanted.format = AUDIO_S16SYS;
/* Seems reasonable to demand a buffer size related to the device
buffer. */
wanted.samples = BUFFER_SAMPLES;
wanted.callback = audio_callback_sdl;
wanted.userdata = ao;
wanted.channels = ao->channels;
wanted.freq = ao->rate;
sh->finished = 0;
/* Open the audio device, forcing the desired format
Actually, it is still subject to constraints by hardware.
Need to have sample rate checked beforehand! SDL will
happily play 22 kHz files with 44 kHz hardware rate!
Same with channel count. No conversion. The manual is a bit
misleading on that (only talking about sample format, I guess). */
if ( SDL_OpenAudio(&wanted, NULL) )
{
if(!AOQUIET)
error1("Couldn't open SDL audio: %s\n", SDL_GetError());
return -1;
}
/* Initialise FIFO */
ringbuffer_len = ao->rate * FIFO_DURATION * SAMPLE_SIZE *ao->channels;
debug2( "Allocating %d byte ring-buffer (%f seconds)", (int)ringbuffer_len, (float)FIFO_DURATION);
if (sfifo_init( fifo, ringbuffer_len ) && !AOQUIET)
error1( "Failed to initialise FIFO of size %d bytes", (int)ringbuffer_len );
}
return(0);
}
static int get_formats_sdl(out123_handle *ao)
{
/* Got no better idea than to just take 16 bit and run with it */
return MPG123_ENC_SIGNED_16;
#if 0
/*
This code would "properly" test audio format support.
But thing is, SDL will always say yes and amen to everything, but it takes
an awful amount of time to get all the variants tested (about 2 seconds,
for example). I have seen SDL builds that do proper format conversion
behind your back, I have seen builds that do not. Every build seems to
claim that it does, though. Just hope you're lucky and your SDL works.
Otherwise, use a proper audio output API.
*/
SDL_AudioSpec wanted, got;
/* Only implemented Signed 16-bit audio for now.
The SDL manual doesn't suggest more interesting formats
like S24 or S32 anyway. */
wanted.format = AUDIO_S16SYS;
wanted.samples = BUFFER_SAMPLES;
wanted.callback = audio_callback_sdl;
wanted.userdata = ao;
wanted.channels = ao->channels;
wanted.freq = ao->rate;
if(SDL_OpenAudio(&wanted, &got)) return 0;
SDL_CloseAudio();
fprintf(stderr, "wanted rate: %li got rate %li\n", (long)wanted.freq, (long)got.freq);
return (got.freq == ao->rate && got.channels == ao->channels)
? MPG123_ENC_SIGNED_16
: 0;
#endif
}
static int write_sdl(out123_handle *ao, unsigned char *buf, int len)
{
struct handle *sh = (struct handle*)ao->userptr;
sfifo_t *fifo = &sh->fifo;
int len_remain = len;
/* Some busy waiting, but feed what is possible. */
while(len_remain) /* Note: input len is multiple of framesize! */
{
int block = sfifo_space(fifo);
block -= block % ao->framesize;
if(block > len_remain)
block = len_remain;
if(block)
{
sfifo_write(fifo, buf, block);
len_remain -= block;
buf += block;
/* Unpause once the buffer is 50% full */
if (sfifo_used(fifo) > (sfifo_size(fifo)/2) )
SDL_PauseAudio(0);
}
if(len_remain)
{
debug1("Still need to write %d bytes, sleeping a bit.", len_remain);
ms_sleep(0.1*FIFO_DURATION*1000);
}
}
return len;
}
static int close_sdl(out123_handle *ao)
{
int stuff;
struct handle *sh = (struct handle*)ao->userptr;
sfifo_t *fifo = &sh->fifo;
debug1("close_sdl with %d", sfifo_used(fifo));
sh->finished = 1;
/* Wait at least until SDL emptied the FIFO. */
while((stuff = sfifo_used(fifo))>0)
{
int ms = stuff/ao->framesize*1000/ao->rate;
debug1("still stuff for about %i ms there", ms);
ms_sleep(ms/2);
}
SDL_CloseAudio();
/* Free up the memory used by the FIFO */
sfifo_close( fifo );
return 0;
}
static void flush_sdl(out123_handle *ao)
{
struct handle *sh = (struct handle*)ao->userptr;
SDL_PauseAudio(1);
sfifo_flush(&sh->fifo);
}
/* You can only rely on that being called after successful init_sdl()!
And sdl_close() should be called before to free the sfifo. */
static int deinit_sdl(out123_handle* ao)
{
/* Free up memory */
if (ao->userptr) {
free( ao->userptr );
ao->userptr = NULL;
}
/* Shut down SDL */
SDL_Quit();
/* Success */
return 0;
}
/* Remember: If this returns failure, no additional cleanup happens.
Resources must be freed here. */
static int init_sdl(out123_handle* ao)
{
struct handle *sh;
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_sdl;
ao->flush = flush_sdl;
ao->write = write_sdl;
ao->get_formats = get_formats_sdl;
ao->close = close_sdl;
ao->deinit = deinit_sdl;
/* Initialise SDL */
if (SDL_Init( SDL_INIT_AUDIO ) )
{
if(!AOQUIET)
error1("Failed to initialise SDL: %s\n", SDL_GetError());
return -1;
}
/* Allocate memory _after_ checking that SDL is available, so we do not
have to free after failure. */
ao->userptr = sh = malloc( sizeof(struct handle) );
if (ao->userptr==NULL)
{
if(!AOQUIET)
error( "Failed to allocated memory for FIFO structure" );
return -1;
}
sh->finished = 0;
/* Not exactly necessary; only for somewhat safe sdl_close after a fake
sdl_open(). */
memset( &sh->fifo, 0, sizeof(sfifo_t) );
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "sdl",
/* description */ "Output audio using SDL (Simple DirectMedia Layer).",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_sdl,
};

View File

@@ -0,0 +1,269 @@
/*
sgi: audio output on SGI boxen
copyright ?-2013 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written (as it seems) by Thomas Woerner
*/
#include "out123_int.h"
#include <fcntl.h>
#include <dmedia/audio.h>
#include "errno.h"
#include "debug.h"
static int set_rate(out123_handle *ao, ALconfig config)
{
int dev = alGetDevice(config);
ALpv params[1];
/* Make sure the device is OK */
if(dev < 0)
{
error1("set_rate: %s", alGetErrorString(oserror()));
return -1;
}
if(ao->rate > 0)
{
params[0].param = AL_RATE;
params[0].value.ll = alDoubleToFixed((double)ao->rate);
if(alSetParams(dev, params, 1) < 0)
{
error1("set_rate: %s", alGetErrorString(oserror()));
return -1;
}
}
return 0;
}
static int set_channels(out123_handle *ao, ALconfig config)
{
int ret;
if(ao->channels == 2)
ret = alSetChannels(config, AL_STEREO);
else
ret = alSetChannels(config, AL_MONO);
if(ret < 0)
{
error1("set_channels : %s", alGetErrorString(oserror()));
return -1;
}
return 0;
}
static int set_format(out123_handle *ao, ALconfig config)
{
if(ao->format == MPG123_ENC_FLOAT_32)
{
if(alSetSampFmt(config, AL_SAMPFMT_FLOAT) < 0)
{
error1("SetSampFmt: %s", alGetErrorString(oserror()));
return -1;
}
} else
{
if(alSetSampFmt(config, AL_SAMPFMT_TWOSCOMP) < 0)
{
error1("SetSampFmt: %s", alGetErrorString(oserror()));
return -1;
}
if(alSetWidth(config, AL_SAMPLE_16) < 0)
{
error1("SetWidth: %s", alGetErrorString(oserror()));
return -1;
}
}
return 0;
}
static int open_sgi(out123_handle *ao)
{
int current_dev;
ALport port = NULL;
ALconfig config = alNewConfig();
ao->userptr = NULL;
/* Test for correct completion */
if(config == 0)
{
error1("open_sgi: %s", alGetErrorString(oserror()));
return -1;
}
/* Setup output device to specified device name. If there is no device name
specified in ao structure, use the default for output */
if((ao->device) != NULL)
{
current_dev = alGetResourceByName(AL_SYSTEM, ao->device, AL_OUTPUT_DEVICE_TYPE);
debug2("Dev: %s %i", ao->device, current_dev);
if(!current_dev)
{
int i, numOut;
char devname[32];
ALpv pv[1];
ALvalue *alvalues;
error2("Invalid audio resource: %s (%s)", ao->device, alGetErrorString(oserror()));
if((numOut= alQueryValues(AL_SYSTEM,AL_DEFAULT_OUTPUT,0,0,0,0))>=0)
fprintf(stderr, "There are %d output devices on this system.\n", numOut);
else
{
fprintf(stderr, "Can't find output devices. alQueryValues failed: %s\n", alGetErrorString(oserror()));
goto open_sgi_bad;
}
alvalues = malloc(sizeof(ALvalue) * numOut);
i = alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, alvalues, numOut, pv, 0);
if(i == -1)
error1("alQueryValues: %s", alGetErrorString(oserror()));
else
{
for(i=0; i < numOut; i++)
{
pv[0].param = AL_NAME;
pv[0].value.ptr = devname;
pv[0].sizeIn = 32;
alGetParams(alvalues[i].i, pv, 1);
fprintf(stderr, "%i: %s\n", i, devname);
}
}
free(alvalues);
goto open_sgi_bad;
}
if(alSetDevice(config, current_dev) < 0)
{
error1("open: alSetDevice : %s",alGetErrorString(oserror()));
goto open_sgi_bad;
}
} else
current_dev = AL_DEFAULT_OUTPUT;
/* Set the device */
if(alSetDevice(config, current_dev) < 0)
{
error1("open_sgi: %s", alGetErrorString(oserror()));
goto open_sgi_bad;
}
/* Set port parameters */
if(alSetQueueSize(config, 131069) < 0)
{
error1("open_sgi: setting audio buffer failed: %s", alGetErrorString(oserror()));
goto open_sgi_bad;
}
if( set_format(ao, config) < 0
|| set_rate(ao, config) < 0
|| set_channels(ao, config) < 0 )
goto open_sgi_bad;
/* Open the audio port */
port = alOpenPort("mpg123-VSC", "w", config);
if(port == NULL)
{
error1("Unable to open audio channel: %s", alGetErrorString(oserror()));
goto open_sgi_bad;
}
ao->userptr = (void*)port;
alFreeConfig(config);
return 1;
open_sgi_bad:
/* clean up and return error */
alFreeConfig(config);
return -1;
}
static int get_formats_sgi(out123_handle *ao)
{
return MPG123_ENC_SIGNED_16|MPG123_ENC_FLOAT_32;
}
static int write_sgi(out123_handle *ao, unsigned char *buf, int len)
{
int length = len;
if(!ao || !ao->userptr) return -1;
ALport port = (ALport)ao->userptr;
if(ao->channels == 2) length >>= 2;
else length >>= 1;
if(ao->format == MPG123_ENC_FLOAT_32) length >>=1;
/* Not much error checking ... */
alWriteFrames(port, buf, length);
return len;
}
static int close_sgi(out123_handle *ao)
{
if(!ao || !ao->userptr) return -1;
ALport port = (ALport)ao->userptr;
if(port)
{
/* play all remaining samples */
while(alGetFilled(port) > 0) sginap(1);
alClosePort(port);
ao->userptr=NULL;
}
return 0;
}
static void flush_sgi(out123_handle *ao)
{
ALport port = (ALport)ao->userptr;
if(port) alDiscardFrames(port, alGetFilled(port));
}
static int init_sgi(out123_handle* ao)
{
if(ao == NULL) return -1;
/* Set callbacks */
ao->open = open_sgi;
ao->flush = flush_sgi;
ao->write = write_sgi;
ao->get_formats = get_formats_sgi;
ao->close = close_sgi;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info =
{
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "sgi",
/* description */ "Audio output for SGI.",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_sgi,
};

View File

@@ -0,0 +1,161 @@
/*
* sndio: sndio audio output
*
* Copyright (c) 2008 Christian Weisgerber <naddy@openbsd.org>,
* Alexandre Ratchov <alex@caoua.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "out123_int.h"
#include <sndio.h>
#include "debug.h"
static int open_sndio(out123_handle *ao)
{
struct sio_hdl *hdl;
struct sio_par par;
hdl = sio_open(ao->device /* NULL is fine */, SIO_PLAY, 0);
if (hdl == NULL)
return -1;
sio_initpar(&par);
par.rate = ao->rate;
par.pchan = ao->channels;
par.le = SIO_LE_NATIVE;
switch(ao->format) {
case MPG123_ENC_SIGNED_32:
par.sig = 1;
par.bits = 32;
break;
case MPG123_ENC_UNSIGNED_32:
par.sig = 0;
par.bits = 32;
break;
case MPG123_ENC_SIGNED_16:
case -1: /* query mode */
par.sig = 1;
par.bits = 16;
break;
case MPG123_ENC_UNSIGNED_16:
par.sig = 0;
par.bits = 16;
break;
case MPG123_ENC_UNSIGNED_8:
par.sig = 0;
par.bits = 8;
break;
case MPG123_ENC_SIGNED_8:
par.sig = 1;
par.bits = 8;
break;
default:
if (!AOQUIET)
error1("open_sndio: invalid sample format %d",
ao->format);
sio_close(hdl);
return -1;
}
if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par) ||
!sio_start(hdl)) {
sio_close(hdl);
return -1;
}
if ((par.bits != 8 && par.bits != 16 && par.bits != 32) ||
par.le != SIO_LE_NATIVE) {
sio_close(hdl);
return -1;
}
ao->rate = par.rate;
ao->channels = par.pchan;
switch (par.bits) {
case 8:
ao->format = par.sig ? MPG123_ENC_SIGNED_8 :
MPG123_ENC_UNSIGNED_8;
break;
case 16:
ao->format = par.sig ? MPG123_ENC_SIGNED_16 :
MPG123_ENC_UNSIGNED_16;
break;
case 32:
ao->format = par.sig ? MPG123_ENC_SIGNED_32 :
MPG123_ENC_UNSIGNED_32;
break;
}
ao->userptr = hdl;
return 0;
}
static int get_formats_sndio(out123_handle *ao)
{
return (MPG123_ENC_SIGNED_32|MPG123_ENC_UNSIGNED_32|
MPG123_ENC_SIGNED_16|MPG123_ENC_UNSIGNED_16|
MPG123_ENC_UNSIGNED_8|MPG123_ENC_SIGNED_8);
}
static int write_sndio(out123_handle *ao, unsigned char *buf, int len)
{
struct sio_hdl *hdl = (struct sio_hdl *)ao->userptr;
int count;
count = (int)sio_write(hdl, buf, len);
if (count == 0 && sio_eof(hdl))
return -1;
return count;
}
static void flush_sndio(out123_handle *ao)
{
return;
}
static int close_sndio(out123_handle *ao)
{
struct sio_hdl *hdl = (struct sio_hdl *)ao->userptr;
sio_close(hdl);
return 0;
}
static int init_sndio(out123_handle* ao)
{
if (ao == NULL)
return -1;
/* Set callbacks */
ao->open = open_sndio;
ao->flush = flush_sndio; /* required */
ao->write = write_sndio;
ao->get_formats = get_formats_sndio;
ao->close = close_sndio;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "sndio",
/* description */ "Output audio using sndio library",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_sndio,
};

View File

@@ -0,0 +1,283 @@
/*
sun: audio output for Sun systems
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp
*/
#include "out123_int.h"
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SUN_AUDIOIO_H
#include <sun/audioio.h>
#endif
#ifdef HAVE_SYS_AUDIOIO_H
#include <sys/audioio.h>
#endif
#ifdef HAVE_SYS_AUDIO_H
#include <sys/audio.h>
#endif
#ifdef HAVE_ASM_AUDIOIO_H
#include <asm/audioio.h>
#endif
#include <fcntl.h>
#include "debug.h"
static void set_format_helper(out123_handle *ao, audio_info_t *ainfo)
{
switch(ao->format) {
case -1:
case MPG123_ENC_SIGNED_16:
default:
#ifndef AUDIO_ENCODING_LINEAR /* not supported */
#define AUDIO_ENCODING_LINEAR 3
#endif
ainfo->play.encoding = AUDIO_ENCODING_LINEAR;
ainfo->play.precision = 16;
break;
case MPG123_ENC_UNSIGNED_8:
#if defined(SOLARIS) || defined(SPARCLINUX)
ainfo->play.encoding = AUDIO_ENCODING_LINEAR8;
ainfo->play.precision = 8;
break;
#endif
case MPG123_ENC_SIGNED_8:
if(!AOQUIET)
error("Linear signed 8 bit not supported!");
return;
case MPG123_ENC_ULAW_8:
ainfo->play.encoding = AUDIO_ENCODING_ULAW;
ainfo->play.precision = 8;
break;
case MPG123_ENC_ALAW_8:
ainfo->play.encoding = AUDIO_ENCODING_ALAW;
ainfo->play.precision = 8;
break;
}
}
static int reset_parameters_sun(out123_handle *ao)
{
audio_info_t ainfo;
AUDIO_INITINFO(&ainfo);
if(ao->rate != -1) ainfo.play.sample_rate = ao->rate;
if(ao->channels >= 0) ainfo.play.channels = ao->channels;
set_format_helper(ao,&ainfo);
if(ioctl(ao->fn, AUDIO_SETINFO, &ainfo) == -1) return -1;
return 0;
}
static int rate_best_match(out123_handle *ao)
{
audio_info_t ainfo;
AUDIO_INITINFO(&ainfo);
ainfo.play.sample_rate = ao->rate;
if(ioctl(ao->fn, AUDIO_SETINFO, &ainfo) < 0) {
ao->rate = 0;
return 0;
}
if(ioctl(ao->fn, AUDIO_GETINFO, &ainfo) < 0) {
return -1;
}
ao->rate = ainfo.play.sample_rate;
return 0;
}
static int set_rate(out123_handle *ao)
{
audio_info_t ainfo;
if(ao->rate != -1) {
AUDIO_INITINFO(&ainfo);
ainfo.play.sample_rate = ao->rate;
if(ioctl(ao->fn, AUDIO_SETINFO, &ainfo) == -1) return -1;
return 0;
}
return -1;
}
static int set_channels(out123_handle *ao)
{
audio_info_t ainfo;
AUDIO_INITINFO(&ainfo);
ainfo.play.channels = ao->channels;
if(ioctl(ao->fn, AUDIO_SETINFO, &ainfo) == -1)
return -1;
return 0;
}
static int set_format(out123_handle *ao)
{
audio_info_t ainfo;
AUDIO_INITINFO(&ainfo);
set_format_helper(ao,&ainfo);
if(ioctl(ao->fn, AUDIO_SETINFO, &ainfo) == -1)
return -1;
return 0;
}
static int open_sun(out123_handle *ao)
{
audio_info_t ainfo;
const char *dev = ao->device;
if(!dev) {
if(getenv("AUDIODEV")) {
dev = getenv("AUDIODEV");
} else {
dev = "/dev/audio";
}
}
ao->fn = open(dev,O_WRONLY);
if(ao->fn < 0) return ao->fn;
#if defined(SUNOS) && defined(AUDIO_GETDEV)
{
int type;
if(ioctl(ao->fn, AUDIO_GETDEV, &type) == -1) return -1;
if(type == AUDIO_DEV_UNKNOWN || type == AUDIO_DEV_AMD)
return -1;
}
#else
#if defined(SOLARIS) || defined(SPARCLINUX)
{
struct audio_device ad;
if(ioctl(ao->fn, AUDIO_GETDEV, &ad) == -1)
return -1;
if(!strstr(ad.name,"dbri") && !strstr(ad.name,"CS4231"))
warning1("Unknown sound system %s. But we try it.",ad.name);
}
#endif
#endif
if(reset_parameters_sun(ao) < 0) return -1;
AUDIO_INITINFO(&ainfo);
if(ao->flags > 0)
ainfo.play.port = 0;
if(ao->flags & OUT123_INTERNAL_SPEAKER)
ainfo.play.port |= AUDIO_SPEAKER;
if(ao->flags & OUT123_HEADPHONES)
ainfo.play.port |= AUDIO_HEADPHONE;
#ifdef AUDIO_LINE_OUT
if(ao->flags & OUT123_LINE_OUT)
ainfo.play.port |= AUDIO_LINE_OUT;
#endif
if(ao->gain != -1)
ainfo.play.gain = ao->gain;
if(ioctl(ao->fn, AUDIO_SETINFO, &ainfo) == -1)
return -1;
return ao->fn;
}
static int get_formats_sun(out123_handle *ao)
{
static int tab[][3] = {
{ AUDIO_ENCODING_ULAW , 8, MPG123_ENC_ULAW_8 } ,
{ AUDIO_ENCODING_ALAW , 8, MPG123_ENC_ALAW_8 } ,
{ AUDIO_ENCODING_LINEAR , 16, MPG123_ENC_SIGNED_16 } ,
#if 0
#if defined(SOLARIS) || defined(SPARCLINUX)
{ AUDIO_ENCODING_LINEAR8 , 8, MPG123_ENC_UNSIGNED_8 } ,
#endif
#endif
};
audio_info_t ainfo;
int i,fmts=0;
for(i=0;i<sizeof(tab)/sizeof(tab[0]);i++) {
AUDIO_INITINFO(&ainfo);
ainfo.play.encoding = tab[i][0];
ainfo.play.precision = tab[i][1];
#if 1
ainfo.play.sample_rate = ao->rate;
ainfo.play.channels = ao->channels;
#endif
if(ioctl(ao->fn, AUDIO_SETINFO, &ainfo) >= 0) {
fmts |= tab[i][2];
}
}
return fmts;
}
static int write_sun(out123_handle *ao,unsigned char *buf,int len)
{
return write(ao->fn,buf,len);
}
static int close_sun(out123_handle *ao)
{
close (ao->fn);
return 0;
}
static void flush_sun(out123_handle *ao)
{
/*ioctl (ao->fn, I_FLUSH, FLUSHRW);*/
}
static int init_sun(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_sun;
ao->flush = flush_sun;
ao->write = write_sun;
ao->get_formats = get_formats_sun;
ao->close = close_sun;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "sun",
/* description */ "Audio output for Sun Audio.",
/* revision */ "$Rev: 3915 $",
/* handle */ NULL,
/* init_output */ init_sun,
};

View File

@@ -0,0 +1,213 @@
/*
tinyalsa: sound output with TINY Advanced Linux Sound Architecture
copyright 2006-8 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Jarno Lehtinen <lehtinen@sci.fi>
*/
#include "out123_int.h"
#include <errno.h>
#include <tinyalsa/asoundlib.h>
#include "debug.h"
typedef struct
{
struct pcm *pcm;
struct pcm_params *params;
struct pcm_config config;
unsigned int device;
unsigned int card;
} mpg123_tinyalsa_t;
static int initialize_device(out123_handle *ao)
{
mpg123_tinyalsa_t* ta = (mpg123_tinyalsa_t*)ao->userptr;
ta->config.channels = ao->channels;
ta->config.rate = ao->rate;
ta->config.period_size = 1024;
ta->config.period_count = 4;
ta->config.format = PCM_FORMAT_S16_LE;
ta->config.start_threshold = 0;
ta->config.stop_threshold = 0;
ta->config.silence_threshold = 0;
ta->pcm = pcm_open(ta->card, ta->device, PCM_OUT, &ta->config);
if (!ta->pcm || !pcm_is_ready(ta->pcm))
{
if(!AOQUIET)
error3( "(open) Unable to open card %u PCM device %u (%s)\n"
, ta->card, ta->device, pcm_get_error(ta->pcm) );
return -1;
}
return 0;
}
static int open_tinyalsa(out123_handle *ao)
{
debug("open_tinyalsa()");
mpg123_tinyalsa_t* ta = (mpg123_tinyalsa_t*)ao->userptr;
if (ao->format != -1)
{
/* we're going to play: initalize sample format */
return initialize_device(ao);
}
else
{
/* query mode; sample format will be set for each query */
return 0;
}
}
static int get_formats_tinyalsa(out123_handle *ao)
{
debug("get_formats_tinyalsa()");
mpg123_tinyalsa_t* ta = (mpg123_tinyalsa_t*)ao->userptr;
if ( ao->rate >= pcm_params_get_min(ta->params, PCM_PARAM_RATE) && ao->rate <= pcm_params_get_max(ta->params, PCM_PARAM_RATE) && ao->channels >= pcm_params_get_min(ta->params, PCM_PARAM_CHANNELS) && ao->channels <= pcm_params_get_max(ta->params, PCM_PARAM_CHANNELS) )
{
return MPG123_ENC_SIGNED_16;
}
else
{
return 0;
}
}
static int write_tinyalsa(out123_handle *ao, unsigned char *buf, int bytes)
{
mpg123_tinyalsa_t* ta = (mpg123_tinyalsa_t*)ao->userptr;
if (ta->pcm)
{
if(pcm_write(ta->pcm, buf, bytes))
{
if(!AOQUIET)
error("Error playing sample\n");
return -1;
}
}
return bytes;
}
static void flush_tinyalsa(out123_handle *ao)
{
debug("flush_tinyalsa()");
}
static int close_tinyalsa(out123_handle *ao)
{
debug("close_tinyalsa()");
mpg123_tinyalsa_t* ta = (mpg123_tinyalsa_t*)ao->userptr;
if (ta->pcm)
{
pcm_close(ta->pcm);
}
return 0;
}
static int deinit_tinyalsa(out123_handle* ao)
{
mpg123_tinyalsa_t* ta = (mpg123_tinyalsa_t*)ao->userptr;
/* Free up card/device parameters */
pcm_params_free(ta->params);
/* Free up memory */
if(ao->userptr)
{
free( ao->userptr );
ao->userptr = NULL;
}
/* Success */
return 0;
}
static int init_tinyalsa(out123_handle* ao)
{
if (ao==NULL) return -1;
/* Set callbacks */
ao->open = open_tinyalsa;
ao->flush = flush_tinyalsa;
ao->write = write_tinyalsa;
ao->get_formats = get_formats_tinyalsa;
ao->close = close_tinyalsa;
ao->deinit = deinit_tinyalsa;
/* Allocate memory for data structure */
ao->userptr = malloc( sizeof( mpg123_tinyalsa_t ) );
if(ao->userptr==NULL)
{
if(!AOQUIET)
error("failed to malloc memory for 'mpg123_tinyalsa_t'");
return -1;
}
memset( ao->userptr, 0, sizeof(mpg123_tinyalsa_t) );
/* Set card and device */
mpg123_tinyalsa_t* ta = (mpg123_tinyalsa_t*)ao->userptr;
ta->card = 0;
ta->device = 0;
if (ao->device)
{
char *ptr = ao->device;
ta->card = (unsigned int)strtol(ptr, &ptr, 10);
if (strlen(ptr) > 0)
{
ta->device = (unsigned int)strtol(++ptr, &ptr, 10);
}
}
/* Get card/device parameters */
ta->params = pcm_params_get(ta->card, ta->device, PCM_OUT);
if (ta->params == NULL)
{
if(!AOQUIET)
error2( "(params) Unable to open card %u PCM device %u.\n"
, ta->card, ta->device );
return -1;
}
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "tinyalsa",
/* description */ "Output audio using TINY Advanced Linux Sound Architecture (TINYALSA).",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_tinyalsa,
};

View File

@@ -0,0 +1,305 @@
/*
win32: audio output for Windows 32bit
copyright ?-2013 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written (as it seems) by Tony Million
rewrite of basic functionality for callback-less and properly ringbuffered operation by ravenexp
Closing buffer playback fixed by David Wohlferd <limegreensocks (*) yahoo dod com>
*/
#include "out123_int.h"
#include <windows.h>
#include "debug.h"
/*
Buffer size and number of buffers in the playback ring
NOTE: This particular num/size combination performs best under heavy
loads for my system, however this may not be true for any hardware/OS out there.
Generally, BUFFER_SIZE < 8k || NUM_BUFFERS > 16 || NUM_BUFFERS < 4 are not recommended.
*/
#define BUFFER_SIZE 0x10000
#define NUM_BUFFERS 8 /* total 512k roughly 2.5 sec of CD quality sound */
static void wait_for_buffer(WAVEHDR* hdr, HANDLE hEvent);
static void drain_win32(out123_handle *ao);
/* Buffer ring queue state */
struct queue_state
{
WAVEHDR buffer_headers[NUM_BUFFERS];
/* The next buffer to be filled and put in playback */
int next_buffer;
/* Buffer playback completion event */
HANDLE play_done_event;
HWAVEOUT waveout;
};
static int open_win32(out123_handle *ao)
{
struct queue_state* state;
int i;
MMRESULT res;
WAVEFORMATEX out_fmt;
UINT dev_id;
if(!ao) return -1;
if(ao->rate == -1) return 0;
/* Allocate queue state struct for this device */
state = calloc(1, sizeof(struct queue_state));
if(!state) return -1;
ao->userptr = state;
state->play_done_event = CreateEvent(0,FALSE,FALSE,0);
if(state->play_done_event == INVALID_HANDLE_VALUE) return -1;
/* FIXME: real device enumeration by capabilities? */
dev_id = WAVE_MAPPER; /* probably does the same thing */
/* FIXME: support for smth besides MPG123_ENC_SIGNED_16? */
out_fmt.wFormatTag = WAVE_FORMAT_PCM;
out_fmt.wBitsPerSample = 16;
out_fmt.nChannels = ao->channels;
out_fmt.nSamplesPerSec = ao->rate;
out_fmt.nBlockAlign = out_fmt.nChannels*out_fmt.wBitsPerSample/8;
out_fmt.nAvgBytesPerSec = out_fmt.nBlockAlign*out_fmt.nSamplesPerSec;
out_fmt.cbSize = 0;
res = waveOutOpen(&state->waveout, dev_id, &out_fmt,
(DWORD_PTR)state->play_done_event, 0, CALLBACK_EVENT);
switch(res)
{
case MMSYSERR_NOERROR:
break;
case MMSYSERR_ALLOCATED:
ereturn(-1, "Audio output device is already allocated.");
case MMSYSERR_NODRIVER:
ereturn(-1, "No device driver is present.");
case MMSYSERR_NOMEM:
ereturn(-1, "Unable to allocate or lock memory.");
case WAVERR_BADFORMAT:
ereturn(-1, "Unsupported waveform-audio format.");
default:
ereturn(-1, "Unable to open wave output device.");
}
/* Reset event from the "device open" message */
ResetEvent(state->play_done_event);
/* Allocate playback buffers */
for(i = 0; i < NUM_BUFFERS; i++)
if(!(state->buffer_headers[i].lpData = (LPSTR)malloc(BUFFER_SIZE)))
{
ereturn(-1, "Out of memory for playback buffers.");
}
else
{
/* Tell waveOutPrepareHeader the maximum value of dwBufferLength
we will ever send */
state->buffer_headers[i].dwBufferLength = BUFFER_SIZE;
state->buffer_headers[i].dwFlags = 0;
res = waveOutPrepareHeader(state->waveout, &state->buffer_headers[i], sizeof(WAVEHDR));
if(res != MMSYSERR_NOERROR) ereturn(-1, "Can't write to audio output device (prepare).");
/* set the current size of the buffer to 0 */
state->buffer_headers[i].dwBufferLength = 0;
/* set flags to unprepared - must reset this to WHDR_PREPARED before calling write */
state->buffer_headers[i].dwFlags = 0;
}
return 0;
}
static void wait_for_buffer(WAVEHDR* hdr, HANDLE hEvent)
{
/* At this point there are several possible states:
1) Empty or partial buffer (unqueued) - dwFlags == 0
2) Buffer queued or being played - dwFlags == WHDR_PREPARED | WHDR_INQUEUE
3) Buffer unqueued and finished being played - dwFlags == WHDR_PREPARED | WHDR_DONE
4) Buffer removed from queue, but not yet marked as done - dwFlags == WHDR_PREPARED
*/
/* Check buffer header and wait if it's being played. */
if (hdr->dwFlags & WHDR_PREPARED)
{
while(!(hdr->dwFlags & WHDR_DONE))
{
/*debug1("waiting for buffer %i...", state->next_buffer);*/
/* Waits for *a* buffer to finish. May not be the one we
want, so check again */
WaitForSingleObject(hEvent, INFINITE);
}
hdr->dwFlags = 0;
hdr->dwBufferLength = 0;
}
}
static int get_formats_win32(out123_handle *ao)
{
/* FIXME: support for smth besides MPG123_ENC_SIGNED_16? */
return MPG123_ENC_SIGNED_16;
}
/* Stores audio data to the fixed size buffers and pushes them into the playback queue.
I have one grief with that: The last piece of a track may not reach the output,
only full buffers sent... But we don't get smooth audio otherwise. */
static int write_win32(out123_handle *ao, unsigned char *buf, int len)
{
struct queue_state* state;
MMRESULT res;
WAVEHDR* hdr;
int rest_len; /* Input data bytes left for next recursion. */
int bufill; /* Bytes we stuff into buffer now. */
if(!ao || !ao->userptr) return -1;
if(!buf || len <= 0) return 0;
state = (struct queue_state*)ao->userptr;
hdr = &state->buffer_headers[state->next_buffer];
wait_for_buffer(hdr, state->play_done_event);
/* Now see how much we want to stuff in and then stuff it in. */
bufill = BUFFER_SIZE - hdr->dwBufferLength;
if(len < bufill) bufill = len;
rest_len = len - bufill;
memcpy(hdr->lpData + hdr->dwBufferLength, buf, bufill);
hdr->dwBufferLength += bufill;
if(hdr->dwBufferLength == BUFFER_SIZE)
{ /* Send the buffer out when it's full. */
hdr->dwFlags |= WHDR_PREPARED;
res = waveOutWrite(state->waveout, hdr, sizeof(WAVEHDR));
if(res != MMSYSERR_NOERROR) ereturn(-1, "Can't write to audio output device.");
/* Cycle to the next buffer in the ring queue */
state->next_buffer = (state->next_buffer + 1) % NUM_BUFFERS;
}
/* I'd like to propagate error codes or something... but there are no catchable surprises left.
Anyhow: Here is the recursion that makes ravenexp happy;-) */
if(rest_len && write_win32(ao, buf + bufill, rest_len) < 0) /* Write the rest. */
return -1;
else
return len;
}
/* Flush means abort any pending playback */
static void flush_win32(out123_handle *ao)
{
struct queue_state* state;
WAVEHDR* hdr;
if(!ao || !ao->userptr) return;
state = (struct queue_state*)ao->userptr;
/* Cancel any buffers in queue. Ignore errors since we are void and
can't return them anyway */
waveOutReset(state->waveout);
/* Discard any partial buffer */
hdr = &state->buffer_headers[state->next_buffer];
/* If WHDR_PREPARED is not set, this is (potentially) a partial buffer */
if (!(hdr->dwFlags & WHDR_PREPARED))
hdr->dwBufferLength = 0;
/* Finish processing the buffers */
drain_win32(ao);
}
/* output final buffer (if any) */
static void write_final_buffer(struct queue_state *state)
{
WAVEHDR* hdr;
hdr = &state->buffer_headers[state->next_buffer];
if((!(hdr->dwFlags & WHDR_PREPARED)) && (hdr->dwBufferLength != 0))
{
hdr->dwFlags |= WHDR_PREPARED;
/* ignore any errors */
waveOutWrite(state->waveout, hdr, sizeof(WAVEHDR));
/* Cycle to the next buffer in the ring queue */
state->next_buffer = (state->next_buffer + 1) % NUM_BUFFERS;
}
}
/* Note: I tried to fix this stuff without testing.
There were some obvious errors in the code.
Someone run this on a win32 machine! -- ThOr */
static void drain_win32(out123_handle *ao)
{
int i, z;
struct queue_state* state;
if(!ao || !ao->userptr) return;
state = (struct queue_state*)ao->userptr;
/* output final buffer (if any) */
write_final_buffer(state);
/* I _think_ I understood how this should work. -- ThOr */
z = state->next_buffer;
for(i = 0; i < NUM_BUFFERS; i++)
{
wait_for_buffer(&state->buffer_headers[z], state->play_done_event);
z = (z + 1) % NUM_BUFFERS;
}
}
static int close_win32(out123_handle *ao)
{
int i;
struct queue_state* state;
if(!ao || !ao->userptr) return -1;
state = (struct queue_state*)ao->userptr;
/* wait for all active buffers to complete */
drain_win32(ao);
CloseHandle(state->play_done_event);
for(i = 0; i < NUM_BUFFERS; i++)
{
state->buffer_headers[i].dwFlags |= WHDR_PREPARED;
waveOutUnprepareHeader(state->waveout, &state->buffer_headers[i], sizeof(WAVEHDR));
free(state->buffer_headers[i].lpData);
}
waveOutClose(state->waveout);
free(ao->userptr);
ao->userptr = 0;
return 0;
}
static int init_win32(out123_handle* ao)
{
if(!ao) return -1;
/* Set callbacks */
ao->open = open_win32;
ao->flush = flush_win32;
ao->write = write_win32;
ao->get_formats = get_formats_win32;
ao->close = close_win32;
/* Success */
return 0;
}
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "win32",
/* description */ "Audio output for Windows (winmm).",
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_win32,
};

View File

@@ -0,0 +1,497 @@
/*
win32_wasapi: audio output for Windows wasapi exclusive mode audio
copyright ?-2013 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
based on win32.c
*/
#define _WIN32_WINNT 0x601
#define COBJMACROS 1
#include "out123_int.h"
#include <initguid.h>
#include <audioclient.h>
#include <mmdeviceapi.h>
#include <avrt.h>
#include "debug.h"
#ifdef _MSC_VER
/* When compiling C code with MSVC it is only possible to declare, but not
define the WASAPI interface GUIDs using MS headers. So we define them
ourselves. */
#ifndef GUID_SECT
#define GUID_SECT
#endif
#define __DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
#define __DEFINE_IID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const IID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
#define __DEFINE_CLSID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const CLSID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
#define MPG123_DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
__DEFINE_CLSID(mpg123_CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
#define MPG123_DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
__DEFINE_IID(mpg123_IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
// "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2"
MPG123_DEFINE_IID(IAudioClient, 1cb9ad4c, dbfa, 4c32, b1, 78, c2, f5, 68, a7, 03, b2);
// "A95664D2-9614-4F35-A746-DE8DB63617E6"
MPG123_DEFINE_IID(IMMDeviceEnumerator, a95664d2, 9614, 4f35, a7, 46, de, 8d, b6, 36, 17, e6);
// "BCDE0395-E52F-467C-8E3D-C4579291692E"
MPG123_DEFINE_CLSID(IMMDeviceEnumerator, bcde0395, e52f, 467c, 8e, 3d, c4, 57, 92, 91, 69, 2e);
// "F294ACFC-3146-4483-A7BF-ADDCA7C260E2"
MPG123_DEFINE_IID(IAudioRenderClient, f294acfc, 3146, 4483, a7, bf, ad, dc, a7, c2, 60, e2);
#else
#define mpg123_IID_IAudioClient IID_IAudioClient
#define mpg123_IID_IMMDeviceEnumerator IID_IMMDeviceEnumerator
#define mpg123_CLSID_IMMDeviceEnumerator CLSID_MMDeviceEnumerator
#define mpg123_IID_IAudioRenderClient IID_IAudioRenderClient
#endif
/* Push mode does not work right yet, noisy audio, probably something to do with timing and buffers */
#define WASAPI_EVENT_MODE 1
#ifdef WASAPI_EVENT_MODE
#define Init_Flag AUDCLNT_STREAMFLAGS_EVENTCALLBACK
#define MOD_STRING "Experimental Audio output for Windows (wasapi event mode)."
#define BUFFER_TIME 20000000.0
#else
#define Init_Flag 0
#define MOD_STRING "Experimental Audio output for Windows (wasapi push mode)."
#define BUFFER_TIME 640000000.0
#endif
static int init_win32(out123_handle* ao);
static void flush_win32(out123_handle *ao);
/*
Module information data structure
*/
mpg123_module_t mpg123_output_module_info = {
/* api_version */ MPG123_MODULE_API_VERSION,
/* name */ "win32_wasapi",
/* description */ MOD_STRING,
/* revision */ "$Rev:$",
/* handle */ NULL,
/* init_output */ init_win32,
};
// REFERENCE_TIME time units per second and per millisecond
#define REFTIMES_PER_SEC 10000000
#define REFTIMES_PER_MILLISEC 10000
#define EXIT_ON_ERROR(hres) \
if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
/* todo: move into handle struct */
typedef struct _wasapi_state_struct {
IMMDeviceEnumerator *pEnumerator;
IMMDevice *pDevice;
IAudioClient *pAudioClient;
IAudioRenderClient *pRenderClient;
BYTE *pData;
UINT32 bufferFrameCount;
REFERENCE_TIME hnsRequestedDuration;
HANDLE hEvent;
HANDLE hTask;
size_t pData_off;
DWORD taskIndex;
char is_playing;
DWORD framesize;
} wasapi_state_struct;
/* setup endpoints */
static int open_win32(out123_handle *ao){
HRESULT hr = 0;
wasapi_state_struct *state;
debug1("%s",__FUNCTION__);
if(!ao || ao->userptr) return -1; /* userptr should really be null */
state = calloc(sizeof(*state),1);
if(!state) return -1;
state->hnsRequestedDuration = REFTIMES_PER_SEC;
ao->userptr = (void *)state;
CoInitialize(NULL);
hr = CoCreateInstance(&mpg123_CLSID_IMMDeviceEnumerator,NULL,CLSCTX_ALL, &mpg123_IID_IMMDeviceEnumerator,(void**)&state->pEnumerator);
debug("CoCreateInstance");
EXIT_ON_ERROR(hr)
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(state->pEnumerator,eRender, eConsole, &state->pDevice);
debug("IMMDeviceEnumerator_GetDefaultAudioEndpoint");
EXIT_ON_ERROR(hr)
hr = IMMDeviceActivator_Activate(state->pDevice,
&mpg123_IID_IAudioClient, CLSCTX_ALL,
NULL, (void**)&state->pAudioClient);
debug("IMMDeviceActivator_Activate");
EXIT_ON_ERROR(hr)
return 0;
Exit:
debug2("%s failed with %lx", __FUNCTION__, hr);
return 1;
}
/*
typedef struct tWAVEFORMATEX {
WORD wFormatTag;
WORD nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD nBlockAlign;
WORD wBitsPerSample;
WORD cbSize;
} WAVEFORMATEX;
*/
static int formats_generator(const out123_handle * const ao, const int waveformat, WAVEFORMATEX *const format){
DWORD bytes_per_sample = 0;
WORD tag = WAVE_FORMAT_PCM;
debug1("%s",__FUNCTION__);
int ret = waveformat;
switch(waveformat){
case MPG123_ENC_SIGNED_8:
bytes_per_sample = 1;
break;
case MPG123_ENC_FLOAT_32:
tag = WAVE_FORMAT_IEEE_FLOAT;
case MPG123_ENC_SIGNED_32:
bytes_per_sample = 4;
break;
case MPG123_ENC_SIGNED_16:
bytes_per_sample = 2;
break;
case MPG123_ENC_SIGNED_24:
bytes_per_sample = 3;
break;
default:
debug1("uh oh unknown %d",waveformat);
ret = 0;
break;
}
format->wFormatTag = tag;
format->nChannels = ao->channels;
format->nSamplesPerSec = ao->rate;
format->nAvgBytesPerSec = ao->channels * bytes_per_sample * ao->rate;
format->nBlockAlign = ao->channels * bytes_per_sample;
format->wBitsPerSample = bytes_per_sample * 8;
format->cbSize = 0;
return ret;
}
/* check supported formats */
static int get_formats_win32(out123_handle *ao){
/* PLEASE check with write_init and write_win32 buffer size calculation in case it is able to support something other than 16bit */
HRESULT hr;
int ret = 0;
debug1("%s",__FUNCTION__);
if(!ao || !ao->userptr) return -1;
wasapi_state_struct *state = (wasapi_state_struct *) ao->userptr;
debug2("channels %d, rate %ld",ao->channels, ao->rate);
WAVEFORMATEX wf;
if(ao->format & MPG123_ENC_SIGNED_8){
formats_generator(ao,MPG123_ENC_SIGNED_8,&wf);
if((hr = IAudioClient_IsFormatSupported(state->pAudioClient,AUDCLNT_SHAREMODE_EXCLUSIVE, &wf, NULL)) == S_OK)
ret |= MPG123_ENC_SIGNED_8;
if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT) debug1("MPG123_ENC_SIGNED_8 %ld not supported", ao->rate);
}
if(ao->format & MPG123_ENC_SIGNED_16){
formats_generator(ao,MPG123_ENC_SIGNED_16,&wf);
if((hr = IAudioClient_IsFormatSupported(state->pAudioClient,AUDCLNT_SHAREMODE_EXCLUSIVE, &wf, NULL)) == S_OK)
ret |= MPG123_ENC_SIGNED_16;
if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT) debug1("MPG123_ENC_SIGNED_16 %ld not supported", ao->rate);
}
if(ao->format & MPG123_ENC_SIGNED_32){
formats_generator(ao,MPG123_ENC_SIGNED_32,&wf);
if((hr = IAudioClient_IsFormatSupported(state->pAudioClient,AUDCLNT_SHAREMODE_EXCLUSIVE, &wf, NULL)) == S_OK)
ret |= MPG123_ENC_SIGNED_32;
if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT) debug1("MPG123_ENC_SIGNED_32 %ld not supported", ao->rate);
}
if(ao->format & MPG123_ENC_FLOAT_32){
formats_generator(ao,MPG123_ENC_FLOAT_32,&wf);
if((hr = IAudioClient_IsFormatSupported(state->pAudioClient,AUDCLNT_SHAREMODE_EXCLUSIVE, &wf, NULL)) == S_OK)
ret |= MPG123_ENC_FLOAT_32;
if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT) debug1("MPG123_ENC_FLOAT_32 %ld not supported", ao->rate);
}
if(ao->format & MPG123_ENC_SIGNED_24){
formats_generator(ao,MPG123_ENC_SIGNED_24,&wf);
if((hr = IAudioClient_IsFormatSupported(state->pAudioClient,AUDCLNT_SHAREMODE_EXCLUSIVE, &wf, NULL)) == S_OK)
ret |= MPG123_ENC_SIGNED_24;
if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT) debug1("MPG123_ENC_SIGNED_24 %ld not supported", ao->rate);
}
return ret; /* afaik only 16bit 44.1kHz/48kHz has been known to work */
}
/* setup with agreed on format, for now only MPG123_ENC_SIGNED_16 */
static int write_init(out123_handle *ao){
HRESULT hr;
double offset = 0.5;
debug1("%s",__FUNCTION__);
if(!ao || !ao->userptr) return -1;
wasapi_state_struct *state = (wasapi_state_struct *) ao->userptr;
WAVEFORMATEX s16;
formats_generator(ao,ao->format,&s16);
state->framesize = s16.nBlockAlign;
debug1("block size %ld", state->framesize);
/* cargo cult code */
hr = IAudioClient_GetDevicePeriod(state->pAudioClient,NULL, &state->hnsRequestedDuration);
debug("IAudioClient_GetDevicePeriod OK");
reinit:
hr = IAudioClient_Initialize(state->pAudioClient,
AUDCLNT_SHAREMODE_EXCLUSIVE,
Init_Flag,
state->hnsRequestedDuration,
state->hnsRequestedDuration,
&s16,
NULL);
debug("IAudioClient_Initialize OK");
/* something about buffer sizes on Win7, fixme might loop forever */
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED){
if (offset > 10.0) goto Exit; /* is 10 enough to break out of the loop?*/
debug("AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED");
IAudioClient_GetBufferSize(state->pAudioClient,&state->bufferFrameCount);
/* double buffered */
state->hnsRequestedDuration = (REFERENCE_TIME)((BUFFER_TIME / s16.nSamplesPerSec * state->bufferFrameCount) + offset);
offset += 0.5;
IAudioClient_Release(state->pAudioClient);
state->pAudioClient = NULL;
hr = IMMDeviceActivator_Activate(state->pDevice,
&mpg123_IID_IAudioClient, CLSCTX_ALL,
NULL, (void**)&state->pAudioClient);
debug("IMMDeviceActivator_Activate");
goto reinit;
}
EXIT_ON_ERROR(hr)
EXIT_ON_ERROR(hr)
hr = IAudioClient_GetService(state->pAudioClient,
&mpg123_IID_IAudioRenderClient,
(void**)&state->pRenderClient);
debug("IAudioClient_GetService OK");
EXIT_ON_ERROR(hr)
#ifdef WASAPI_EVENT_MODE
state->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
debug("CreateEvent OK");
if(!state->hEvent) goto Exit;
hr = IAudioClient_SetEventHandle(state->pAudioClient,state->hEvent);
EXIT_ON_ERROR(hr);
#endif
hr = IAudioClient_GetBufferSize(state->pAudioClient,&state->bufferFrameCount);
debug("IAudioClient_GetBufferSize OK");
EXIT_ON_ERROR(hr)
return 0;
Exit:
debug2("%s failed with %lx", __FUNCTION__, hr);
return 1;
}
/* Set play mode if unset, also raise thread priority */
static HRESULT play_init(out123_handle *ao){
HRESULT hr = S_OK;
if(!ao || !ao->userptr) return -1;
wasapi_state_struct *state = (wasapi_state_struct *) ao->userptr;
if(!state->is_playing){
debug1("%s",__FUNCTION__);
state->hTask = AvSetMmThreadCharacteristicsW(L"Pro Audio", &state->taskIndex);
hr = IAudioClient_Start(state->pAudioClient);
state->is_playing = 1;
debug("IAudioClient_Start");
EXIT_ON_ERROR(hr)
}
return hr;
Exit:
debug2("%s failed with %lx", __FUNCTION__, hr);
return hr;
}
/* copy audio into IAudioRenderClient provided buffer */
static int write_win32(out123_handle *ao, unsigned char *buf, int len){
HRESULT hr;
size_t to_copy = 0;
debug1("%s",__FUNCTION__);
if(!ao || !ao->userptr) return -1;
wasapi_state_struct *state = (wasapi_state_struct *) ao->userptr;
if(!len) return 0;
if(!state->pRenderClient) write_init(ao);
size_t frames_in = len/state->framesize; /* Frames in buf, is framesize even correct? */
debug("mode entered");
#ifdef WASAPI_EVENT_MODE
/* Event mode WASAPI */
DWORD retval = -1;
int flag = 0; /* Silence flag */
feed_again:
if(!state->pData){
/* Acquire buffer */
hr = IAudioRenderClient_GetBuffer(state->pRenderClient,state->bufferFrameCount, &state->pData);
debug("IAudioRenderClient_GetBuffer");
EXIT_ON_ERROR(hr)
}
if(frames_in){ /* Did we get half a frame?? non-zero len smaller than framesize? */
/* We must put in exactly the amount of frames specified by IAudioRenderClient_GetBuffer */
while(state->pData_off < state->bufferFrameCount){
to_copy = state->bufferFrameCount - state->pData_off;
debug3("pData_off %I64d, bufferFrameCount %d, to_copy %I64d", state->pData_off, state->bufferFrameCount, to_copy);
if(to_copy > frames_in){
/* buf can fit in provided buffer space */
debug1("all buffers copied, %I64d", frames_in);
memcpy(state->pData+state->pData_off*state->framesize,buf,state->framesize*(frames_in));
state->pData_off += frames_in;
frames_in = 0;
break;
} else {
/* buf too big, needs spliting */
debug1("partial buffers %I64d", to_copy);
memcpy(state->pData+state->pData_off*state->framesize,buf,state->framesize*(to_copy));
state->pData_off += to_copy;
buf+=(to_copy*state->framesize);
frames_in -= to_copy;
}
}
} else {
/* In case we ever get half a frame, is it possible? */
flag = AUDCLNT_BUFFERFLAGS_SILENT;
}
debug2("Copied %I64d, left %I64d", state->pData_off, frames_in);
if(state->pData_off == state->bufferFrameCount) {
/* Tell IAudioRenderClient that buffer is filled and released */
hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient,state->pData_off, flag);
state->pData_off = 0;
state->pData = NULL;
debug("IAudioRenderClient_ReleaseBuffer");
EXIT_ON_ERROR(hr)
if(!state->is_playing){
hr = play_init(ao);
EXIT_ON_ERROR(hr)
}
/* wait for next pull event */
retval = WaitForSingleObject(state->hEvent, 2000);
if (retval != WAIT_OBJECT_0){
/* Event handle timed out after a 2-second wait, something went very wrong */
IAudioClient_Stop(state->pAudioClient);
hr = ERROR_TIMEOUT;
goto Exit;
}
}
if(frames_in > 0)
goto feed_again;
#else /* PUSH mode code */
UINT32 numFramesAvailable, numFramesPadding;
debug1("block size %ld", state->framesize);
feed_again:
/* How much buffer do we get to use? */
hr = IAudioClient_GetBufferSize(state->pAudioClient,&state->bufferFrameCount);
debug("IAudioRenderClient_GetBuffer");
EXIT_ON_ERROR(hr)
hr = IAudioClient_GetCurrentPadding(state->pAudioClient,&numFramesPadding);
debug("IAudioClient_GetCurrentPadding");
EXIT_ON_ERROR(hr)
/* How much buffer is writable at the moment? */
numFramesAvailable = state->bufferFrameCount - numFramesPadding;
debug3("numFramesAvailable %d, bufferFrameCount %d, numFramesPadding %d", numFramesAvailable, state->bufferFrameCount, numFramesPadding);
if(numFramesAvailable > frames_in){
/* can fit all frames now */
state->pData_off = 0;
to_copy = frames_in;
} else {
/* copy whatever that fits in the buffer */
state->pData_off = frames_in - numFramesAvailable;
to_copy = numFramesAvailable;
}
/* Acquire buffer */
hr = IAudioRenderClient_GetBuffer(state->pRenderClient,to_copy,&state->pData);
debug("IAudioRenderClient_GetBuffer");
EXIT_ON_ERROR(hr)
memcpy(state->pData,buf+state->pData_off * state->framesize,to_copy*state->framesize);
/* Release buffer */
hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient,to_copy, 0);
debug("IAudioRenderClient_ReleaseBuffer");
EXIT_ON_ERROR(hr)
if(!state->is_playing){
hr = play_init(ao);
EXIT_ON_ERROR(hr)
}
frames_in -= to_copy;
/* Wait sometime for buffer to empty? */
DWORD sleeptime = (DWORD)(state->hnsRequestedDuration/REFTIMES_PER_MILLISEC/ao->rate);
debug1("Sleeping %ld msec", sleeptime);
Sleep(sleeptime);
if (frames_in)
goto feed_again;
#endif
return len;
Exit:
debug2("%s failed with %lx", __FUNCTION__, hr);
return -1;
}
static void flush_win32(out123_handle *ao){
/* Wait for the last buffer to play before stopping. */
debug1("%s",__FUNCTION__);
if(!ao || !ao->userptr) return;
wasapi_state_struct *state = (wasapi_state_struct *) ao->userptr;
HRESULT hr;
if(!state->pAudioClient) return;
state->pData = NULL;
hr = IAudioClient_Stop(state->pAudioClient);
EXIT_ON_ERROR(hr)
IAudioClient_Reset(state->pAudioClient);
EXIT_ON_ERROR(hr)
return;
Exit:
debug2("%s IAudioClient_Stop with %lx", __FUNCTION__, hr);
}
static int close_win32(out123_handle *ao)
{
debug1("%s",__FUNCTION__);
if(!ao || !ao->userptr) return -1;
wasapi_state_struct *state = (wasapi_state_struct *) ao->userptr;
#ifdef WASAPI_EVENT_MODE
if(state->pData){
/* Play all in buffer before closing */
debug("Flushing remaining buffers");
IAudioRenderClient_ReleaseBuffer(state->pRenderClient,state->bufferFrameCount, 0);
WaitForSingleObject(state->hEvent, 2000);
state->pData = NULL;
}
#endif
if(state->pAudioClient) IAudioClient_Stop(state->pAudioClient);
if(state->pRenderClient) IAudioRenderClient_Release(state->pRenderClient);
if(state->pAudioClient) IAudioClient_Release(state->pAudioClient);
if(state->hTask) AvRevertMmThreadCharacteristics(state->hTask);
if(state->pEnumerator) IMMDeviceEnumerator_Release(state->pEnumerator);
if(state->pDevice) IMMDevice_Release(state->pDevice);
CoUninitialize();
free(state);
ao->userptr = NULL;
return 0;
}
static int init_win32(out123_handle* ao){
debug1("%s",__FUNCTION__);
if(!ao) return -1;
/* Set callbacks */
ao->open = open_win32;
ao->flush = flush_win32;
ao->write = write_win32;
ao->get_formats = get_formats_win32;
ao->close = close_win32;
ao->userptr = NULL;
/* Success */
return 0;
}

View File

@@ -0,0 +1,588 @@
/*
out123: audio output interface
copyright 1995-2016 by the mpg123 project,
free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written as audio.h by Michael Hipp, reworked into out123 API
by Thomas Orgis
*/
#ifndef _OUT123_H_
#define _OUT123_H_
/** \file out123.h The header file for the libout123 audio output facility. */
/* We only need size_t definition. */
#include <stddef.h>
/* Common audio encoding specification, including a macro for getting
* size of encodined samples in bytes. Said macro is still hardcoded
* into out123_encsize(). Relying on this one may help an old program
* know sizes of encodings added to fmt123.h later on.
* If you don't care, just use the macro.
*/
#include <fmt123.h>
/** A macro to check at compile time which set of API functions to expect.
* This should be incremented at least each time a new symbol is added
* to the header.
*/
#define OUT123_API_VERSION @OUTAPI_VERSION@
#ifndef MPG123_EXPORT
/** Defines needed for MS Visual Studio(tm) DLL builds.
* Every public function must be prefixed with MPG123_EXPORT. When building
* the DLL ensure to define BUILD_MPG123_DLL. This makes the function accessible
* for clients and includes it in the import library which is created together
* with the DLL. When consuming the DLL ensure to define LINK_MPG123_DLL which
* imports the functions from the DLL.
*/
#ifdef BUILD_MPG123_DLL
/* The dll exports. */
#define MPG123_EXPORT __declspec(dllexport)
#else
#ifdef LINK_MPG123_DLL
/* The exe imports. */
#define MPG123_EXPORT __declspec(dllimport)
#else
/* Nothing on normal/UNIX builds */
#define MPG123_EXPORT
#endif
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/** \defgroup out123_api out123 library API
* This is out123, a library focused on continuous playback of audio streams
* via various platform-specific output methods. It glosses over details of
* the native APIs to give an interface close to simply writing data to a
* file. There might be the option to tune details like buffer (period) sizes
* and the number of them on the device side in future, but the focus of the
* library is to ease the use case of just getting that raw audio data out
* there, without interruptions.
*
* The basic idea is to create a handle with out123_new() and open a certain
* output device (using a certain driver module, possibly build-time defaults)
* with out123_open(). Now, you can query the output device for supported
* encodings for given rate and channel count with out123_get_encodings() and
* decide what to use for actually starting playback with out123_start().
*
* Then, you just need to provide (interleaved pcm) data for playback with
* out123_play(), which will block when the device's buffers are full. You get
* your timing from that (instead of callbacks). If your program does the
* production of the audio data just a little bit faster than the playback,
* causing out123_play() to block ever so briefly, you're fine.
*
* You stop playback with out123_stop(), or just close the device and driver
* via out123_close(), or even just decide to drop it all and do out123_del()
* right away when you're done.
*
* There are other functions for specific needs, but the basic idea should be
* covered by the above.
@{
*/
/** Opaque structure for the libout123 handle. */
struct out123_struct;
/** Typedef shortcut as preferrend name for the handle type. */
typedef struct out123_struct out123_handle;
/** Enumeration of codes for the parameters that it is possible to set/get. */
enum out123_parms
{
OUT123_FLAGS = 1 /**< integer, various flags, see enum out123_flags */
, OUT123_PRELOAD /**< float, fraction of buffer to fill before playback */
, OUT123_GAIN /**< integer, output device gain (module-specific) */
, OUT123_VERBOSE /**< integer, verbosity to stderr, >= 0 */
, OUT123_DEVICEBUFFER /**<
* float, length of device buffer in seconds;
* This might be ignored, might have only a loose relation to actual
* buffer sizes and latency, depending on output driver. Try to tune
* this before opening a device if you want to influcence latency or reduce
* dropouts. Value <= 0 uses some default, usually favouring stable playback
* over low latency. Values above 0.5 are probably too much.
*/
, OUT123_PROPFLAGS /**< integer, query driver/device property flags (r/o) */
, OUT123_NAME /**< string, name of this instance (NULL restores default);
* The value returned by out123_getparam() might be different if the audio
* backend changed it (to be unique among clients, p.ex.).
* TODO: The name provided here is used as prefix in diagnostic messages. */
, OUT123_BINDIR /**< string, path to a program binary directory to use
* as starting point in the search for the output module directory
* (e.g. ../lib/mpg123 or ./plugins). The environment variable MPG123_MODDIR
* is always tried first and the in-built installation path last.
*/
};
/** Flags to tune out123 behaviour */
enum out123_flags
{
OUT123_HEADPHONES = 0x01 /**< output to headphones (if supported) */
, OUT123_INTERNAL_SPEAKER = 0x02 /**< output to speaker (if supported) */
, OUT123_LINE_OUT = 0x04 /**< output to line out (if supported) */
, OUT123_QUIET = 0x08 /**< no printouts to standard error */
, OUT123_KEEP_PLAYING = 0x10 /**<
* When this is set (default), playback continues in a loop when the device
* does not consume all given data at once. This happens when encountering
* signals (like SIGSTOP, SIGCONT) that cause interruption of the underlying
* functions.
* Note that this flag is meaningless when the optional buffer is employed,
* There, your program will always block until the buffer completely took
* over the data given to it via out123_play(), unless a communication error
* arises.
*/
};
/** Read-only output driver/device property flags (OUT123_PROPFLAGS). */
enum out123_propflags
{
OUT123_PROP_LIVE = 0x01 /**< This is a live output, meaning that
* special care might be needed for pauses in playback (p.ex. stream
* of silence instead of interruption), as opposed to files on disk.
*/
, OUT123_PROP_PERSISTENT = 0x02 /**< This (live) output does not need
* special care for pauses (continues with silence itself),
* out123_pause() does nothing to the device.
*/
};
/** Create a new output handle.
* This only allocates and initializes memory, so the only possible
* error condition is running out of memory.
* \return pointer to new handle or NULL on error
*/
MPG123_EXPORT
out123_handle *out123_new(void);
/** Delete output handle.
* This implies out123_close().
*/
MPG123_EXPORT
void out123_del(out123_handle *ao);
/** Error code enumeration
* API calls return a useful (positve) value or zero (OUT123_OK) on simple
* success. A negative value (-1 == OUT123_ERR) usually indicates that some
* error occured. Which one, that can be queried using out123_errcode()
* and friends.
*/
enum out123_error
{
OUT123_ERR = -1 /**< generic alias for verbosity, always == -1 */
, OUT123_OK = 0 /**< just a name for zero, not going to change */
, OUT123_DOOM /**< dazzled, out of memory */
, OUT123_BAD_DRIVER_NAME /**< bad driver name given */
, OUT123_BAD_DRIVER /**< unspecified issue loading a driver */
, OUT123_NO_DRIVER /**< no driver loaded */
, OUT123_NOT_LIVE /**< no active audio device */
, OUT123_DEV_PLAY /**< some device playback error */
, OUT123_DEV_OPEN /**< error opening device */
, OUT123_BUFFER_ERROR /**<
* Some (really unexpected) error in buffer infrastructure.
*/
, OUT123_MODULE_ERROR /**< basic failure in module loading */
, OUT123_ARG_ERROR /**< some bad function arguments supplied */
, OUT123_BAD_PARAM /**< unknown parameter code */
, OUT123_SET_RO_PARAM /**< attempt to set read-only parameter */
, OUT123_BAD_HANDLE /**< bad handle pointer (NULL, usually) */
, OUT123_ERRCOUNT /**< placeholder for shaping arrays */
};
/** Get string representation of last encountered error in the
* context of given handle.
* \param ao handle
* \return error string
*/
MPG123_EXPORT
const char* out123_strerror(out123_handle *ao);
/** Get the plain errcode intead of a string.
* Note that this used to return OUT123_ERR instead of
* OUT123_BAD_HANDLE in case of ao==NULL before mpg123-1.23.5 .
* \param ao handle
* \return error code recorded in handle or OUT123_BAD_HANDLE
*/
MPG123_EXPORT
int out123_errcode(out123_handle *ao);
/** Return the error string for a given error code.
* \param errcode the integer error code
* \return error string
*/
MPG123_EXPORT
const char* out123_plain_strerror(int errcode);
/** Set a desired output buffer size.
* This starts a separate process that handles the audio output, decoupling
* the latter from the main process with a memory buffer and saving you the
* burden to ensure sparing CPU cycles for actual playback.
* This is for applicatons that prefer continuous playback over small latency.
* In other words: The kind of applications that out123 is designed for.
* This routine always kills off any currently active audio output module /
* device, even if you just disable the buffer when there is no buffer.
*
* Keep this in mind for memory-constrainted systems: Activating the
* buffer causes a fork of the calling process, doubling the virtual memory
* use. Depending on your operating system kernel's behaviour regarding
* memory overcommit, it might be wise to call out123_set_buffer() very
* early in your program before allocating lots of memory.
*
* There _might_ be a change to threads in future, but for now this is
* classic fork with shared memory, working without any threading library.
* If your platform or build does not support that, you will always get an
* error on trying to set up a non-zero buffer (but the API call will be
* present).
*
* Also, if you do intend to use this from a multithreaded program, think
* twice and make sure that your setup is happy with forking full-blown
* processes off threaded programs. Probably you are better off spawning a
* buffer thread yourself.
*
* \param ao handle
* \param buffer_bytes size (bytes) of a memory buffer for decoded audio,
* a value of zero disables the buffer.
* \return 0 on success, OUT123_ERR on error
*/
MPG123_EXPORT
int out123_set_buffer(out123_handle *ao, size_t buffer_bytes);
/** Set a specific parameter, for a specific out123_handle, using a parameter
* code chosen from the out123_parms enumeration, to the specified value.
* The parameters usually only change what happens on next out123_open, not
* incfluencing running operation.
* \param ao handle
* \param code parameter code
* \param value input value for integer parameters
* \param fvalue input value for floating point parameters
* \param svalue input value for string parameters (contens are copied)
* \return 0 on success, OUT123_ERR on error.
*/
MPG123_EXPORT
int out123_param( out123_handle *ao, enum out123_parms code
, long value, double fvalue, const char *svalue );
#define out123_param_int(ao, code, value) \
out123_param((ao), (code), (value), 0., NULL)
#define out123_param_float(ao, code, value) \
out123_param((ao), (code), 0, (value), NULL)
#define out123_param_string(ao, code, value) \
out123_param((ao), (code), 0, 0., (value))
/** Get a specific parameter, for a specific out123_handle, using a parameter
* code chosen from the out123_parms enumeration, to the specified value.
* \param ao handle
* \param code parameter code
* \param ret_value output address for integer parameters
* \param ret_fvalue output address for floating point parameters
* \param ret_svalue output address for string parameters (pointer to
* internal memory, so no messing around, please)
* \return 0 on success, OUT123_ERR on error (bad parameter name or bad handle).
*/
MPG123_EXPORT
int out123_getparam( out123_handle *ao, enum out123_parms code
, long *ret_value, double *ret_fvalue, char* *ret_svalue );
#define out123_getparam_int(ao, code, value) \
out123_getparam((ao), (code), (value), NULL, NULL)
#define out123_getparam_float(ao, code, value) \
out123_getparam((ao), (code), NULL, (value), NULL)
#define out123_getparam_string(ao, code, value) \
out123_getparam((ao), (code), NULL, NULL, (value))
/** Copy parameters from another out123_handle.
* \param ao handle
* \param from_ao the handle to copy parameters from
* \return 0 in success, -1 on error
*/
MPG123_EXPORT
int out123_param_from(out123_handle *ao, out123_handle* from_ao);
/** Get list of driver modules reachable in system in C argv-style format.
* The client is responsible for freeing the memory of both the individual
* strings and the lists themselves.
* A module that is not loadable because of missing libraries is simply
* skipped. You will get stderr messages about that unless OUT123_QUIET was
* was set, though. Failure to open the module directory is a serious error,
* resulting in negative return value.
* \param ao handle
* \param names address for storing list of names
* \param descr address for storing list of descriptions
* \return number of drivers found, -1 on error
*/
MPG123_EXPORT
int out123_drivers(out123_handle *ao, char ***names, char ***descr);
/** Open an output device with a certain driver
* Note: Opening means that the driver code is loaded and the desired
* device name recorded, possibly tested for availability or tentatively
* opened. After out123_open(), you can ask for supported encodings
* and then really open the device for playback with out123_start().
* \param ao handle
* \param driver (comma-separated list of) output driver name(s to try),
* NULL for default (stdout for file-based drivers)
* \param device device name to open, NULL for default
* \return 0 on success, -1 on error.
*/
MPG123_EXPORT
int out123_open(out123_handle *ao, const char* driver, const char* device);
/** Give info about currently loaded driver and device
* Any of the return addresses can be NULL if you are not interested in
* everything. You get pointers to internal storage. They are valid
* as long as the driver/device combination is opened.
* The device may be NULL indicating some unnamed default.
* TODO: Make the driver modules return names for such defaults.
* \param ao handle
* \param driver return address for driver name
* \param device return address for device name
* \return 0 on success, -1 on error (i.e. no driver loaded)
*/
MPG123_EXPORT
int out123_driver_info(out123_handle *ao, char **driver, char **device);
/** Close the current output device and driver.
* This implies out123_drain() to ensure no data is lost.
* With a buffer, that might cause considerable delay during
* which your main application is blocked waiting.
* Call out123_drop() beforehand if you want to end things
* quickly.
* \param ao handle
*/
MPG123_EXPORT
void out123_close(out123_handle *ao);
/** Get supported audio encodings for given rate and channel count,
* for the currently openend audio device.
* TODO: Reopening the underlying audio device for each query
* is dumb, at least when dealing with JACK. It takes
* a long time and is just a waste. Reconsider that.
* Make sure that all output modules are fine with it, though!
* Usually, a wider range of rates is supported, but the number
* of sample encodings is limited, as is the number of channels.
* So you can call this with some standard rate and hope that the
* returned encodings work also for others, with the tested channel
* count.
* The return value of -1 on some encountered error conveniently also
* does not match any defined format (only 15 bits used for encodings,
* so this would even work with 16 bit integers).
* This implies out123_stop() to enter query mode.
* \param ao handle
* \param rate sampling rate
* \param channels number of channels
* \return supported encodings combined with bitwise or, to be checked
* against your favourite bitmask, -1 on error
*/
MPG123_EXPORT
int out123_encodings(out123_handle *ao, long rate, int channels);
/** Return the size (in bytes) of one mono sample of the named encoding.
* \param encoding The encoding value to analyze.
* \return positive size of encoding in bytes, 0 on invalid encoding. */
MPG123_EXPORT int out123_encsize(int encoding);
/** Get list of supported formats for currently opened audio device.
* Given a list of sampling rates and minimal/maximal channel count,
* this quickly checks what formats are supported with these
* constraints. The first entry is always reserved for a default
* format for the output device. If there is no such default,
* all values of the format are -1.
* For each requested combination of rate and channels, a format entry is
* created, possible with encoding value 0 to indicate that this combination
* has been tested and rejected. So, when there is no basic error, the
* number of returned format entries should be
* (ratecount*(maxchannels-minchannels+1)+1)
* . But instead of forcing you to guess, this will be allocated by
* successful run.
* For the first entry, the encoding member is supposed to be a definite
* encoding, for the others it is a bitwise combination of all possible
* encodings.
* This function is more efficient than many calls to out123_encodings().
* \param ao handle
* \param rates pointer to an array of sampling rates, may be NULL for none
* \param ratecount number of provided sampling rates
* \param minchannels minimal channel count
* \param maxchannels maximal channel count
* \param fmtlist return address for array of supported formats
* the encoding field of each entry is a combination of all
* supported encodings at this rate and channel count;
* Memory shall be freed by user.
* \return number of returned format enries, -1 on error
*/
MPG123_EXPORT
int out123_formats( out123_handle *ao, const long *rates, int ratecount
, int minchannels, int maxchannels
, struct mpg123_fmt **fmtlist );
/** Get list of encodings known to the library.
* You are responsible for freeing the allocated array.
* \param enclist return address for allocated array of encoding codes
* \return number of encodings, -1 on error
*/
MPG123_EXPORT
int out123_enc_list(int **enclist);
/** Find encoding code by name.
* \param name short or long name to find encoding code for
* \return encoding if found (enum mpg123_enc_enum), else 0
*/
MPG123_EXPORT
int out123_enc_byname(const char *name);
/** Get name of encoding.
* \param encoding code (enum mpg123_enc_enum)
* \return short name for valid encodings, NULL otherwise
*/
MPG123_EXPORT
const char* out123_enc_name(int encoding);
/** Get long name of encoding.
* \param encoding code (enum mpg123_enc_enum)
* \return long name for valid encodings, NULL otherwise
*/
MPG123_EXPORT
const char* out123_enc_longname(int encoding);
/** Start playback with a certain output format
* It might be a good idea to have audio data handy to feed after this
* returns with success.
* Rationale for not taking a pointer to struct mpg123_fmt: This would
* always force you to deal with that type and needlessly enlarge the
* shortest possible program.
* \param ao handle
* \param encoding sample encoding (values matching libmpg123 API)
* \param channels number of channels (1 or 2, usually)
* \param rate sampling rate
* \return 0 on success, negative on error (bad format, usually)
*/
MPG123_EXPORT
int out123_start( out123_handle *ao
, long rate, int channels, int encoding );
/** Pause playback
* Interrupt playback, holding any data in the optional buffer.
*
* This closes the audio device if it is a live sink, ready to be re-opened
* by out123_continue() or out123_play() with the existing parameters.
* \param ao handle
*/
MPG123_EXPORT
void out123_pause(out123_handle *ao);
/** Continue playback
* The counterpart to out123_pause(). Announce to the driver that playback
* shall continue.
*
* Playback might not resume immediately if the optional buffer is configured
* to wait for a minimum fill and close to being empty. You can force playback
* of the last scrap with out123_drain(), or just by feeding more data with
* out123_play(), which will trigger out123_continue() for you, too.
* \param ao handle
*/
MPG123_EXPORT
void out123_continue(out123_handle *ao);
/** Stop playback.
* This waits for pending audio data to drain to the speakers.
* You might want to call out123_drop() before stopping if you want
* to end things right away.
* \param ao handle
*/
MPG123_EXPORT
void out123_stop(out123_handle *ao);
/** Hand over data for playback and wait in case audio device is busy.
* This survives non-fatal signals like SIGSTOP/SIGCONT and keeps on
* playing until the buffer is done with if the flag
* OUT123_KEEP_PLAYING ist set (default). So, per default, if
* you provided a byte count divisible by the PCM frame size, it is an
* error when less bytes than given are played.
* To be sure if an error occured, check out123_errcode().
* Also note that it is no accident that the buffer parameter is not marked
* as constant. Some output drivers might need to do things like swap
* byte order. This is done in-place instead of wasting memory on yet
* another copy.
* \param ao handle
* \param buffer pointer to raw audio data to be played
* \param bytes number of bytes to read from the buffer
* \return number of bytes played (might be less than given, even zero)
*/
MPG123_EXPORT
size_t out123_play( out123_handle *ao
, void *buffer, size_t bytes );
/** Drop any buffered data, making next provided data play right away.
* This does not imply an actual pause in playback.
* You are expected to play something, unless you called out123_pause().
* Feel free to call out123_stop() afterwards instead for a quicker
* exit than the implied out123_drain().
* For live sinks, this may include dropping data from their buffers.
* For others (files), this only concerns data in the optional buffer.
* \param ao handle
*/
MPG123_EXPORT
void out123_drop(out123_handle *ao);
/** Drain the output, waiting until all data went to the hardware.
* This does imply out123_continue() before and out123_pause()
* after draining.
* This might involve only the optional buffer process, or the
* buffers on the audio driver side, too.
* \param ao handle
*/
MPG123_EXPORT
void out123_drain(out123_handle *ao);
/** Drain the output, but only partially up to the given number of
* bytes. This gives you the opportunity to do something while
* the optional buffer is writing remaining data instead of having
* one atomic API call for it all.
*
* It is wholly expected that the return value of out123_buffered()
* before and after calling this has a bigger difference than the
* provided limit, as the buffer is writing all the time in the
* background.
*
* This is just a plain out123_drain() if the optional buffer is not
* in use. Also triggers out123_continue(), but only out123_pause()
* if there is no buffered data anymore.
* \param ao handle
* \param bytes limit of buffered bytes to drain
* \return number of bytes drained from buffer
*/
MPG123_EXPORT
void out123_ndrain(out123_handle *ao, size_t bytes);
/** Get an indication of how many bytes reside in the optional buffer.
* This might get extended to tell the number of bytes queued up in the
* audio backend, too.
* \param ao handle
* \return number of bytes in out123 library buffer
*/
MPG123_EXPORT
size_t out123_buffered(out123_handle *ao);
/** Extract currently used audio format from handle.
* matching mpg123_getformat().
* Given return addresses may be NULL to indicate no interest.
* \param ao handle
* \param rate address for sample rate
* \param channels address for channel count
* \param encoding address for encoding
* \param framesize size of a full PCM frame (for convenience)
* \return 0 on success, -1 on error
*/
MPG123_EXPORT
int out123_getformat( out123_handle *ao
, long *rate, int *channels, int *encoding, int *framesize );
/* @} */
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,115 @@
/*
out123_int: internal header for libout123
copyright ?-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp (some traces left)
*/
#ifndef _MPG123_OUT123_INT_H_
#define _MPG123_OUT123_INT_H_
#include "config.h"
#include "intsym.h"
#include "abi_align.h"
#include "compat.h"
#include "out123.h"
#include "module.h"
#ifndef NOXFERMEM
#include "xfermem.h"
#endif
/* 3% rate tolerance */
#define AUDIO_RATE_TOLERANCE 3
/* Keep those internally? To the outside, it's just a selection of
driver modules. */
enum {
DECODE_TEST, /* "test" */
DECODE_AUDIO, /* gone */
DECODE_FILE, /* "raw" */
DECODE_BUFFER, /* internal use only, if at all */
DECODE_WAV, /* wav */
DECODE_AU, /* au */
DECODE_CDR, /* cdr */
DECODE_AUDIOFILE /* internal use only, if at all */
};
/* Playback states mostly for the buffer process.
Maybe also used in main program. */
enum playstate
{
play_dead = 0 /* nothing playing, nothing loaded */
, play_stopped /* driver present, but no device configured/opened */
/* The ordering is used, state > play_stopped means some device is opened. */
, play_paused /* paused, ready to continue, device still active */
, play_live /* playing right now */
};
struct out123_struct
{
enum out123_error errcode;
#ifndef NOXFERMEM
/* If buffer_pid >= 0, there is a separate buffer process actually
handling everything, this instance here is then only a proxy. */
int buffer_pid;
int buffer_fd[2];
txfermem *buffermem;
#endif
int fn; /* filenumber */
void *userptr; /* driver specific pointer */
/* Callbacks */
int (*open)(out123_handle *);
int (*get_formats)(out123_handle *);
int (*write)(out123_handle *, unsigned char *,int);
void (*flush)(out123_handle *); /* flush == drop != drain */
void (*drain)(out123_handle *);
int (*close)(out123_handle *);
int (*deinit)(out123_handle *);
/* the loaded that has set the above */
mpg123_module_t *module;
char *name; /* optional name of this instance */
char *realname; /* name possibly changed by backend */
char *driver; /* driver (module) name */
char *device; /* device name */
int flags; /* some bits; namely headphone/speaker/line */
long rate; /* sample rate */
long gain; /* output gain */
int channels; /* number of channels */
int format; /* encoding (TODO: rename this to "encoding"!) */
int framesize; /* Output needs data in chunks of framesize bytes. */
enum playstate state; /* ... */
int auxflags; /* For now just one: quiet mode (for probing). */
int propflags; /* Property flags, set by driver. */
double preload; /* buffer fraction to preload before play */
int verbose; /* verbosity to stderr */
double device_buffer; /* device buffer in seconds */
char *bindir; /* OUT123_BINDIR */
/* TODO int intflag; ... is it really useful/necessary from the outside? */
};
/* Lazy. */
#define AOQUIET ((ao->auxflags | ao->flags) & OUT123_QUIET)
#define AOVERBOSE(v) (!AOQUIET && ao->verbose >= (v))
#define GOOD_WRITEVAL(fd, val) (unintr_write(fd, &(val), sizeof((val))) == sizeof((val)))
#define GOOD_WRITEBUF(fd, addr, n) (unintr_write(fd, (addr), (n)) == (n))
#define GOOD_READVAL(fd, val) (unintr_read(fd, &(val), sizeof((val))) == sizeof((val)))
#define GOOD_READBUF(fd, addr, n) (unintr_read(fd, (addr), (n)) == (n))
struct audio_format_name {
int val;
char *name;
char *sname;
};
int write_parameters(out123_handle *ao, int fd);
int read_parameters(out123_handle *ao
, int fd, byte *prebuf, int *preoff, int presize);
#endif

View File

@@ -0,0 +1,149 @@
/*
SFIFO 1.3 Simple portable lock-free FIFO
(c) 2000-2002, David Olofson - free software under the terms of the LGPL 2.1
*/
/*
-----------------------------------------------------------
TODO:
* Is there a way to avoid losing one byte of buffer
space to avoid extra variables or locking?
* Test more compilers and environments.
-----------------------------------------------------------
*/
#include <string.h>
#include <stdlib.h>
#include "sfifo.h"
#include "debug.h"
/*
* Alloc buffer, init FIFO etc...
*/
SFIFO_SCOPE int sfifo_init(sfifo_t *f, int size)
{
memset(f, 0, sizeof(sfifo_t));
if(size > SFIFO_MAX_BUFFER_SIZE)
return -EINVAL;
/*
* Set sufficient power-of-2 size.
*
* No, there's no bug. If you need
* room for N bytes, the buffer must
* be at least N+1 bytes. (The fifo
* can't tell 'empty' from 'full'
* without unsafe index manipulations
* otherwise.)
*/
f->size = 1;
for(; f->size <= size; f->size <<= 1)
;
/* Get buffer */
if( 0 == (f->buffer = (void *)malloc(f->size)) )
return -ENOMEM;
return 0;
}
/*
* Dealloc buffer etc...
*/
SFIFO_SCOPE void sfifo_close(sfifo_t *f)
{
if(f->buffer) {
free(f->buffer);
f->buffer = NULL; /* Prevent double free */
}
}
/*
* Empty FIFO buffer
*/
SFIFO_SCOPE void sfifo_flush(sfifo_t *f)
{
debug("sfifo_flush()");
/* Reset positions */
f->readpos = 0;
f->writepos = 0;
}
/*
* Write bytes to a FIFO
* Return number of bytes written, or an error code
*/
SFIFO_SCOPE int sfifo_write(sfifo_t *f, const void *_buf, int len)
{
int total;
int i;
const char *buf = (const char *)_buf;
if(!f->buffer)
return -ENODEV; /* No buffer! */
/* total = len = min(space, len) */
total = sfifo_space(f);
debug1("sfifo_space() = %d",total);
if(len > total)
len = total;
else
total = len;
debug1("sfifo_write() = %d", total);
i = f->writepos;
if(i + len > f->size)
{
memcpy(f->buffer + i, buf, f->size - i);
buf += f->size - i;
len -= f->size - i;
i = 0;
}
memcpy(f->buffer + i, buf, len);
f->writepos = i + len;
return total;
}
/*
* Read bytes from a FIFO
* Return number of bytes read, or an error code
*/
SFIFO_SCOPE int sfifo_read(sfifo_t *f, void *_buf, int len)
{
int total;
int i;
char *buf = (char *)_buf;
if(!f->buffer)
return -ENODEV; /* No buffer! */
/* total = len = min(used, len) */
total = sfifo_used(f);
debug1("sfifo_used() = %d",total);
if(len > total)
len = total;
else
total = len;
debug1("sfifo_read() = %d", total);
i = f->readpos;
if(i + len > f->size)
{
memcpy(buf, f->buffer + i, f->size - i);
buf += f->size - i;
len -= f->size - i;
i = 0;
}
memcpy(buf, f->buffer + i, len);
f->readpos = i + len;
return total;
}

View File

@@ -0,0 +1,95 @@
/*
SFIFO 1.3 Simple portable lock-free FIFO
(c) 2000-2002, David Olofson - free software under the terms of the LGPL 2.1
*/
/*
* Platform support:
* gcc / Linux / x86: Works
* gcc / Linux / x86 kernel: Works
* gcc / FreeBSD / x86: Works
* gcc / NetBSD / x86: Works
* gcc / Mac OS X / PPC: Works
* gcc / Win32 / x86: Works
* Borland C++ / DOS / x86RM: Works
* Borland C++ / Win32 / x86PM16: Untested
* ? / Various Un*ces / ?: Untested
* ? / Mac OS / PPC: Untested
* gcc / BeOS / x86: Untested
* gcc / BeOS / PPC: Untested
* ? / ? / Alpha: Untested
*
* 1.2: Max buffer size halved, to avoid problems with
* the sign bit...
*
* 1.3: Critical buffer allocation bug fixed! For certain
* requested buffer sizes, older version would
* allocate a buffer of insufficient size, which
* would result in memory thrashing. (Amazing that
* I've manage to use this to the extent I have
* without running into this... *heh*)
*/
#ifndef _SFIFO_H_
#define _SFIFO_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <errno.h>
/* Defining SFIFO_STATIC and then including the sfifo.c will result in local code. */
#ifdef SFIFO_STATIC
#define SFIFO_SCOPE static
#else
#define SFIFO_SCOPE
#endif
/*------------------------------------------------
"Private" stuff
------------------------------------------------*/
/*
* Porting note:
* Reads and writes of a variable of this type in memory
* must be *atomic*! 'int' is *not* atomic on all platforms.
* A safe type should be used, and sfifo should limit the
* maximum buffer size accordingly.
*/
typedef int sfifo_atomic_t;
#ifdef __TURBOC__
# define SFIFO_MAX_BUFFER_SIZE 0x7fff
#else /* Kludge: Assume 32 bit platform */
# define SFIFO_MAX_BUFFER_SIZE 0x7fffffff
#endif
typedef struct sfifo_t
{
char *buffer;
int size; /* Number of bytes */
sfifo_atomic_t readpos; /* Read position */
sfifo_atomic_t writepos; /* Write position */
} sfifo_t;
#define SFIFO_SIZEMASK(x) ((x)->size - 1)
/*------------------------------------------------
API
------------------------------------------------*/
SFIFO_SCOPE int sfifo_init(sfifo_t *f, int size);
SFIFO_SCOPE void sfifo_close(sfifo_t *f);
SFIFO_SCOPE void sfifo_flush(sfifo_t *f);
SFIFO_SCOPE int sfifo_write(sfifo_t *f, const void *buf, int len);
SFIFO_SCOPE int sfifo_read(sfifo_t *f, void *buf, int len);
#define sfifo_used(x) (((x)->writepos - (x)->readpos) & SFIFO_SIZEMASK(x))
#define sfifo_space(x) ((x)->size - 1 - sfifo_used(x))
#define sfifo_size(x) ((x)->size - 1)
#ifdef __cplusplus
};
#endif
#endif

View File

@@ -0,0 +1,51 @@
/*
stringlists: creation of paired string lists for one-time consumption
copyright 2015 by the mpg123 project
free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
Thomas did not want to introduce a list type complete with management
functions just for returning driver module lists.
*/
#include "compat.h"
/* Construction helper for paired string lists.
Returns 0 on success. */
int stringlists_add( char ***alist, char ***blist
, const char *atext, const char *btext, int *count)
{
char *atextcopy = NULL;
char *btextcopy = NULL;
char **morealist = NULL;
char **moreblist = NULL;
/* If one of these succeeded, the old memory is gone, so always overwrite
the old pointer, worst case is wasted but not leaked memory in an
out-of-memory situation. */
if((morealist = safe_realloc(*alist, sizeof(char*)*(*count+1))))
*alist = morealist;
if((moreblist = safe_realloc(*blist, sizeof(char*)*(*count+1))))
*blist = moreblist;
if(!morealist || !moreblist)
return -1;
if(
(atextcopy = compat_strdup(atext))
&& (btextcopy = compat_strdup(btext))
)
{
(*alist)[*count] = atextcopy;
(*blist)[*count] = btextcopy;
++*count;
return 0;
}
else
{
free(atextcopy);
return -1;
}
}

View File

@@ -0,0 +1,16 @@
/*
stringlists: creation of paired string lists for one-time consumption
copyright 2015 by the mpg123 project
free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
*/
#ifndef MPG123_H_STRINGLISTS
#define MPG123_H_STRINGLISTS
int stringlists_add( char ***alist, char ***blist
, const char *atext, const char *btext, int *count);
#endif

View File

@@ -0,0 +1,756 @@
/*
wav.c: write wav/au/cdr files (and headerless raw
copyright ?-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Samuel Audet
Geez, why are WAV RIFF headers are so secret? I got something together,
but wow... anyway, I hope someone will find this useful.
- Samuel Audet
minor simplifications and ugly AU/CDR format stuff by MH
It's not a very clean code ... Fix this!
ThOr: The usage of stdio streams means we loose control over what data is actually written. On a full disk, fwrite() happily suceeds for ages, only a fflush fails.
Now: Do we want to fflush() after every write? That defeats the purpose of buffered I/O. So, switching to good old write() is an option (kernel doing disk buffering anyway).
ThOr: Again reworked things for libout123, with non-static state.
This set of builtin "modules" is what we can use in automated tests
of libout123. Code still not very nice, but I tried to keep modification
of what stood the test of time minimal. One still can add a module to
libout123 that uses sndfile and similar libraries for more choice on writing
output files.
*/
#include "out123_int.h"
#include "wav.h"
#include <errno.h>
#include "debug.h"
/* Create the two WAV headers. */
#define WAVE_FORMAT 1
#define RIFF_NAME riff_template
#define RIFF_STRUCT_NAME riff
#include "wavhead.h"
#undef WAVE_FORMAT
#undef RIFF_NAME
#undef RIFF_STRUCT_NAME
#define WAVE_FORMAT 3
#define RIFF_NAME riff_float_template
#define RIFF_STRUCT_NAME riff_float
#define FLOATOUT
#include "wavhead.h"
/* AU header struct... */
struct auhead {
byte magic[4];
byte headlen[4];
byte datalen[4];
byte encoding[4];
byte rate[4];
byte channels[4];
byte dummy[8];
} const auhead_template = {
{ 0x2e,0x73,0x6e,0x64 } , { 0x00,0x00,0x00,0x20 } ,
{ 0xff,0xff,0xff,0xff } , { 0,0,0,0 } , { 0,0,0,0 } , { 0,0,0,0 } ,
{ 0,0,0,0,0,0,0,0 }};
struct wavdata
{
FILE *wavfp;
long datalen;
int flipendian;
int bytes_per_sample;
int floatwav; /* If we write a floating point WAV file. */
/*
Open routines only prepare a header, stored here and written on first
actual data write. If no data is written at all, proper files will
still get a header via the update at closing; non-seekable streams will
just have no no header if there is no data.
*/
void *the_header;
size_t the_header_size;
};
static struct wavdata* wavdata_new(void)
{
struct wavdata *wdat = malloc(sizeof(struct wavdata));
if(wdat)
{
wdat->wavfp = NULL;
wdat->datalen = 0;
wdat->flipendian = 0;
wdat->bytes_per_sample = -1;
wdat->floatwav = 0;
wdat->the_header = NULL;
wdat->the_header_size = 0;
}
return wdat;
}
static void wavdata_del(struct wavdata *wdat)
{
if(!wdat) return;
if(wdat->wavfp && wdat->wavfp != stdout)
compat_fclose(wdat->wavfp);
if(wdat->the_header)
free(wdat->the_header);
free(wdat);
}
/* Pointer types are for pussies;-) */
static void* wavhead_new(void const *template, size_t size)
{
void *header = malloc(size);
if(header)
memcpy(header, template, size);
return header;
}
/* Convertfunctions: */
/* always little endian */
static void long2littleendian(long inval,byte *outval,int b)
{
int i;
for(i=0;i<b;i++) {
outval[i] = (inval>>(i*8)) & 0xff;
}
}
/* always big endian */
static void long2bigendian(long inval,byte *outval,int b)
{
int i;
for(i=0;i<b;i++) {
outval[i] = (inval>>((b-i-1)*8)) & 0xff;
}
}
static long from_little(byte *inval, int b)
{
long ret = 0;
int i;
for(i=0;i<b;++i) ret += ((long)inval[i])<<(i*8);
return ret;
}
static int testEndian(void)
{
long i,a=0,b=0,c=0;
int ret = 0;
for(i=0;i<sizeof(long);i++) {
((byte *)&a)[i] = i;
b<<=8;
b |= i;
c |= i << (i*8);
}
if(a == b)
ret = 1;
else if(a != c) {
ret = -1;
}
return ret;
}
/* return: 0 is good, -1 is bad */
static int open_file(struct wavdata *wdat, char *filename)
{
debug2("open_file(%p, %s)", (void*)wdat, filename ? filename : "<nil>");
if(!wdat)
return -1;
#if defined(HAVE_SETUID) && defined(HAVE_GETUID)
/* TODO: get rid of that and settle that you rather not install mpg123
setuid-root. Why should you?
In case this program is setuid, create files owned by original user. */
setuid(getuid());
#endif
if(!filename || !strcmp("-",filename) || !strcmp("", filename))
{
wdat->wavfp = stdout;
#ifdef WIN32
_setmode(STDOUT_FILENO, _O_BINARY);
#endif
/* If stdout is redirected to a file, seeks suddenly can work.
Doing one here to ensure that such a file has the same output
it had when opening directly as such. */
fseek(wdat->wavfp, 0L, SEEK_SET);
return 0;
}
else
{
wdat->wavfp = compat_fopen(filename, "wb");
if(!wdat->wavfp)
return -1;
else
return 0;
}
}
/* return: 0 is good, -1 is bad
Works for any partial state of setup, especially should not complain if
ao->userptr == NULL. */
static int close_file(out123_handle *ao)
{
struct wavdata *wdat = ao->userptr;
int ret = 0;
if(wdat->wavfp != NULL && wdat->wavfp != stdout)
{
if(compat_fclose(wdat->wavfp))
{
if(!AOQUIET)
error1("problem closing the audio file, probably because of flushing to disk: %s\n", strerror(errno));
ret = -1;
}
}
/* Always cleanup here. */
wdat->wavfp = NULL;
wavdata_del(wdat);
ao->userptr = NULL;
return ret;
}
/* return: 0 is good, -1 is bad */
static int write_header(out123_handle *ao)
{
struct wavdata *wdat = ao->userptr;
if(!wdat)
return 0;
if(
wdat->the_header_size > 0
&& (
fwrite(wdat->the_header, wdat->the_header_size, 1, wdat->wavfp) != 1
|| fflush(wdat->wavfp)
)
)
{
if(!AOQUIET)
error1("cannot write header: %s", strerror(errno));
return -1;
}
else return 0;
}
int au_open(out123_handle *ao)
{
struct wavdata *wdat = NULL;
struct auhead *auhead = NULL;
if(ao->format < 0)
{
ao->rate = 44100;
ao->channels = 2;
ao->format = MPG123_ENC_SIGNED_16;
return 0;
}
if(ao->format & MPG123_ENC_FLOAT)
{
if(!AOQUIET)
error("AU file support for float values not there yet");
goto au_open_bad;
}
if(
!(wdat = wavdata_new())
|| !(auhead = wavhead_new(&auhead_template, sizeof(auhead_template)))
)
{
ao->errcode = OUT123_DOOM;
goto au_open_bad;
}
wdat->the_header = auhead;
wdat->the_header_size = sizeof(*auhead);
wdat->flipendian = 0;
switch(ao->format)
{
case MPG123_ENC_SIGNED_16:
{
int endiantest = testEndian();
if(endiantest == -1)
goto au_open_bad;
wdat->flipendian = !endiantest; /* big end */
long2bigendian(3,auhead->encoding,sizeof(auhead->encoding));
}
break;
case MPG123_ENC_UNSIGNED_8:
ao->format = MPG123_ENC_ULAW_8;
case MPG123_ENC_ULAW_8:
long2bigendian(1,auhead->encoding,sizeof(auhead->encoding));
break;
default:
if(!AOQUIET)
error("AU output is only a hack. This audio mode isn't supported yet.");
goto au_open_bad;
}
long2bigendian(0xffffffff,auhead->datalen,sizeof(auhead->datalen));
long2bigendian(ao->rate,auhead->rate,sizeof(auhead->rate));
long2bigendian(ao->channels,auhead->channels,sizeof(auhead->channels));
if(open_file(wdat, ao->device) < 0)
goto au_open_bad;
wdat->datalen = 0;
ao->userptr = wdat;
return 0;
au_open_bad:
if(auhead)
free(auhead);
if(wdat)
{
wdat->the_header = NULL;
wavdata_del(wdat);
}
return -1;
}
int cdr_open(out123_handle *ao)
{
struct wavdata *wdat = NULL;
if(ao->format < 0)
{
ao->rate = 44100;
ao->channels = 2;
ao->format = MPG123_ENC_SIGNED_16;
return 0;
}
if(
ao->format != MPG123_ENC_SIGNED_16
|| ao->rate != 44100
|| ao->channels != 2
)
{
if(!AOQUIET)
error("Oops .. not forced to 16 bit, 44 kHz, stereo?");
goto cdr_open_bad;
}
if(!(wdat = wavdata_new()))
{
ao->errcode = OUT123_DOOM;
goto cdr_open_bad;
}
wdat->flipendian = !testEndian(); /* big end */
if(open_file(wdat, ao->device) < 0)
{
if(!AOQUIET)
error("cannot open file for writing");
goto cdr_open_bad;
}
ao->userptr = wdat;
return 0;
cdr_open_bad:
if(wdat)
wavdata_del(wdat);
return -1;
}
/* RAW files are headerless WAVs where the format does not matter. */
int raw_open(out123_handle *ao)
{
struct wavdata *wdat;
if(ao->format < 0)
{
ao->rate = 44100;
ao->channels = 2;
ao->format = MPG123_ENC_SIGNED_16;
return 0;
}
if(!(wdat = wavdata_new()))
{
ao->errcode = OUT123_DOOM;
goto raw_open_bad;
}
if(open_file(wdat, ao->device) < 0)
goto raw_open_bad;
ao->userptr = wdat;
return 1;
raw_open_bad:
if(wdat)
wavdata_del(wdat);
return -1;
}
int wav_open(out123_handle *ao)
{
int bps;
struct wavdata *wdat = NULL;
struct riff *inthead = NULL;
struct riff_float *floathead = NULL;
if(ao->format < 0)
{
ao->rate = 44100;
ao->channels = 2;
ao->format = MPG123_ENC_SIGNED_16;
return 0;
}
if(!(wdat = wavdata_new()))
{
ao->errcode = OUT123_DOOM;
goto wav_open_bad;
}
wdat->floatwav = (ao->format & MPG123_ENC_FLOAT);
if(wdat->floatwav)
{
if(!(floathead = wavhead_new( &riff_float_template
, sizeof(riff_float_template)) ))
{
ao->errcode = OUT123_DOOM;
goto wav_open_bad;
}
wdat->the_header = floathead;
wdat->the_header_size = sizeof(*floathead);
}
else
{
if(!(inthead = wavhead_new( &riff_template
, sizeof(riff_template)) ))
{
ao->errcode = OUT123_DOOM;
goto wav_open_bad;
}
wdat->the_header = inthead;
wdat->the_header_size = sizeof(*inthead);
/* standard MS PCM, and its format specific is BitsPerSample */
long2littleendian(1, inthead->WAVE.fmt.FormatTag
, sizeof(inthead->WAVE.fmt.FormatTag));
}
if(ao->format == MPG123_ENC_FLOAT_32)
{
long2littleendian(3, floathead->WAVE.fmt.FormatTag
, sizeof(floathead->WAVE.fmt.FormatTag));
long2littleendian(bps=32, floathead->WAVE.fmt.BitsPerSample
, sizeof(floathead->WAVE.fmt.BitsPerSample));
wdat->flipendian = testEndian();
}
else if(ao->format == MPG123_ENC_SIGNED_32)
{
long2littleendian(bps=32, inthead->WAVE.fmt.BitsPerSample
, sizeof(inthead->WAVE.fmt.BitsPerSample));
wdat->flipendian = testEndian();
}
else if(ao->format == MPG123_ENC_SIGNED_24)
{
long2littleendian(bps=24, inthead->WAVE.fmt.BitsPerSample
, sizeof(inthead->WAVE.fmt.BitsPerSample));
wdat->flipendian = testEndian();
}
else if(ao->format == MPG123_ENC_SIGNED_16)
{
long2littleendian(bps=16, inthead->WAVE.fmt.BitsPerSample
, sizeof(inthead->WAVE.fmt.BitsPerSample));
wdat->flipendian = testEndian();
}
else if(ao->format == MPG123_ENC_UNSIGNED_8)
long2littleendian(bps=8, inthead->WAVE.fmt.BitsPerSample
, sizeof(inthead->WAVE.fmt.BitsPerSample));
else
{
if(!AOQUIET)
error("Format not supported.");
goto wav_open_bad;
}
if(wdat->floatwav)
{
long2littleendian(ao->channels, floathead->WAVE.fmt.Channels
, sizeof(floathead->WAVE.fmt.Channels));
long2littleendian(ao->rate, floathead->WAVE.fmt.SamplesPerSec
, sizeof(floathead->WAVE.fmt.SamplesPerSec));
long2littleendian( (int)(ao->channels * ao->rate * bps)>>3
, floathead->WAVE.fmt.AvgBytesPerSec
, sizeof(floathead->WAVE.fmt.AvgBytesPerSec) );
long2littleendian( (int)(ao->channels * bps)>>3
, floathead->WAVE.fmt.BlockAlign
, sizeof(floathead->WAVE.fmt.BlockAlign) );
}
else
{
long2littleendian(ao->channels, inthead->WAVE.fmt.Channels
, sizeof(inthead->WAVE.fmt.Channels));
long2littleendian(ao->rate, inthead->WAVE.fmt.SamplesPerSec
, sizeof(inthead->WAVE.fmt.SamplesPerSec));
long2littleendian( (int)(ao->channels * ao->rate * bps)>>3
, inthead->WAVE.fmt.AvgBytesPerSec
,sizeof(inthead->WAVE.fmt.AvgBytesPerSec) );
long2littleendian( (int)(ao->channels * bps)>>3
, inthead->WAVE.fmt.BlockAlign
, sizeof(inthead->WAVE.fmt.BlockAlign) );
}
if(open_file(wdat, ao->device) < 0)
goto wav_open_bad;
if(wdat->floatwav)
{
long2littleendian(wdat->datalen, floathead->WAVE.data.datalen
, sizeof(floathead->WAVE.data.datalen));
long2littleendian(wdat->datalen+sizeof(floathead->WAVE)
, floathead->WAVElen, sizeof(floathead->WAVElen));
}
else
{
long2littleendian(wdat->datalen, inthead->WAVE.data.datalen
, sizeof(inthead->WAVE.data.datalen));
long2littleendian( wdat->datalen+sizeof(inthead->WAVE)
, inthead->WAVElen
, sizeof(inthead->WAVElen) );
}
wdat->bytes_per_sample = bps>>3;
ao->userptr = wdat;
return 0;
wav_open_bad:
if(inthead)
free(inthead);
if(floathead)
free(floathead);
if(wdat)
{
wdat->the_header = NULL;
wavdata_del(wdat);
}
return -1;
}
int wav_write(out123_handle *ao, unsigned char *buf, int len)
{
struct wavdata *wdat = ao->userptr;
int temp;
int i;
if(!wdat || !wdat->wavfp)
return 0; /* Really? Zero? */
if(wdat->datalen == 0 && write_header(ao) < 0)
return -1;
/* Endianess conversion. Not fancy / optimized. */
if(wdat->flipendian)
{
if(wdat->bytes_per_sample == 4) /* 32 bit */
{
if(len & 3)
{
if(!AOQUIET)
error("Number of bytes no multiple of 4 (32bit)!");
return -1;
}
for(i=0;i<len;i+=4)
{
int j;
unsigned char tmp[4];
for(j = 0; j<=3; ++j) tmp[j] = buf[i+j];
for(j = 0; j<=3; ++j) buf[i+j] = tmp[3-j];
}
}
else /* 16 bit */
{
if(len & 1)
{
error("Odd number of bytes!");
return -1;
}
for(i=0;i<len;i+=2)
{
unsigned char tmp;
tmp = buf[i+0];
buf[i+0] = buf[i+1];
buf[i+1] = tmp;
}
}
}
temp = fwrite(buf, 1, len, wdat->wavfp);
if(temp <= 0) return temp;
/* That would kill it of early when running out of disk space. */
#if 0
if(fflush(wdat->wavfp))
{
if(!AOQUIET)
error1("flushing failed: %s\n", strerror(errno));
return -1;
}
#endif
wdat->datalen += temp;
return temp;
}
int wav_close(out123_handle *ao)
{
struct wavdata *wdat = ao->userptr;
if(!wdat) /* Special case: Opened only for format query. */
return 0;
if(!wdat || !wdat->wavfp)
return -1;
/* flush before seeking to catch out-of-disk explicitly at least at the end */
if(fflush(wdat->wavfp))
{
if(!AOQUIET)
error1("cannot flush WAV stream: %s", strerror(errno));
return close_file(ao);
}
if(fseek(wdat->wavfp, 0L, SEEK_SET) >= 0)
{
if(wdat->floatwav)
{
struct riff_float *floathead = wdat->the_header;
long2littleendian(wdat->datalen
, floathead->WAVE.data.datalen
, sizeof(floathead->WAVE.data.datalen));
long2littleendian(wdat->datalen+sizeof(floathead->WAVE)
, floathead->WAVElen
, sizeof(floathead->WAVElen));
long2littleendian( wdat->datalen
/ (
from_little(floathead->WAVE.fmt.Channels,2)
* from_little(floathead->WAVE.fmt.BitsPerSample,2)/8
)
, floathead->WAVE.fact.samplelen
, sizeof(floathead->WAVE.fact.samplelen) );
}
else
{
struct riff *inthead = wdat->the_header;
long2littleendian(wdat->datalen, inthead->WAVE.data.datalen
, sizeof(inthead->WAVE.data.datalen));
long2littleendian(wdat->datalen+sizeof(inthead->WAVE), inthead->WAVElen
, sizeof(inthead->WAVElen));
}
/* Always (over)writing the header here; also for stdout, when
fseek worked, this overwrite works. */
write_header(ao);
}
else if(!AOQUIET)
warning("Cannot rewind WAV file. File-format isn't fully conform now.");
return close_file(ao);
}
int au_close(out123_handle *ao)
{
struct wavdata *wdat = ao->userptr;
if(!wdat) /* Special case: Opened only for format query. */
return 0;
if(!wdat->wavfp)
return -1;
/* flush before seeking to catch out-of-disk explicitly at least at the end */
if(fflush(wdat->wavfp))
{
if(!AOQUIET)
error1("cannot flush WAV stream: %s", strerror(errno));
return close_file(ao);
}
if(fseek(wdat->wavfp, 0L, SEEK_SET) >= 0)
{
struct auhead *auhead = wdat->the_header;
long2bigendian(wdat->datalen, auhead->datalen, sizeof(auhead->datalen));
/* Always (over)writing the header here; also for stdout, when
fseek worked, this overwrite works. */
write_header(ao);
}
else if(!AOQUIET)
warning("Cannot rewind AU file. File-format isn't fully conform now.");
return close_file(ao);
}
/* CDR data also uses that. */
int raw_close(out123_handle *ao)
{
struct wavdata *wdat = ao->userptr;
if(!wdat) /* Special case: Opened only for format query. */
return 0;
if(!wdat->wavfp)
return -1;
return close_file(ao);
}
/* Some trivial functions to interface with out123's module architecture. */
int cdr_formats(out123_handle *ao)
{
if(ao->rate == 44100 && ao->channels == 2)
return MPG123_ENC_SIGNED_16;
else
return 0;
}
int au_formats(out123_handle *ao)
{
return MPG123_ENC_SIGNED_16|MPG123_ENC_UNSIGNED_8|MPG123_ENC_ULAW_8;
}
int raw_formats(out123_handle *ao)
{
return MPG123_ENC_ANY;
}
int wav_formats(out123_handle *ao)
{
return
MPG123_ENC_SIGNED_16
| MPG123_ENC_UNSIGNED_8
| MPG123_ENC_FLOAT_32
| MPG123_ENC_SIGNED_24
| MPG123_ENC_SIGNED_32;
}
/* Draining is flushing to disk. Words do suck at times.
One could call fsync(), too, but to be safe, that would need to
be called on the directory, too. Also, apps randomly calling
fsync() can cause annoying issues in a system. */
void wav_drain(out123_handle *ao)
{
struct wavdata *wdat = ao->userptr;
if(!wdat)
return;
if(fflush(wdat->wavfp) && !AOQUIET)
error1("flushing failed: %s\n", strerror(errno));
}

View File

@@ -0,0 +1,33 @@
/*
wav.c: write wav/au/cdr files (and headerless raw)
copyright ?-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially extracted of out123_int.h, formerly audio.h, by Thomas Orgis
*/
#ifndef _MPG123_WAV_H_
#define _MPG123_WAV_H_
/* Could get away without any header, as only pointers declared. */
#include "out123.h"
/* Interfaces from wav.c, variants of file writing, to be combined into
fake modules by the main library code. */
int au_open(out123_handle *);
int cdr_open(out123_handle *);
int raw_open(out123_handle *);
int wav_open(out123_handle *);
int wav_write(out123_handle *, unsigned char *buf, int len);
int wav_close(out123_handle *);
int au_close(out123_handle *);
int raw_close(out123_handle *);
int cdr_formats(out123_handle *);
int au_formats(out123_handle *);
int raw_formats(out123_handle *);
int wav_formats(out123_handle *);
void wav_drain(out123_handle *);
#endif

View File

@@ -0,0 +1,68 @@
/*
wavhead.h: wav file header, to be included twice for integer and float wavs
copyright ?-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Samuel Audet
*/
struct RIFF_STRUCT_NAME
{
byte riffheader[4];
byte WAVElen[4]; /* should this include riffheader or not? */
struct
{
byte WAVEID[4];
byte fmtheader[4];
byte fmtlen[4];
struct
{
byte FormatTag[2];
byte Channels[2];
byte SamplesPerSec[4];
byte AvgBytesPerSec[4];
byte BlockAlign[2];
byte BitsPerSample[2]; /* format specific for PCM */
#ifdef FLOATOUT
byte cbSize[2];
#endif
} fmt;
#ifdef FLOATOUT
byte factheader[4];
byte factlen[4];
struct
{
byte samplelen[4];
} fact;
#endif
struct
{
byte dataheader[4];
byte datalen[4];
/* from here you insert your PCM data */
} data;
} WAVE;
} const RIFF_NAME =
{
{ 'R','I','F','F' } ,
{ sizeof(RIFF_NAME.WAVE),0,0,0 } ,
{
{ 'W','A','V','E' },
{ 'f','m','t',' ' },
{ sizeof(RIFF_NAME.WAVE.fmt),0,0,0 } ,
{
{WAVE_FORMAT,0} , {0,0},{0,0,0,0},{0,0,0,0},{0,0},{0,0}
#ifdef FLOATOUT
,{0,0}
#endif
} ,
#ifdef FLOATOUT
{ 'f','a','c','t' },
{ sizeof(RIFF_NAME.WAVE.fact),0,0,0 },
{
{0,0,0,0} /* to be filled later, like datalen and wavelen */
},
#endif
{ { 'd','a','t','a' } , {0,0,0,0} }
}
};

View File

@@ -0,0 +1,304 @@
/*
xfermem: unidirectional fast pipe
copyright ?-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Oliver Fromme
old timestamp: Sun Apr 6 02:26:26 MET DST 1997
See xfermem.h for documentation/description.
*/
#include "config.h"
#include "compat.h"
#include "xfermem.h"
#include <string.h>
#include <errno.h>
#include <sys/uio.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <fcntl.h>
#ifndef HAVE_MMAP
#include <sys/ipc.h>
#include <sys/shm.h>
#endif
#include "debug.h"
#if defined (HAVE_MMAP) && defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
#define MAP_ANON MAP_ANONYMOUS
#endif
void xfermem_init (txfermem **xf, size_t bufsize, size_t msize, size_t skipbuf)
{
size_t regsize = bufsize + msize + skipbuf + sizeof(txfermem);
#ifdef HAVE_MMAP
# ifdef MAP_ANON
if ((*xf = (txfermem *) mmap(0, regsize, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_SHARED, -1, 0)) == (txfermem *) -1) {
perror ("mmap()");
exit (1);
}
# else
int devzero;
if ((devzero = open("/dev/zero", O_RDWR, 0)) == -1) {
perror ("open(/dev/zero)");
exit (1);
}
if ((*xf = (txfermem *) mmap(0, regsize, PROT_READ | PROT_WRITE,
MAP_SHARED, devzero, 0)) == (txfermem *) -1) {
perror ("mmap()");
exit (1);
}
close (devzero);
# endif
#else
struct shmid_ds shmemds;
int shmemid;
if ((shmemid = shmget(IPC_PRIVATE, regsize, IPC_CREAT | 0600)) == -1) {
perror ("shmget()");
exit (1);
}
if ((*xf = (txfermem *) shmat(shmemid, 0, 0)) == (txfermem *) -1) {
perror ("shmat()");
shmctl (shmemid, IPC_RMID, &shmemds);
exit (1);
}
if (shmctl(shmemid, IPC_RMID, &shmemds) == -1) {
perror ("shmctl()");
xfermem_done (*xf);
exit (1);
}
#endif
if (socketpair(AF_UNIX, SOCK_STREAM, 0, (*xf)->fd) < 0) {
perror ("socketpair()");
xfermem_done (*xf);
exit (1);
}
(*xf)->freeindex = (*xf)->readindex = 0;
(*xf)->data = ((char *) *xf) + sizeof(txfermem) + msize;
(*xf)->metadata = ((char *) *xf) + sizeof(txfermem);
(*xf)->size = bufsize;
(*xf)->metasize = msize + skipbuf;
}
void xfermem_done (txfermem *xf)
{
if(!xf)
return;
#ifdef HAVE_MMAP
/* Here was a cast to (caddr_t) ... why? Was this needed for SunOS?
Casting to (void*) should silence compilers in case of funny
prototype for munmap(). */
munmap ( (void*)xf, xf->size + xf->metasize + sizeof(txfermem));
#else
if (shmdt((void *) xf) == -1) {
perror ("shmdt()");
exit (1);
}
#endif
}
void xfermem_init_writer (txfermem *xf)
{
if(xf)
close (xf->fd[XF_READER]);
debug1("xfermem writer fd=%i", xf->fd[XF_WRITER]);
}
void xfermem_init_reader (txfermem *xf)
{
if(xf)
close (xf->fd[XF_WRITER]);
debug1("xfermem reader fd=%i", xf->fd[XF_READER]);
}
size_t xfermem_get_freespace (txfermem *xf)
{
size_t freeindex, readindex;
if(!xf)
return 0;
if ((freeindex = xf->freeindex) < 0
|| (readindex = xf->readindex) < 0)
return (0);
if (readindex > freeindex)
return ((readindex - freeindex) - 1);
else
return ((xf->size - (freeindex - readindex)) - 1);
}
size_t xfermem_get_usedspace (txfermem *xf)
{
size_t freeindex, readindex;
if(!xf)
return 0;
if ((freeindex = xf->freeindex) < 0
|| (readindex = xf->readindex) < 0)
return (0);
if (freeindex >= readindex)
return (freeindex - readindex);
else
return (xf->size - (readindex - freeindex));
}
static int xfermem_getcmd_raw (int fd, int block, byte *cmds, int count)
{
fd_set selfds;
int ret;
for (;;) {
struct timeval selto = {0, 0};
FD_ZERO (&selfds);
FD_SET (fd, &selfds);
#ifdef HPUX
switch (select(FD_SETSIZE, (int *) &selfds, NULL, NULL, block ? NULL : &selto))
#else
switch (select(FD_SETSIZE, &selfds, NULL, NULL, block ? NULL : &selto))
#endif
{
case 0:
if (!block)
return (0);
continue;
case -1:
if (errno == EINTR)
continue;
return (-2);
case 1:
if (FD_ISSET(fd, &selfds))
switch((ret=read(fd, cmds, count)))
{
case 0: /* EOF */
return (-1);
case -1:
if (errno == EINTR)
continue;
return (-3);
default:
return ret;
}
else /* ?!? */
return (-5);
default: /* ?!? */
return (-6);
}
}
}
/* Verbose variant for debugging communication. */
int xfermem_getcmd(int fd, int block)
{
byte cmd;
int res = xfermem_getcmd_raw(fd, block, &cmd, 1);
debug3("xfermem_getcmd(%i, %i) = %i", fd, block, res == 1 ? cmd : res);
return res == 1 ? cmd : res;
}
int xfermem_getcmds(int fd, int block, byte *cmds, int count)
{
int res = xfermem_getcmd_raw(fd, block, cmds, count);
debug5("xfermem_getcmds(%i, %i, %p, %i) = %i"
, fd, block, (void*)cmds, count
, res);
return res;
}
int xfermem_putcmd (int fd, byte cmd)
{
for (;;) {
switch (write(fd, &cmd, 1)) {
case 1:
debug2("xfermem_putcmd(%i, %i) = 1", fd, cmd);
return (1);
case -1:
if (errno != EINTR)
{
debug3("xfermem_putcmd(%i, %i) = -1 (%s)"
, fd, cmd, strerror(errno));
return (-1);
}
}
}
}
/*
There is a basic assumetry between reader and writer:
The reader does work in periodic pieces and can be relied upon to
eventually answer a call. It is important that it does not block
for a significant duration unless it has really nothing to do.
The writer is more undefined in its behaviour, it is controlled by
external agents. You cannot rely on it answering synchronization
requests in a timely manner. But on the other hand, it can be left
hanging for a while. The critical side is that of the reader.
Because of that, it is only sensible to provide a voluntary
xfermem_writer_block() here. The reader does not need such a function.
Only if it has nothing else to do, it will simply block on
xfermem_getcmd(), and the writer promises to xfermem_putcmd() when
something happens.
The writer always sends a wakeup command to the reader since the latter
could be in the process of putting itself to sleep right now, without
a flag indicating so being set yet.
The reader periodically reads from its file descriptor so that it does
not get clogged up with pending messages. It will only (and always) send
a wakeup call in response to a received command.
*/
/* Wait a bit to get a sign of life from the reader.
Returns -1 if even that did not work. */
int xfermem_writer_block(txfermem *xf)
{
int myfd = xf->fd[XF_WRITER];
int result;
xfermem_putcmd(myfd, XF_CMD_PING);
result = xfermem_getcmd(myfd, TRUE);
/* Only a pong to my ping is the expected good answer.
Everything else is a problem to be communicated. */
return (result == XF_CMD_PONG) ? 0 : result;
}
/* Return: 0 on success, -1 on communication error, > 0 for
error on buffer side, some special return code from buffer to be
evaluated. */
int xfermem_write(txfermem *xf, void *buffer, size_t bytes)
{
if(buffer == NULL || bytes < 1) return 0;
/* You weren't so braindead not allocating enough space at all, right? */
while (xfermem_get_freespace(xf) < bytes)
{
int cmd = xfermem_writer_block(xf);
if(cmd) /* Non-successful wait. */
return cmd;
}
/* Now we have enough space. copy the memory, possibly with the wrap. */
if(xf->size - xf->freeindex >= bytes)
{ /* one block of free memory */
memcpy(xf->data+xf->freeindex, buffer, bytes);
}
else
{ /* two blocks */
size_t endblock = xf->size - xf->freeindex;
memcpy(xf->data+xf->freeindex, buffer, endblock);
memcpy(xf->data, (char*)buffer + endblock, bytes-endblock);
}
/* Advance the free space pointer, including the wrap. */
xf->freeindex = (xf->freeindex + bytes) % xf->size;
/* Always notify the buffer process. */
debug("write waking");
return xfermem_putcmd(xf->fd[XF_WRITER], XF_CMD_DATA) < 0
? -1
: 0;
}

View File

@@ -0,0 +1,86 @@
/*
xfermem: unidirectional fast pipe
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Oliver Fromme
old timestamp: Sat Mar 29 04:41:34 MET 1997
This is a stand-alone module which implements a unidirectional,
fast pipe using mmap(). Its primary use is to transfer large
amounts of data from a parent process to its child process,
with a buffer in between which decouples blocking conditions
on both sides. Control information is transferred between the
processes through a socketpair. See xftest.c for an example on
how to use this module.
note: xftest not there anymore
*/
#ifndef _XFERMEM_H_
#define _XFERMEM_H_
#include "compat.h"
typedef struct {
size_t freeindex; /* [W] next free index */
size_t readindex; /* [R] next index to read */
int fd[2];
char *data;
char *metadata;
size_t size;
size_t metasize;
} txfermem;
/*
* [W] -- May be written to by the writing process only!
* [R] -- May be written to by the reading process only!
* All other entries are initialized once.
*/
void xfermem_init (txfermem **xf, size_t bufsize, size_t msize, size_t skipbuf);
void xfermem_init_writer (txfermem *xf);
void xfermem_init_reader (txfermem *xf);
size_t xfermem_get_freespace (txfermem *xf);
size_t xfermem_get_usedspace (txfermem *xf);
/* Unless otherwise noted, each command demands a reponse if issued from the
writer. The reader does not expect responses, only orders. */
enum xf_cmd_code
{
XF_CMD_PING = 1 /**< Wake up and give a response, not changing any state. */
, XF_CMD_PONG /**< The response to a ping. */
, XF_CMD_DATA /**< Re-check the amount of data available without response. */
, XF_CMD_TERMINATE /**< Stop operation. */
, XF_CMD_DROP /**< Drop current buffer contents. */
, XF_CMD_DRAIN /**< Consume current buffer contents now. */
, XF_CMD_PAUSE /**< Pause operation, wait for next command. */
, XF_CMD_CONTINUE /**< Continue operation. */
, XF_CMD_IGNLOW /**< Ignore situation with low buffer fill. */
, XF_CMD_OK /**< Response from reader: Operation succeeded. */
, XF_CMD_ERROR /**< Response from reader: Operation failed. */
, XF_CMD_CUSTOM1 /**< Some custom command to be filled with meaning. */
, XF_CMD_CUSTOM2 /**< Some custom command to be filled with meaning. */
, XF_CMD_CUSTOM3 /**< Some custom command to be filled with meaning. */
, XF_CMD_CUSTOM4 /**< Some custom command to be filled with meaning. */
, XF_CMD_CUSTOM5 /**< Some custom command to be filled with meaning. */
, XF_CMD_CUSTOM6 /**< Some custom command to be filled with meaning. */
, XF_CMD_CUSTOM7 /**< Some custom command to be filled with meaning. */
, XF_CMD_CUSTOM8 /**< Some custom command to be filled with meaning. */
};
#define XF_WRITER 0
#define XF_READER 1
int xfermem_getcmd(int fd, int block);
int xfermem_getcmds(int fd, int block, byte* cmds, int count);
int xfermem_putcmd(int fd, byte cmd);
int xfermem_writer_block(txfermem *xf);
/* returns TRUE for being interrupted */
int xfermem_write(txfermem *xf, void *buffer, size_t bytes);
void xfermem_done (txfermem *xf);
#define xfermem_done_writer xfermem_init_reader
#define xfermem_done_reader xfermem_init_writer
#endif