mirror of
http://mpg123.de/trunk/.git
synced 2025-08-07 21:02:55 +03:00
merge in libout123 branch
git-svn-id: svn://scm.orgis.org/mpg123/trunk@3770 35dc7657-300d-0410-a2e5-dc2837fedb53
This commit is contained in:
42
Makefile.am
42
Makefile.am
@@ -3,18 +3,48 @@
|
||||
## copyright 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
|
||||
dist_man_MANS = man1/mpg123.1 man1/out123.1
|
||||
## reworked for non-recursive make by Thomas Orgis
|
||||
|
||||
SUBDIRS = src doc
|
||||
DIST_SUBDIRS = src doc
|
||||
# Mention all global variables first before including Make modules.
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
bin_PROGRAMS =
|
||||
EXTRA_PROGRAMS =
|
||||
LIBS =
|
||||
EXTRA_DIST =
|
||||
pkglib_LTLIBRARIES =
|
||||
lib_LTLIBRARIES =
|
||||
noinst_LIBRARIES =
|
||||
noinst_LTLIBRARIES =
|
||||
nodist_include_HEADERS =
|
||||
dist_man_MANS =
|
||||
CLEANFILES =
|
||||
|
||||
AM_CPPFLAGS = -DPKGLIBDIR="\"$(pkglibdir)\""
|
||||
# That can be trimmed down later when adapting the sources to
|
||||
# use relative paths for includes.
|
||||
# Watch out for generated headers (that's why top_builddir is also present).
|
||||
AM_CPPFLAGS += \
|
||||
$(LTDLINCL) \
|
||||
-I$(top_srcdir)/src \
|
||||
-I$(top_srcdir)/src/compat \
|
||||
-I$(top_srcdir)/src/libmpg123 \
|
||||
-I$(top_srcdir)/src/libout123 \
|
||||
-I$(top_builddir)/src/libmpg123
|
||||
|
||||
# pkg-config file for the mpg123 library
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libmpg123.pc
|
||||
pkgconfig_DATA =
|
||||
|
||||
# Include Make modules from subdirectories.
|
||||
include src/Makemodule.am
|
||||
include doc/Makemodule.am
|
||||
|
||||
# Stuff from this directory.
|
||||
pkgconfig_DATA += libmpg123.pc
|
||||
|
||||
dist_man_MANS += man1/mpg123.1 man1/out123.1
|
||||
|
||||
# mpg123.spec is autogenerated but needs to be present in tarball!
|
||||
EXTRA_DIST = \
|
||||
EXTRA_DIST += \
|
||||
mpg123.spec \
|
||||
makedll.sh \
|
||||
windows-builds.sh \
|
||||
|
17
NEWS
17
NEWS
@@ -1,3 +1,13 @@
|
||||
- Activate terminal control automatically if run in an interactive terminal.
|
||||
Provide switch to _deactivate_ terminal control.
|
||||
|
||||
- reverse order of arguments for out123_start() again, matching mpg123_getformat()
|
||||
|
||||
- fancy: do progress bar on terminal by reversing text colors on status line
|
||||
|
||||
- anticipate messages from libmpg123 with param.verbose>1 in term.c and print("\n")
|
||||
on key presses that _will_ result in "Note: " printouts
|
||||
|
||||
1.23.0
|
||||
---
|
||||
- Added mpg123 --no-infoframe.
|
||||
@@ -8,6 +18,13 @@
|
||||
- Warning messages also start with a line break now to better fit in with
|
||||
verbose playback.
|
||||
- Reporting of clipped samples also includes a line break now.
|
||||
- Default for --preload now is 0.2 instead of 1 (fill whole buffer before
|
||||
playback). The maximum is 0.5 . This is mandated by corrected buffer
|
||||
interaction in libout123.
|
||||
- Improved interaction with buffer process in terminal control mode.
|
||||
Seeking is more transparent now, taking dropped samples from buffer into
|
||||
account to avoid unintended jumps. Direct seeks with number row do not
|
||||
pause playback anymore.
|
||||
- Silently skip APE tags (thanks to Hans de Goede).
|
||||
- Some reduction in bitrot on AIX (typos in output module, build with
|
||||
--disable-largefile --with-audio=aix, real test welcome).
|
||||
|
224
configure.ac
224
configure.ac
@@ -10,12 +10,23 @@ AC_PREREQ(2.57)
|
||||
dnl ############# Initialisation
|
||||
AC_INIT([mpg123], [1.23.0], [mpg123-devel@lists.sourceforge.net])
|
||||
dnl Increment API_VERSION when the API gets changes (new functions).
|
||||
|
||||
dnl libmpg123
|
||||
API_VERSION=41
|
||||
LIB_PATCHLEVEL=4
|
||||
|
||||
dnl libout123
|
||||
OUTAPI_VERSION=1
|
||||
OUTLIB_PATCHLEVEL=0
|
||||
|
||||
dnl Since we want to be backwards compatible, both sides get set to API_VERSION.
|
||||
LIBMPG123_VERSION=$API_VERSION:$LIB_PATCHLEVEL:$API_VERSION
|
||||
LIBOUT123_VERSION=$OUTAPI_VERSION:$OUTLIB_PATCHLEVEL:$OUTAPI_VERSION
|
||||
AC_SUBST(LIBMPG123_VERSION)
|
||||
AC_SUBST(API_VERSION)
|
||||
AC_SUBST(LIBOUT123_VERSION)
|
||||
AC_SUBST(OUTAPI_VERSION)
|
||||
|
||||
|
||||
AC_CONFIG_SRCDIR(src/mpg123.c)
|
||||
AC_CONFIG_AUX_DIR(build)
|
||||
@@ -24,7 +35,9 @@ AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
dnl Version 1.7 of automake is recommended
|
||||
AM_INIT_AUTOMAKE(1.7)
|
||||
dnl Not sure what minimal version does not choke on sub directories.
|
||||
dnl Testing with 1.14.
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
AC_CONFIG_HEADERS([src/config.h])
|
||||
|
||||
|
||||
@@ -164,17 +177,8 @@ dnl Configure libtool
|
||||
AC_LIBTOOL_WIN32_DLL
|
||||
AM_PROG_LIBTOOL
|
||||
|
||||
if test x"$modules" = xdisabled
|
||||
then
|
||||
echo "Modules still disabled..."
|
||||
MODULE_OBJ="legacy_module.\$(OBJEXT)"
|
||||
else
|
||||
MODULE_OBJ="module.\$(OBJEXT)"
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL( [HAVE_MODULES], [test "x$modules" = xenabled] )
|
||||
|
||||
AC_SUBST(MODULE_OBJ)
|
||||
AC_SUBST(LT_LDFLAGS)
|
||||
AC_SUBST(EXEC_LT_LDFLAGS)
|
||||
|
||||
@@ -1057,6 +1061,8 @@ else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
|
||||
# Again, prepend path for non-recursive make.
|
||||
LFS_LOBJ=`for i in $LFS_LOBJ; do echo src/libmpg123/$i; done | tr '\n' ' '`
|
||||
AC_SUBST(LFS_LOBJ)
|
||||
|
||||
|
||||
@@ -1472,6 +1478,12 @@ do
|
||||
word_in_list "$i.lo" $DECODER_LOBJ || DECODER_LOBJ="$DECODER_LOBJ $i.lo"
|
||||
done
|
||||
|
||||
# Another preprocessing step: Append prefix for non-recursive make.
|
||||
# Just because $(addprefix ...) is a GNU extension.
|
||||
|
||||
DECODER_OBJ=`for i in $DECODER_OBJ; do echo src/libmpg123/$i; done | tr '\n' ' '`
|
||||
DECODER_LOBJ=`for i in $DECODER_LOBJ; do echo src/libmpg123/$i; done | tr '\n' ' '`
|
||||
|
||||
AC_SUBST(DECODER_OBJ)
|
||||
AC_SUBST(DECODER_LOBJ)
|
||||
|
||||
@@ -1954,150 +1966,45 @@ fi
|
||||
# That's (beginning of) the list for mpg123's internal default.
|
||||
default_output_modules=$default_output_module
|
||||
|
||||
OUTPUT_OBJ=
|
||||
OUTPUT_MOD=
|
||||
OUTPUT_CFLAGS=
|
||||
OUTPUT_LIBS=
|
||||
OUTPUT_LDFLAGS=
|
||||
|
||||
# Setup the static build.
|
||||
if test "x$modules" = xdisabled
|
||||
then
|
||||
echo "Preparing static output $default_output_module"
|
||||
OUTPUT_MOD="$default_output_module"
|
||||
OUTPUT_OBJ="output/$default_output_module.\$(OBJEXT)"
|
||||
OUTPUT_CFLAGS=
|
||||
OUTPUT_LIBS=
|
||||
# That feels stupid... what about hashed arrays?
|
||||
case $OUTPUT_MOD in
|
||||
# Here's a script for that tedious list, perhaps to be outsourced together with the one in src/output/Makefile.am
|
||||
#for i in tinyalsa alsa qsa coreaudio esd jack nas oss portaudio pulse sdl sndio sun win32 win32_wasapi aix alib arts hp os2 sgi mint openal
|
||||
# The conditionals always need to be defined by configure, even if
|
||||
# HAVE_MODULES is FALSE!
|
||||
# Here's a script for that tedious list, perhaps to be outsourced together with the one in #src/output/Makefile.am
|
||||
#for i in 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
|
||||
#do echo $i; done |
|
||||
#perl -ne 'chomp; $big = uc($_); print <<EOT;
|
||||
# $_)
|
||||
# OUTPUT_LIBS="\$${big}_LIBS"
|
||||
# OUTPUT_LDFLAGS="\$${big}_LDFLAGS"
|
||||
# OUTPUT_CFLAGS="\$${big}_CFLAGS"
|
||||
# ;;
|
||||
#AM_CONDITIONAL([BUILD_${big}], [ test "$_" = \$default_output_module ])
|
||||
#EOT
|
||||
#'
|
||||
tinyalsa)
|
||||
OUTPUT_LIBS="$TINYALSA_LIBS"
|
||||
OUTPUT_LDFLAGS="$TINYALSA_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$TINYALSA_CFLAGS"
|
||||
;;
|
||||
alsa)
|
||||
OUTPUT_LIBS="$ALSA_LIBS"
|
||||
OUTPUT_LDFLAGS="$ALSA_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$ALSA_CFLAGS"
|
||||
;;
|
||||
qsa)
|
||||
OUTPUT_LIBS="$QSA_LIBS"
|
||||
OUTPUT_LDFLAGS="$QSA_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$QSA_CFLAGS"
|
||||
;;
|
||||
coreaudio)
|
||||
OUTPUT_LIBS="$COREAUDIO_LIBS"
|
||||
OUTPUT_LDFLAGS="$COREAUDIO_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$COREAUDIO_CFLAGS"
|
||||
;;
|
||||
esd)
|
||||
OUTPUT_LIBS="$ESD_LIBS"
|
||||
OUTPUT_LDFLAGS="$ESD_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$ESD_CFLAGS"
|
||||
;;
|
||||
jack)
|
||||
OUTPUT_LIBS="$JACK_LIBS"
|
||||
OUTPUT_LDFLAGS="$JACK_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$JACK_CFLAGS"
|
||||
;;
|
||||
nas)
|
||||
OUTPUT_LIBS="$NAS_LIBS"
|
||||
OUTPUT_LDFLAGS="$NAS_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$NAS_CFLAGS"
|
||||
;;
|
||||
oss)
|
||||
OUTPUT_LIBS="$OSS_LIBS"
|
||||
OUTPUT_LDFLAGS="$OSS_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$OSS_CFLAGS"
|
||||
;;
|
||||
portaudio)
|
||||
OUTPUT_LIBS="$PORTAUDIO_LIBS"
|
||||
OUTPUT_LDFLAGS="$PORTAUDIO_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$PORTAUDIO_CFLAGS"
|
||||
;;
|
||||
pulse)
|
||||
OUTPUT_LIBS="$PULSE_LIBS"
|
||||
OUTPUT_LDFLAGS="$PULSE_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$PULSE_CFLAGS"
|
||||
;;
|
||||
sdl)
|
||||
OUTPUT_LIBS="$SDL_LIBS"
|
||||
OUTPUT_LDFLAGS="$SDL_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$SDL_CFLAGS"
|
||||
;;
|
||||
sndio)
|
||||
OUTPUT_LIBS="$SNDIO_LIBS"
|
||||
OUTPUT_LDFLAGS="$SNDIO_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$SNDIO_CFLAGS"
|
||||
;;
|
||||
sun)
|
||||
OUTPUT_LIBS="$SUN_LIBS"
|
||||
OUTPUT_LDFLAGS="$SUN_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$SUN_CFLAGS"
|
||||
;;
|
||||
win32)
|
||||
OUTPUT_LIBS="$WIN32_LIBS"
|
||||
OUTPUT_LDFLAGS="$WIN32_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$WIN32_CFLAGS"
|
||||
;;
|
||||
win32_wasapi)
|
||||
OUTPUT_LIBS="$WIN32_WASAPI_LIBS"
|
||||
OUTPUT_LDFLAGS="$WIN32_WASAPI_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$WIN32_WASAPI_CFLAGS"
|
||||
;;
|
||||
aix)
|
||||
OUTPUT_LIBS="$AIX_LIBS"
|
||||
OUTPUT_LDFLAGS="$AIX_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$AIX_CFLAGS"
|
||||
;;
|
||||
alib)
|
||||
OUTPUT_LIBS="$ALIB_LIBS"
|
||||
OUTPUT_LDFLAGS="$ALIB_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$ALIB_CFLAGS"
|
||||
;;
|
||||
arts)
|
||||
OUTPUT_LIBS="$ARTS_LIBS"
|
||||
OUTPUT_LDFLAGS="$ARTS_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$ARTS_CFLAGS"
|
||||
;;
|
||||
hp)
|
||||
OUTPUT_LIBS="$HP_LIBS"
|
||||
OUTPUT_LDFLAGS="$HP_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$HP_CFLAGS"
|
||||
;;
|
||||
os2)
|
||||
OUTPUT_LIBS="$OS2_LIBS"
|
||||
OUTPUT_LDFLAGS="$OS2_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$OS2_CFLAGS"
|
||||
;;
|
||||
sgi)
|
||||
OUTPUT_LIBS="$SGI_LIBS"
|
||||
OUTPUT_LDFLAGS="$SGI_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$SGI_CFLAGS"
|
||||
;;
|
||||
mint)
|
||||
OUTPUT_LIBS="$MINT_LIBS"
|
||||
OUTPUT_LDFLAGS="$MINT_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$MINT_CFLAGS"
|
||||
;;
|
||||
openal)
|
||||
OUTPUT_LIBS="$OPENAL_LIBS"
|
||||
OUTPUT_LDFLAGS="$OPENAL_LDFLAGS"
|
||||
OUTPUT_CFLAGS="$OPENAL_CFLAGS"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
|
||||
AM_CONDITIONAL([BUILD_DUMMY], [ test "dummy" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_TINYALSA], [ test "tinyalsa" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_ALSA], [ test "alsa" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_QSA], [ test "qsa" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_COREAUDIO], [ test "coreaudio" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_ESD], [ test "esd" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_JACK], [ test "jack" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_NAS], [ test "nas" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_OSS], [ test "oss" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_PORTAUDIO], [ test "portaudio" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_PULSE], [ test "pulse" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_SDL], [ test "sdl" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_SNDIO], [ test "sndio" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_SUN], [ test "sun" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_WIN32], [ test "win32" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_WIN32_WASAPI], [ test "win32_wasapi" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_AIX], [ test "aix" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_ALIB], [ test "alib" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_ARTS], [ test "arts" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_HP], [ test "hp" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_OS2], [ test "os2" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_SGI], [ test "sgi" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_MINT], [ test "mint" = $default_output_module ])
|
||||
AM_CONDITIONAL([BUILD_OPENAL], [ test "openal" = $default_output_module ])
|
||||
|
||||
if test "x$modules" = xenabled
|
||||
then
|
||||
|
||||
# Now make a comma-separated list again... eliminating the possible duplicate and dummy.
|
||||
for i in $output_modules
|
||||
do
|
||||
@@ -2109,13 +2016,6 @@ fi
|
||||
|
||||
AC_DEFINE_UNQUOTED( DEFAULT_OUTPUT_MODULE, "$default_output_modules", [The default audio output module(s) to use] )
|
||||
|
||||
|
||||
AC_SUBST(OUTPUT_OBJ)
|
||||
AC_SUBST(OUTPUT_MOD)
|
||||
AC_SUBST(OUTPUT_CFLAGS)
|
||||
AC_SUBST(OUTPUT_LIBS)
|
||||
AC_SUBST(OUTPUT_LDFLAGS)
|
||||
|
||||
dnl ############## Compiler Optimizations
|
||||
|
||||
CFLAGS="$ADD_CFLAGS $CFLAGS"
|
||||
@@ -2398,22 +2298,12 @@ fi
|
||||
|
||||
dnl ############## Final Output
|
||||
|
||||
# Do that here to prevent the above tests usign -lltdl...
|
||||
LIBMPG123_LIBS=$LIBS
|
||||
AC_SUBST(LIBMPG123_LIBS)
|
||||
if test "x$modules" = xenabled; then
|
||||
LIBS="-lltdl $LIBS"
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
libmpg123.pc
|
||||
mpg123.spec
|
||||
doc/Makefile
|
||||
src/Makefile
|
||||
src/output/Makefile
|
||||
src/libmpg123/Makefile
|
||||
src/libmpg123/mpg123.h
|
||||
src/libout123/out123.h
|
||||
])
|
||||
|
||||
AC_OUTPUT
|
||||
|
@@ -1,33 +0,0 @@
|
||||
## Makefile.am: produce Makefile.in from this
|
||||
|
||||
## copyright 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
|
||||
|
||||
|
||||
EXTRA_DIST = \
|
||||
BENCHMARKING \
|
||||
BUGS \
|
||||
CONTACT \
|
||||
PATENTS \
|
||||
README.3DNOW \
|
||||
README.gain \
|
||||
README.remote \
|
||||
ROAD_TO_LGPL \
|
||||
TODO \
|
||||
LICENSE \
|
||||
THANKS \
|
||||
ACCURACY \
|
||||
LARGEFILE \
|
||||
libmpg123_speed.txt \
|
||||
doxyhead.xhtml \
|
||||
doxy_examples.c \
|
||||
doxygen.conf \
|
||||
examples/mpg123_to_wav.c \
|
||||
examples/scan.c \
|
||||
examples/mpglib.c \
|
||||
examples/id3dump.c \
|
||||
examples/feedseek.c \
|
||||
examples/dump_seekindex.c \
|
||||
examples/extract_frames.c \
|
||||
examples/Makefile
|
28
doc/Makemodule.am
Normal file
28
doc/Makemodule.am
Normal file
@@ -0,0 +1,28 @@
|
||||
# Module for non-recursive mpg123 build system.
|
||||
|
||||
EXTRA_DIST += \
|
||||
doc/BENCHMARKING \
|
||||
doc/BUGS \
|
||||
doc/CONTACT \
|
||||
doc/PATENTS \
|
||||
doc/README.3DNOW \
|
||||
doc/README.gain \
|
||||
doc/README.remote \
|
||||
doc/ROAD_TO_LGPL \
|
||||
doc/TODO \
|
||||
doc/LICENSE \
|
||||
doc/THANKS \
|
||||
doc/ACCURACY \
|
||||
doc/LARGEFILE \
|
||||
doc/libmpg123_speed.txt \
|
||||
doc/doxyhead.xhtml \
|
||||
doc/doxy_examples.c \
|
||||
doc/doxygen.conf \
|
||||
doc/examples/mpg123_to_wav.c \
|
||||
doc/examples/scan.c \
|
||||
doc/examples/mpglib.c \
|
||||
doc/examples/id3dump.c \
|
||||
doc/examples/feedseek.c \
|
||||
doc/examples/dump_seekindex.c \
|
||||
doc/examples/extract_frames.c \
|
||||
doc/examples/Makefile
|
384
doc/buffer.txt
Normal file
384
doc/buffer.txt
Normal file
@@ -0,0 +1,384 @@
|
||||
________________________________________________________________________
|
||||
/ \
|
||||
| Let's analyze how the buffer stuff works to put it into proper form |
|
||||
| for libout123! (ThOr, 2015-06-13 and ongoing) |
|
||||
\________________________________________________________________________/
|
||||
|
||||
1. How does the buffer communication work?
|
||||
==========================================
|
||||
|
||||
There are these API calls:
|
||||
|
||||
- buffer_start()
|
||||
if(buffermem->justwait)
|
||||
xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_WAKEUP);
|
||||
|
||||
- buffer_stop()
|
||||
buffermem->justwait = TRUE;
|
||||
buffer_sig(SIGINT, TRUE);
|
||||
|
||||
- buffer_reset()
|
||||
buffer_sig(SIGUSR1, TRUE);
|
||||
|
||||
- buffer_resync()
|
||||
if(buffermem->justwait)
|
||||
{
|
||||
buffermem->wakeme[XF_WRITER] = TRUE;
|
||||
xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_RESYNC);
|
||||
xfermem_getcmd(buffermem->fd[XF_WRITER], TRUE);
|
||||
}
|
||||
else buffer_sig(SIGINT, TRUE);
|
||||
|
||||
- plain_buffer_resync()
|
||||
buffer_sig(SIGINT, FALSE);
|
||||
|
||||
- buffer_ignore_lowmem()
|
||||
if(buffermem->wakeme[XF_READER])
|
||||
xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_WAKEUP);
|
||||
|
||||
- buffer_end()
|
||||
xfermem_putcmd(buffermem->fd[XF_WRITER], rude ? XF_CMD_ABORT : XF_CMD_TERMINATE);
|
||||
|
||||
Also, there is direct use of the xfermem API:
|
||||
|
||||
- xfermem_write(buffermem, bytes, count)
|
||||
to push data to buffer
|
||||
|
||||
- xfermem_get_usedspace(buffermem)
|
||||
to get bytes still in buffer (not written to audio output)
|
||||
|
||||
- xfermem_block(XF_WRITER, buffermem)
|
||||
for synchronization / messaging to writer (buffer client)
|
||||
|
||||
- xfermem_putcmd(buffermem->fd[XF_WRITER], cmd)
|
||||
to give comands to buffer
|
||||
|
||||
- xfermem_getcmd(buffermem->fd[XF_WRITER], TRUE (FALSE?))
|
||||
to get commands/response from buffer?
|
||||
|
||||
I probably should clear that up first.
|
||||
|
||||
|
||||
1.1 xfermem
|
||||
-----------
|
||||
|
||||
Quoting Oliver:
|
||||
|
||||
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.
|
||||
|
||||
The actual shared memory is implemented using anonymous mmap(),
|
||||
mmap() of /dev/zero, or via traditional System V memory. This reminds
|
||||
me that there are some code paths that are not excercised often.
|
||||
I should introduce (runtime?) switch to be able to test each variant.
|
||||
On the other hand, the sysVshm API is not that rapidly changing.
|
||||
|
||||
Anyhow, the point is that we have xfermem structure and buffer memory
|
||||
shared between main and buffer process, by whatever means. Commands
|
||||
are exchanged via the socket pair xfermem->fd[XF_WRITER] and
|
||||
xfermem->fd[XF_READER].
|
||||
|
||||
- xfermem_init(xf, bufsize, msize, skipbuf)
|
||||
to intialize the pipe, msize and skipbuf equal to zero for mpg123 use
|
||||
|
||||
- xfermem_done(xf)
|
||||
to free the shared memory, not bothering with cleaning up the sockets
|
||||
Should I change that? The reader process exists before clearing the
|
||||
data structure, but the writer process keeps the socket open ...
|
||||
I think I should introduce xfermem_exit_writer() and xfermem_exit_reader(),
|
||||
|
||||
- xfermem_init_writer() / xfermem_init_reader()
|
||||
to close the respective other end of the socket pair
|
||||
|
||||
- xfermem_get_freespace()
|
||||
to return space available for writing
|
||||
|
||||
- xfermem_get_usedspace()
|
||||
to return space filled with data, waiting to be consumed
|
||||
|
||||
- xfermem_getcmd(fd, block)
|
||||
to ... well wait for a one-byte command code on the given file descriptor,
|
||||
blocking or non-blocking.
|
||||
|
||||
- xfermem_putcmd(fd, cmd)
|
||||
to send a command to the other end with the fd on this side
|
||||
|
||||
- xfermem_block(rw, xf)
|
||||
to synchronize ... needs some thought
|
||||
The value of rw is XF_READER or XF_WRITER, xf->wakeme[rw] is set, if the
|
||||
other end has set xf->wakeme[1-rw], it gets a wakeup call
|
||||
(xfermem_putcmd(xf->fd[rw], XF_CMD_WAKEUP)) and this end waits for a sign
|
||||
(xfermem_getcmd(xf->fd[rw], TRUE)), clearing cf->wakeme[rw] after that.
|
||||
Classic synchronization.
|
||||
|
||||
- xfermem_sigblock(rw, xf, pid, sig)
|
||||
to signal the other process by given pid and wait for a wakeup call as
|
||||
response
|
||||
I added that to fix bug 2796802 in the year 2009. Hm. Well, it is
|
||||
necessary to interrupt the buffer process if it is not currently
|
||||
waiting for a command.
|
||||
|
||||
- xfermem_write(xf, buffer, bytes)
|
||||
to wait until enough space is free, then copy over the bytes
|
||||
A bonus is to wakeup the reader process if it has xf->wakeme set.
|
||||
|
||||
That's it. It's a shared ringbuffer with some synchronization and
|
||||
messaging.
|
||||
|
||||
|
||||
1.2 buffer API explained
|
||||
------------------------
|
||||
|
||||
Now it's time to decipher what the buffer API calls do. The buffer works
|
||||
on an instance of xfermem which is called buffermem, but I'll refer to it
|
||||
using xf as before.
|
||||
|
||||
- buffer_start()
|
||||
to send XF_CMD_WAKEUP to the buffer in case it is waiting (xf->justwait)
|
||||
Why is there xf->justwait in addition to xf->wakeme? Apparently to set
|
||||
it from the reader in buffer_stop(). It's the hack to be able to use
|
||||
SIGINT for two messages.
|
||||
|
||||
- buffer_stop()
|
||||
to interrupt the buffer and send it into waiting mode
|
||||
This sets xf->justwait and then signals SIGINT, waiting for the buffer
|
||||
to acknowledge. The buffer process sets intflag and, on the next occasion
|
||||
in the main loop, drains its command queue, sends wakeup to the writer
|
||||
desired. It then waits for commands.
|
||||
|
||||
- buffer_reset()
|
||||
to interrupt the buffer, causing it to reopen the audio device, possibly
|
||||
with new settings
|
||||
This also discards all data currently in the buffer (presumably in
|
||||
incompatible audio format/encoding) and sends a wakeup to the writer
|
||||
if desired. Flushing of audio device happens first.
|
||||
|
||||
- buffer_resync()
|
||||
to send XF_CMD_RESYNC directly or indirectly (plain SIGINT) to the buffer
|
||||
The buffer flushes the audio and discards all buffer data, wakes writer
|
||||
afterwards.
|
||||
|
||||
- plain_buffer_resync()
|
||||
to signal a resync (SIGINT) to the buffer without waiting for the result
|
||||
This disregards the case
|
||||
|
||||
- buffer_ignore_lowmem()
|
||||
to wake the buffer process
|
||||
Yes, this just sends XF_CMD_WAKEUP to the buffer, if it is waiting for it.
|
||||
Why the funny name for the function?
|
||||
Well, it is designed for the situation where the buffer is waiting for
|
||||
more data to start playback again. Normal writing of data gives
|
||||
XF_CMD_WAKEUP_INFO, which only triggers playback if there is enough data
|
||||
buffered. XF_CMD_WAKEUP triggers playback right away (if there is any data).
|
||||
There is a catch: This routine only does a wakeup call if the buffer already
|
||||
blocks waiting for a command, but there is a possible race condition where
|
||||
the buffer is about to enter xfermem_block() but didn't yet. Then, there is
|
||||
no effect of buffer_ignore_lowmem()!
|
||||
|
||||
- buffer_end()
|
||||
to either send XF_CMD_ABORT (end processing right away) or XF_CMD_TERMINATE
|
||||
In either case, the buffer only gets this as normal command, not per signal,
|
||||
though I have to think about what the signalling stuff really adds to the
|
||||
picture. Damn! It's designed to abort flush_output(), or rather
|
||||
ao->write(). I twarted that some time ago by making flush_output() resilient.
|
||||
I need to revert that; the buffer code should not call flush_output.
|
||||
Instead, it should just call ao->write(). Being interrupted does not matter
|
||||
much since the buffer continues to write on the next iteration anyway.
|
||||
But then, I also need to make the buffer resilient about ao->write()
|
||||
being interrupted. It is already checking for SIGINT / SIGUSR1, but
|
||||
I uttered something about SIGSTOP / SIGCONT. I need to revisit that
|
||||
behaviour. Does SIGSTOP really cause audio output writes to return early?
|
||||
|
||||
|
||||
1.3 Analysis
|
||||
------------
|
||||
|
||||
Well, there we are now. Some questions popped, specifically about the use
|
||||
of signals.
|
||||
|
||||
- 1. Is the use of signals SIGINT / SIGUSR1 really appropriate?
|
||||
|
||||
- 2. What do SIGSTOP / SIGCONT do to ao->flush()?
|
||||
|
||||
The idea of the signals should be that the buffer reacts to them immediately
|
||||
instead of finishing a write to the audio device. I don't see another point.
|
||||
I may want to revisit which actions really demand that kind of reaction.
|
||||
Currently, it's three of them:
|
||||
|
||||
- stop operation (justwait)
|
||||
- resync (discarding buffered data)
|
||||
- reset (discard data, re-open audio device)
|
||||
|
||||
That list looks sensible.
|
||||
|
||||
Now, I damanged that logic in 2007 with commit 1278. I put flush_output() also
|
||||
into the buffer, which broke the immediate reaction after a signal. Now the
|
||||
question is: Do we really care about that immediate reaction? Only if the
|
||||
hardware buffer happens to be large, or because it is blocking for some
|
||||
reason. Hm, the latter could be annoying.
|
||||
|
||||
In the current state, the signals could be replaced with normal messages and
|
||||
nothing would change. But I will change that and repair the buffer reaction
|
||||
again!
|
||||
|
||||
The second question about SIGSTOP / SIGCONT doesn't really matter in this
|
||||
context. The normal flush_output() indeed should loop to make things work,
|
||||
as that is the semantics of the non-buffered output. It returns after it
|
||||
is finised. It may be a question if I want to introduce that in the libout123
|
||||
API, too. I guess there should be a parameter or separate API call to
|
||||
decide if the writing/flushing should return after being interrupted or
|
||||
only after it wrote the given data. There are use cases for both.
|
||||
|
||||
But still, I have to check the behaviour with SIGSTOP. How does ALSA handle
|
||||
it? Heh, a search for that returns this:
|
||||
|
||||
https://sourceforge.net/p/mpg123/bugs/37/
|
||||
|
||||
with alsa and mpg123-0.65, when I press ^Z and then
|
||||
fg after some time, there is evil squeaky sound --
|
||||
the longer the pause, the longer the squeaky sound.
|
||||
|
||||
I keep stumbling over my own tracks in the internet;-) Of course, this bug
|
||||
wasn't always numbered 37, that's sf.net's reboot. Also, I don't find
|
||||
an attachment, but a patch is referenced by Clemens. Did sf.net kill that? At
|
||||
least I mentioned the revision: 637. Yes, that introduces some EINTR handling.
|
||||
|
||||
Another interesting data point:
|
||||
|
||||
http://compgroups.net/comp.linux.development.system/sigstop-and-interrupted-system/2865328
|
||||
|
||||
This is somewhat debatable. What the OP is talking about here is
|
||||
behaviour that occurs *in the absence* of a signal handler. On almost
|
||||
every other Unix, in this case, the signal is not visible to the
|
||||
application; that is, the system call is automatically resumed upon
|
||||
receipt of SIGCONT.
|
||||
|
||||
The behaviour of Linux is idiosyncratic: on Linux, a stop signal +
|
||||
SIGCONT causes certain system calls to fail with EINTR, even in the
|
||||
absence of signal handlers. In my reading of SUSv3, this ishould not
|
||||
happen. No other contemporary Unix implementation that I know of (and
|
||||
I've tested many) does this. (I'm told that historically one oether
|
||||
implementation -- I think it was AIX -- did this, but does not do it
|
||||
nowadays, since it was deemed to be non-standard.)
|
||||
|
||||
Cheers,
|
||||
|
||||
Michael
|
||||
|
||||
So it seems that, indeed, I need the outer loop over ao->flush to cope
|
||||
with SIGSTOP / SIGCONT. I didn't add it just for fun. If I remove that
|
||||
loop from the buffer, it needs to be aware that less written bytes that
|
||||
given does not have to mean that an error occured. It could be
|
||||
SIGSTOP+SIGCONT. And since the EINTR is handled inside the audio outputs,
|
||||
the details are hidden and the buffer just has to assume that if the
|
||||
number of written bytes is >= 0, that everything is OK so far.
|
||||
|
||||
|
||||
1.4 Specific use of buffer API in terminal mode
|
||||
-----------------------------------------------
|
||||
|
||||
The client part that does care about buffer operation is the interactive
|
||||
terminal control mode. I need to understand where it needs what buffer
|
||||
calls and how they should be abstracted in a generic audio output API.
|
||||
|
||||
First, there's buffer_ignore_lowmem() while paused to force playback in the
|
||||
loop. The usage of the buffer with the pause/looping mode is not encouraged
|
||||
anyway.
|
||||
|
||||
|
||||
1.5 Synchronization issues
|
||||
--------------------------
|
||||
|
||||
It occurs to me that, although the buffer logic at the core is rather old
|
||||
and tried, there are race conditions. A common idiom is this:
|
||||
|
||||
if (xf->wakeme[XF_X]) xfermem_putcmd(xf->fd[XF_Y], XF_CMD_WAKEUP);
|
||||
|
||||
Now, this works if we are sure that xf->wakeme[XF_X] has been set by the other
|
||||
end before the code above was triggered. Even without digging into shared
|
||||
memory semantics and questions of atomicity (I hereby declare int-sized writes
|
||||
as atomic, which generally works nowadays. Also, I could make the data type
|
||||
smaller, as it's just about zero or one. How can the effective setting of
|
||||
a single bit not be atomic?), this can only be considered safe if the
|
||||
code is triggered in response to an action from the other side, after the
|
||||
other side has set xf->wakeme[XF_X].
|
||||
|
||||
This works when the buffer is currently blocking, waiting for a command inside
|
||||
xfermem_block(), and the writer does a xfermem_block or xfermem_sigblock on
|
||||
its own.
|
||||
|
||||
Let's focus on buffer_ignore_lowmem(). This works to unleash the buffer if
|
||||
it is currently stuck in xfermem_block() because of not having enough data.
|
||||
What if it just entered the branch for that but did not call xfermem_block()
|
||||
yet? then, xf->wakeme[XF_READER] won't be set yet and buffer_ignore_lowmem()
|
||||
will not trigger anything. The buffer will block although we wanted it to
|
||||
continue!
|
||||
|
||||
This is a very small time window, but it is possible. This is a definition of
|
||||
unreliable code.
|
||||
|
||||
In this specific case, we don't actually want to tell the buffer all the time
|
||||
that it should ignore small buffer fill. We want it to ignore the low buffer
|
||||
fill from now on until told otherwise. We want to set some permanent flag.
|
||||
|
||||
Also, in this specific case, things work out since buffer_ignore_lowmem() is
|
||||
called in a loop anyway. It will catch the buffer blocking some later time.
|
||||
Is this now as intended? Is it buggy?
|
||||
|
||||
The crux with buffer_ignore_lowmem() is that it assumes state on the side of
|
||||
the buffer and gives a command the meaning of which depends on this state.
|
||||
But, well it's really non-fatal in this case. I have to wonder how I can
|
||||
make it robust for general use. I guess the sending of the commands needs
|
||||
to be unconditional and the buffer needs to look for commands on each loop
|
||||
cycle, without blocking.
|
||||
|
||||
Uh, apart from the messed up semantics of output_pause(), I now come back
|
||||
to the races with seekmode() of term.c . It uses buffer_stop, but it also
|
||||
should do buffer_resync right away. The call to that follows later, after
|
||||
we have put the buffer into justwait mode. And it actually works that way.
|
||||
But it is still not the right way to do this!
|
||||
|
||||
The interactive seeking really is an interesting place for audio buffering.
|
||||
As long as I do not want to seek around inside the actual buffer, the
|
||||
buffered data needs to be thrown away. This should happen in seekmode()
|
||||
directly. Also, the playback needs to be paused with the buffer as long
|
||||
as it does not really tie in with the seeking. They're mutually exclusive.
|
||||
|
||||
If not using the buffer, playback can stay active to give audible feedback
|
||||
during seeking. How do I abstract that? I guess there need to be
|
||||
audio_buffer_disable/enable() calls. A bonus could even be to really just
|
||||
disable the buffer and do playback directly while seeking, but I don't want
|
||||
to close/reopen the audio device unnecessarily (think JACK again).
|
||||
|
||||
There could be a pass-through mode for the buffer, giving lower latency.
|
||||
But what am I doing here? I want to extract the audio code into a library.
|
||||
I can do the funky improvements later.
|
||||
|
||||
|
||||
2. What would I like to change?
|
||||
===============================
|
||||
|
||||
I want to have operation with and without buffer more symmetric. I do not
|
||||
want only a one-shot time window to query audio caps, I want to be able to
|
||||
do that anytime (preferrably not while an audio device is open). I want
|
||||
more communication with the buffer. I need to exchange strings (module
|
||||
name, error response) and numbers (various parameters).
|
||||
|
||||
There are established ways already. I can repoen the audio device with
|
||||
differing settings. I can query caps. One just needs some generic
|
||||
(or not so generic) fields in the xfermem structure. The only new thing
|
||||
is that I can reserve some space for moderate error strings, too. Or:
|
||||
If no audio data is pending, I'd have lots of space to point into for
|
||||
message strings. But, well, it should be enough to communicate a specific
|
||||
error code and the value of errno, if that may help. Verbose printing
|
||||
can hapen to stderr.
|
||||
|
||||
While at that, I'll make the communication safer by adhering to a stricter
|
||||
protocol: Keep requests and responses together. Acknowledge actions on the
|
||||
buffer side. The only fire-and-forget performance critical part is about
|
||||
feeding audio data. Everhthing else should be rather synchronous, thank
|
||||
you very much.
|
@@ -2,37 +2,64 @@
|
||||
|
||||
use strict;
|
||||
|
||||
my $dir = 'src/libmpg123';
|
||||
my @headers = qw(compat decode dither frame getbits getcpuflags huffman icy2utf8 icy id3 index mpg123lib_intern optimize parse reader);
|
||||
my $prefix = 'INT123_';
|
||||
my @leavealone = qw(strerror strdup);
|
||||
print STDERR "Do not forget to handle renaming of symbols from a statically linked output module!\n";
|
||||
|
||||
my %ident;
|
||||
my @instances =
|
||||
(
|
||||
{ name => 'intsym.h'
|
||||
, guard => 'MPG123_INTSYM_H'
|
||||
, dir => 'src/libmpg123'
|
||||
, headers => [qw(../compat/compat decode dither frame getbits getcpuflags huffman icy2utf8 icy id3 index mpg123lib_intern optimize parse reader)]
|
||||
, prefix => 'INT123_'
|
||||
, apiprefix => 'mpg123_'
|
||||
, conditional => { strerror=>'HAVE_STRERROR', strdup=>'HAVE_STRDUP' }
|
||||
, symbols => [qw(COS9 tfcos36 pnts)] # extra symbols
|
||||
}
|
||||
,
|
||||
{ name => 'out123_intsym.h'
|
||||
, guard => 'OUT123_INTSYM_H'
|
||||
, dir => 'src/libout123'
|
||||
, headers => [qw(../compat/compat module buffer xfermem wav out123_int)]
|
||||
, prefix => 'IOT123_'
|
||||
, apiprefix => 'out123_|audio_|output_'
|
||||
, conditional => { strerror=>'HAVE_STRERROR', strdup=>'HAVE_STRDUP' }
|
||||
, symbols => [qw(catchsignal)] # extra symbols
|
||||
}
|
||||
);
|
||||
|
||||
# Extra symbols.
|
||||
my @symbols = qw(COS9 tfcos36 pnts);
|
||||
|
||||
foreach my $header (@headers)
|
||||
for my $i (@instances)
|
||||
{
|
||||
my $dir = $i->{dir};
|
||||
print STDERR "dir: $dir\n";
|
||||
my $outfile = "$dir/$i->{name}";
|
||||
print STDERR "generating $outfile\n";
|
||||
open(my $out, '>', $outfile) or die "Meh.\n";
|
||||
|
||||
my %ident;
|
||||
my @symbols = @{$i->{symbols}};
|
||||
my $apiex = qr/^$i->{apiprefix}/;
|
||||
|
||||
foreach my $header (@{$i->{headers}})
|
||||
{
|
||||
print STDERR "==== working on header $header\n";
|
||||
open(DAT, '<', $dir.'/'.$header.'.h') or die "Cannot open $header.\n";
|
||||
while(<DAT>)
|
||||
{
|
||||
{ # Only simple declarations parsed here, more configured manually.
|
||||
if(/^([^\s\(#][^\(]*)\s\*?([a-z][a-z_0-9]+)\s*\(/)
|
||||
{
|
||||
# Skip preprocessing/comment stuff and official API.
|
||||
unless($1 =~ '^#' or $1 =~ '/\*' or $2 =~ /^mpg123_/)
|
||||
unless($1 =~ '^#' or $1 =~ '/\*' or $2 =~ $apiex)
|
||||
{
|
||||
push(@symbols, $2) unless grep {$_ eq $2} @leavealone;
|
||||
push(@symbols, $2) unless grep {$_ eq $2} (keys %{$i->{conditional}});
|
||||
}
|
||||
}
|
||||
}
|
||||
close(DAT);
|
||||
}
|
||||
}
|
||||
|
||||
print STDERR join("\n", glob("$dir/*.S"))."\n";
|
||||
foreach my $asm (glob("$dir/*.S"))
|
||||
{
|
||||
print STDERR join("\n", glob("$dir/*.S"))."\n";
|
||||
foreach my $asm (glob("$dir/*.S"))
|
||||
{
|
||||
print STDERR "==== working on asm file $asm\n";
|
||||
open(DAT, '<', $asm) or die "Cannot open $asm.\n";
|
||||
while(<DAT>)
|
||||
@@ -44,22 +71,36 @@ foreach my $asm (glob("$dir/*.S"))
|
||||
}
|
||||
}
|
||||
close(DAT);
|
||||
}
|
||||
}
|
||||
|
||||
print "#ifndef MPG123_INTMAP_H\n";
|
||||
print "#define MPG123_INTMAP_H\n";
|
||||
print "/* Mapping of internal mpg123 symbols to something that is less likely to conflict in case of static linking. */\n";
|
||||
print $out "#ifndef $i->{guard}\n";
|
||||
print $out "#define $i->{guard}\n";
|
||||
print $out "/* Mapping of internal mpg123 symbols to something that is less likely to conflict in case of static linking. */\n";
|
||||
|
||||
foreach my $sym (@symbols)
|
||||
{
|
||||
my $name = $prefix.$sym;
|
||||
foreach my $sym (@symbols)
|
||||
{
|
||||
my $name = $i->{prefix}.$sym;
|
||||
my $signi = substr($name,0,31);
|
||||
#print STDERR "$name / $signi\n";
|
||||
if(++$ident{$signi} > 1)
|
||||
{
|
||||
die "That symbol is not unique in 31 chars: $name\n";
|
||||
print STDERR "WARNING: That symbol is not unique in 31 chars: $name\n";
|
||||
}
|
||||
print $out "#define $sym $name\n";
|
||||
}
|
||||
foreach my $key (keys %{$i->{conditional}})
|
||||
{
|
||||
my ($sym, $guard) = ($key, $i->{conditional}{$key});
|
||||
my $name = $i->{prefix}.$sym;
|
||||
my $signi = substr($name,0,31);
|
||||
if(++$ident{$signi} > 1)
|
||||
{
|
||||
print STDERR "WARNING: That symbol is not unique in 31 chars: $name\n";
|
||||
}
|
||||
print $out "#ifndef $guard\n";
|
||||
print $out "#define $sym $name\n";
|
||||
print $out "#endif\n";
|
||||
}
|
||||
print "#define $sym $name\n";
|
||||
}
|
||||
|
||||
print "#endif\n";
|
||||
print $out "#endif\n";
|
||||
}
|
||||
|
238
src/Makefile.am
238
src/Makefile.am
@@ -1,238 +0,0 @@
|
||||
## Makefile.am: produce Makefile.in from this
|
||||
|
||||
## copyright 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
|
||||
|
||||
AM_CPPFLAGS = -DPKGLIBDIR="\"$(pkglibdir)\""
|
||||
mpg123_LDADD = $(LIBLTDL) libmpg123/libmpg123.la @MODULE_OBJ@ @OUTPUT_OBJ@ @OUTPUT_LIBS@
|
||||
mpg123_LDFLAGS = @EXEC_LT_LDFLAGS@ @OUTPUT_LDFLAGS@
|
||||
|
||||
# The dependency on libmpg123 could/should vanish. Or not.
|
||||
# Does it matter much?
|
||||
out123_LDADD = $(LIBLTDL) libmpg123/libmpg123.la @MODULE_OBJ@ @OUTPUT_OBJ@ @OUTPUT_LIBS@
|
||||
out123_LDFLAGS = @EXEC_LT_LDFLAGS@ @OUTPUT_LDFLAGS@
|
||||
|
||||
AM_CPPFLAGS += $(LTDLINCL) -I$(top_builddir)/src/libmpg123 -I$(top_srcdir)/src/libmpg123
|
||||
# libltdl is not mentioned here... it's not that trivial
|
||||
mpg123_DEPENDENCIES = @OUTPUT_OBJ@ @MODULE_OBJ@ libmpg123/libmpg123.la
|
||||
out123_DEPENDENCIES = @OUTPUT_OBJ@ @MODULE_OBJ@ libmpg123/libmpg123.la
|
||||
|
||||
SUBDIRS = output libmpg123
|
||||
EXTRA_DIST = legacy_module.c module.c sfifo.c sfifo.h mpg123-with-modules out123-with-modules
|
||||
|
||||
|
||||
CLEANFILES = *.a
|
||||
|
||||
bin_PROGRAMS = mpg123 out123 mpg123-id3dump mpg123-strip
|
||||
|
||||
mpg123_id3dump_DEPENDENCIES = libmpg123/libmpg123.la
|
||||
mpg123_id3dump_LDADD = libmpg123/libmpg123.la
|
||||
|
||||
mpg123_strip_DEPENDENCIES = libmpg123/libmpg123.la
|
||||
mpg123_strip_LDADD = libmpg123/libmpg123.la
|
||||
|
||||
EXTRA_PROGRAMS = tests/seek_whence tests/noise tests/text tests/plain_id3
|
||||
|
||||
mpg123_SOURCES = \
|
||||
audio.c \
|
||||
audio.h \
|
||||
buffer.c \
|
||||
buffer.h \
|
||||
common.c \
|
||||
common.h \
|
||||
sysutil.c \
|
||||
sysutil.h \
|
||||
libmpg123/compat.c \
|
||||
libmpg123/compat.h \
|
||||
control_generic.c \
|
||||
equalizer.c \
|
||||
getlopt.c \
|
||||
getlopt.h \
|
||||
httpget.c \
|
||||
httpget.h \
|
||||
resolver.c \
|
||||
resolver.h \
|
||||
genre.h \
|
||||
genre.c \
|
||||
module.h \
|
||||
mpg123.c \
|
||||
mpg123app.h \
|
||||
metaprint.c \
|
||||
metaprint.h \
|
||||
local.h \
|
||||
local.c \
|
||||
playlist.c \
|
||||
playlist.h \
|
||||
streamdump.h \
|
||||
streamdump.c \
|
||||
term.c \
|
||||
term.h \
|
||||
wav.c \
|
||||
win32_support.h \
|
||||
wavhead.h \
|
||||
xfermem.c \
|
||||
xfermem.h
|
||||
|
||||
# Replace common.h by sysutil.h!
|
||||
out123_SOURCES = \
|
||||
audio.c \
|
||||
audio.h \
|
||||
buffer.c \
|
||||
buffer.h \
|
||||
sysutil.c \
|
||||
sysutil.h \
|
||||
libmpg123/compat.c \
|
||||
libmpg123/compat.h \
|
||||
getlopt.c \
|
||||
getlopt.h \
|
||||
module.h \
|
||||
out123.c \
|
||||
mpg123app.h \
|
||||
wav.c \
|
||||
win32_support.h \
|
||||
wavhead.h \
|
||||
xfermem.c \
|
||||
xfermem.h
|
||||
|
||||
mpg123_id3dump_SOURCES = mpg123-id3dump.c \
|
||||
getlopt.c \
|
||||
getlopt.h \
|
||||
libmpg123/compat.c \
|
||||
libmpg123/compat.h
|
||||
|
||||
mpg123_strip_SOURCES = mpg123-strip.c \
|
||||
getlopt.c \
|
||||
getlopt.h \
|
||||
libmpg123/compat.c \
|
||||
libmpg123/compat.h
|
||||
|
||||
if WIN32_CODES
|
||||
mpg123_SOURCES += \
|
||||
win32_support.c \
|
||||
win32_net.c
|
||||
|
||||
out123_SOURCES+= \
|
||||
win32_support.c
|
||||
|
||||
mpg123_id3dump_SOURCES += \
|
||||
win32_support.c
|
||||
endif
|
||||
|
||||
# That is not nice... but it is how I manage to get the dependency on output/alsa.o without error about .deps/output/alsa.Tpo .
|
||||
# Did I mention that recursive make sucks?
|
||||
# Actually ... does that really make really here? Not been updated
|
||||
# for some time before. I need to revisit our build system.
|
||||
# `%'-style pattern rules are a GNU make extension
|
||||
#find output/ -name '*.c' | sort | perl -ne 'chomp; $mod=$_; $mod=~s/\.c$/.\$(OBJEXT)/;
|
||||
#print "$mod: $_ audio.h module.h\n";
|
||||
#print "\tcd output && \$(MAKE)\n\n";'
|
||||
|
||||
output/aix.$(OBJEXT): output/aix.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/alib.$(OBJEXT): output/alib.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/alsa.$(OBJEXT): output/alsa.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/arts.$(OBJEXT): output/arts.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/coreaudio.$(OBJEXT): output/coreaudio.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/dummy.$(OBJEXT): output/dummy.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/esd.$(OBJEXT): output/esd.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/hp.$(OBJEXT): output/hp.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/jack.$(OBJEXT): output/jack.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/mint.$(OBJEXT): output/mint.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/nas.$(OBJEXT): output/nas.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/openal.$(OBJEXT): output/openal.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/os2.$(OBJEXT): output/os2.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/oss.$(OBJEXT): output/oss.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/portaudio.$(OBJEXT): output/portaudio.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/pulse.$(OBJEXT): output/pulse.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/qsa.$(OBJEXT): output/qsa.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/sdl.$(OBJEXT): output/sdl.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/sgi.$(OBJEXT): output/sgi.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/sndio.$(OBJEXT): output/sndio.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/sun.$(OBJEXT): output/sun.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/tinyalsa.$(OBJEXT): output/tinyalsa.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/win32.$(OBJEXT): output/win32.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
output/win32_wasapi.$(OBJEXT): output/win32_wasapi.c audio.h module.h
|
||||
cd output && $(MAKE)
|
||||
|
||||
# Would have to mention _all_ source files... Dammit, that's what the libmpg123/Makefile.am does!
|
||||
# But again, the a make $something here needs that stupid rule... WHY???
|
||||
libmpg123/libmpg123.la: config.h libmpg123/mpg123.h
|
||||
cd libmpg123 && $(MAKE)
|
||||
|
||||
tests_seek_whence_SOURCES = \
|
||||
tests/seek_whence.c \
|
||||
libmpg123/compat.h \
|
||||
libmpg123/compat.c
|
||||
|
||||
tests_seek_whence_DEPENDENCIES = libmpg123/libmpg123.la
|
||||
tests_seek_whence_LDADD = libmpg123/libmpg123.la
|
||||
|
||||
tests_noise_SOURCES = \
|
||||
tests/noise.c \
|
||||
libmpg123/compat.h \
|
||||
libmpg123/compat.c \
|
||||
libmpg123/dither.h \
|
||||
libmpg123/dither.c
|
||||
|
||||
tests_text_SOURCES = \
|
||||
tests/text.c \
|
||||
tests/testtext.h \
|
||||
libmpg123/compat.h \
|
||||
libmpg123/compat.c
|
||||
|
||||
tests_text_DEPENDENCIES = libmpg123/libmpg123.la
|
||||
tests_text_LDADD = libmpg123/libmpg123.la
|
||||
|
||||
tests_plain_id3_SOURCES = \
|
||||
tests/plain_id3.c \
|
||||
libmpg123/compat.h \
|
||||
libmpg123/compat.c
|
||||
|
||||
tests_plain_id3_DEPENDENCIES = libmpg123/libmpg123.la
|
||||
tests_plain_id3_LDADD = libmpg123/libmpg123.la
|
161
src/Makemodule.am
Normal file
161
src/Makemodule.am
Normal file
@@ -0,0 +1,161 @@
|
||||
# Module for non-recursive mpg123 build system.
|
||||
|
||||
include src/libmpg123/Makemodule.am
|
||||
include src/libout123/Makemodule.am
|
||||
|
||||
bin_PROGRAMS += \
|
||||
src/mpg123 \
|
||||
src/out123 \
|
||||
src/mpg123-id3dump \
|
||||
src/mpg123-strip
|
||||
|
||||
src_mpg123_LDADD = $(LIBLTDL) \
|
||||
src/libmpg123/libmpg123.la \
|
||||
src/libout123/libout123.la
|
||||
|
||||
src_mpg123_LDFLAGS = @EXEC_LT_LDFLAGS@ $(output_ldflags)
|
||||
|
||||
# The dependency on libmpg123 could/should vanish. Or not.
|
||||
# Does it matter much?
|
||||
src_out123_LDADD = $(LIBLTDL) \
|
||||
src/libmpg123/libmpg123.la \
|
||||
src/libout123/libout123.la
|
||||
|
||||
src_out123_LDFLAGS = @EXEC_LT_LDFLAGS@ $(output_ldflags)
|
||||
|
||||
# The sfifo situation needs to be cleared up.
|
||||
# It's used from places.
|
||||
EXTRA_DIST += \
|
||||
src/sfifo.c \
|
||||
src/sfifo.h \
|
||||
src/mpg123-with-modules \
|
||||
src/out123-with-modules
|
||||
|
||||
CLEANFILES += src/*.a
|
||||
|
||||
src_mpg123_id3dump_LDADD = src/libmpg123/libmpg123.la
|
||||
|
||||
src_mpg123_strip_LDADD = src/libmpg123/libmpg123.la
|
||||
|
||||
EXTRA_PROGRAMS += \
|
||||
src/tests/seek_whence \
|
||||
src/tests/noise \
|
||||
src/tests/text \
|
||||
src/tests/plain_id3
|
||||
|
||||
src_mpg123_SOURCES = \
|
||||
src/audio.c \
|
||||
src/audio.h \
|
||||
src/common.c \
|
||||
src/common.h \
|
||||
src/sysutil.c \
|
||||
src/sysutil.h \
|
||||
src/compat.c \
|
||||
src/compat/compat.h \
|
||||
src/compat/compat_impl.h \
|
||||
src/control_generic.c \
|
||||
src/equalizer.c \
|
||||
src/getlopt.c \
|
||||
src/getlopt.h \
|
||||
src/httpget.c \
|
||||
src/httpget.h \
|
||||
src/resolver.c \
|
||||
src/resolver.h \
|
||||
src/genre.h \
|
||||
src/genre.c \
|
||||
src/mpg123.c \
|
||||
src/mpg123app.h \
|
||||
src/metaprint.c \
|
||||
src/metaprint.h \
|
||||
src/local.h \
|
||||
src/local.c \
|
||||
src/playlist.c \
|
||||
src/playlist.h \
|
||||
src/streamdump.h \
|
||||
src/streamdump.c \
|
||||
src/term.c \
|
||||
src/term.h \
|
||||
src/win32_support.h
|
||||
|
||||
# Does that finally work to build/link the correct object file?
|
||||
src_mpg123_SOURCES +=
|
||||
|
||||
# Replace common.h by sysutil.h!
|
||||
src_out123_SOURCES = \
|
||||
src/audio.h \
|
||||
src/audio.c \
|
||||
src/sysutil.c \
|
||||
src/sysutil.h \
|
||||
src/common.h \
|
||||
src/compat.c \
|
||||
src/compat/compat.h \
|
||||
src/compat/compat_impl.h \
|
||||
src/getlopt.c \
|
||||
src/getlopt.h \
|
||||
src/out123.c \
|
||||
src/mpg123app.h \
|
||||
src/win32_support.h
|
||||
|
||||
src_mpg123_id3dump_SOURCES = \
|
||||
src/mpg123-id3dump.c \
|
||||
src/getlopt.c \
|
||||
src/getlopt.h \
|
||||
src/compat.c \
|
||||
src/compat/compat.h \
|
||||
src/compat/compat_impl.h
|
||||
|
||||
src_mpg123_strip_SOURCES = \
|
||||
src/mpg123-strip.c \
|
||||
src/getlopt.c \
|
||||
src/getlopt.h \
|
||||
src/compat.c \
|
||||
src/compat/compat.h \
|
||||
src/compat/compat_impl.h
|
||||
|
||||
if WIN32_CODES
|
||||
src_mpg123_SOURCES += \
|
||||
src/win32_support.c \
|
||||
src/win32_net.c
|
||||
|
||||
src_out123_SOURCES+= \
|
||||
src/win32_support.c
|
||||
|
||||
src_mpg123_id3dump_SOURCES += \
|
||||
src/win32_support.c
|
||||
endif
|
||||
|
||||
src_tests_seek_whence_SOURCES = \
|
||||
src/tests/seek_whence.c \
|
||||
src/compat.c \
|
||||
src/compat/compat.h \
|
||||
src/compat/compat_impl.h
|
||||
|
||||
src_tests_seek_whence_DEPENDENCIES = src/libmpg123/libmpg123.la
|
||||
src_tests_seek_whence_LDADD = src/libmpg123/libmpg123.la
|
||||
|
||||
src_tests_noise_SOURCES = \
|
||||
src/tests/noise.c \
|
||||
src/compat.c \
|
||||
src/compat/compat.h \
|
||||
src/compat/compat_impl.h \
|
||||
src/libmpg123/dither.h \
|
||||
src/libmpg123/dither_impl.h
|
||||
|
||||
src_tests_text_SOURCES = \
|
||||
src/tests/text.c \
|
||||
src/tests/testtext.h \
|
||||
src/compat.c \
|
||||
src/compat/compat.h \
|
||||
src/compat/compat_impl.h
|
||||
|
||||
src_tests_text_DEPENDENCIES = src/libmpg123/libmpg123.la
|
||||
src_tests_text_LDADD = src/libmpg123/libmpg123.la
|
||||
|
||||
src_tests_plain_id3_SOURCES = \
|
||||
src/tests/plain_id3.c \
|
||||
src/compat.c \
|
||||
src/compat/compat.h \
|
||||
src/compat/compat_impl.h
|
||||
|
||||
src_tests_plain_id3_DEPENDENCIES = src/libmpg123/libmpg123.la
|
||||
src_tests_plain_id3_LDADD = src/libmpg123/libmpg123.la
|
541
src/audio.c
541
src/audio.c
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
audio: audio output interface
|
||||
|
||||
copyright ?-2008 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
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 Michael Hipp
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include "mpg123app.h"
|
||||
#include "out123.h"
|
||||
#include "common.h"
|
||||
#include "sysutil.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
@@ -18,237 +18,6 @@
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
static int file_write(struct audio_output_struct* ao, unsigned char *bytes, int count)
|
||||
{
|
||||
return (int)write(ao->fn, bytes, count);
|
||||
}
|
||||
static int wave_write(struct audio_output_struct* ao, unsigned char *bytes, int count)
|
||||
{
|
||||
return wav_write(bytes, count);
|
||||
}
|
||||
static int builtin_get_formats(struct audio_output_struct *ao)
|
||||
{
|
||||
if(param.outmode == DECODE_CDR)
|
||||
{
|
||||
if(ao->rate == 44100 && ao->channels == 2)
|
||||
return MPG123_ENC_SIGNED_16;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else if(param.outmode == DECODE_AU) return MPG123_ENC_SIGNED_16|MPG123_ENC_UNSIGNED_8|MPG123_ENC_ULAW_8;
|
||||
else if(param.outmode == DECODE_WAV) return MPG123_ENC_SIGNED_16|MPG123_ENC_UNSIGNED_8|MPG123_ENC_FLOAT_32|MPG123_ENC_SIGNED_24|MPG123_ENC_SIGNED_32;
|
||||
else return MPG123_ENC_ANY;
|
||||
}
|
||||
static int builtin_close(struct audio_output_struct *ao)
|
||||
{
|
||||
switch(param.outmode)
|
||||
{
|
||||
case DECODE_WAV:
|
||||
return wav_close();
|
||||
break;
|
||||
case DECODE_AU:
|
||||
return au_close();
|
||||
break;
|
||||
case DECODE_CDR:
|
||||
return cdr_close();
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static int builtin_nothingint(struct audio_output_struct *ao){ return 0; }
|
||||
static void builtin_nothing(struct audio_output_struct *ao){}
|
||||
|
||||
audio_output_t* open_fake_module(void)
|
||||
{
|
||||
audio_output_t *ao = NULL;
|
||||
ao = alloc_audio_output();
|
||||
if(ao == NULL)
|
||||
{
|
||||
error("Cannot allocate memory for audio output data.");
|
||||
return NULL;
|
||||
}
|
||||
ao->module = NULL;
|
||||
ao->open = builtin_nothingint;
|
||||
ao->flush = builtin_nothing;
|
||||
ao->get_formats = builtin_get_formats;
|
||||
ao->write = wave_write;
|
||||
ao->close = builtin_close;
|
||||
ao->device = param.filename;
|
||||
ao->is_open = FALSE;
|
||||
switch(param.outmode)
|
||||
{
|
||||
case DECODE_FILE:
|
||||
ao->fn = OutputDescriptor;
|
||||
ao->write = file_write;
|
||||
break;
|
||||
case DECODE_WAV:
|
||||
ao->open = wav_open;
|
||||
break;
|
||||
case DECODE_CDR:
|
||||
ao->open = cdr_open;
|
||||
break;
|
||||
case DECODE_AU:
|
||||
ao->open = au_open;
|
||||
break;
|
||||
case DECODE_TEST:
|
||||
break;
|
||||
}
|
||||
|
||||
return ao;
|
||||
}
|
||||
|
||||
/* Open an audio output module, trying modules in list (comma-separated). */
|
||||
audio_output_t* open_output_module( const char* names )
|
||||
{
|
||||
mpg123_module_t *module = NULL;
|
||||
audio_output_t *ao = NULL;
|
||||
int result = 0;
|
||||
char *curname, *modnames;
|
||||
|
||||
if(param.usebuffer || names==NULL) return NULL;
|
||||
|
||||
/* Use internal code. */
|
||||
if(param.outmode != DECODE_AUDIO) return open_fake_module();
|
||||
|
||||
modnames = strdup(names);
|
||||
if(modnames == NULL)
|
||||
{
|
||||
error("Error allocating memory for module names.");
|
||||
return NULL;
|
||||
}
|
||||
/* Now loop over the list of possible modules to find one that works. */
|
||||
curname = strtok(modnames, ",");
|
||||
while(curname != NULL)
|
||||
{
|
||||
char* name = curname;
|
||||
curname = strtok(NULL, ",");
|
||||
if(param.verbose > 1) fprintf(stderr, "Trying output module %s.\n", name);
|
||||
/* Open the module, initial check for availability+libraries. */
|
||||
module = open_module( "output", name );
|
||||
if(module == NULL) continue;
|
||||
/* Check if module supports output */
|
||||
if(module->init_output == NULL)
|
||||
{
|
||||
error1("Module '%s' does not support audio output.", name);
|
||||
close_module(module);
|
||||
continue; /* Try next one. */
|
||||
}
|
||||
/* Allocation+initialization of memory for audio output type. */
|
||||
ao = alloc_audio_output();
|
||||
if(ao==NULL)
|
||||
{
|
||||
error("Failed to allocate audio output structure.");
|
||||
close_module(module);
|
||||
break; /* This is fatal. */
|
||||
}
|
||||
|
||||
/* Call the init function */
|
||||
ao->device = param.output_device;
|
||||
ao->flags = param.output_flags;
|
||||
/* Should I do funny stuff with stderr file descriptor instead? */
|
||||
if(curname == NULL)
|
||||
{
|
||||
if(param.verbose > 1)
|
||||
fprintf(stderr, "Note: %s is the last output option... showing you any error messages now.\n", name);
|
||||
}
|
||||
else ao->auxflags |= MPG123_OUT_QUIET; /* Probing, so don't spill stderr with errors. */
|
||||
ao->is_open = FALSE;
|
||||
ao->module = module; /* Need that to close module later. */
|
||||
result = module->init_output(ao);
|
||||
if(result == 0)
|
||||
{ /* Try to open the device. I'm only interested in actually working modules. */
|
||||
result = open_output(ao);
|
||||
close_output(ao);
|
||||
}
|
||||
else error2("Module '%s' init failed: %i", name, result);
|
||||
|
||||
if(result!=0)
|
||||
{ /* Try next one... */
|
||||
close_module(module);
|
||||
free(ao);
|
||||
ao = NULL;
|
||||
}
|
||||
else
|
||||
{ /* All good, leave the loop. */
|
||||
if(param.verbose > 1) fprintf(stderr, "Output module '%s' chosen.\n", name);
|
||||
|
||||
ao->auxflags &= ~MPG123_OUT_QUIET;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(modnames);
|
||||
if(ao==NULL) error1("Unable to find a working output module in this list: %s", names);
|
||||
|
||||
return ao;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Close the audio output and close the module */
|
||||
void close_output_module( audio_output_t* ao )
|
||||
{
|
||||
if (!ao) return; /* That covers buffer mode, too (ao == NULL there). */
|
||||
|
||||
debug("closing output module");
|
||||
/* Close the audio output */
|
||||
if(ao->is_open && ao->close != NULL) ao->close(ao);
|
||||
|
||||
/* Deinitialise the audio output */
|
||||
if (ao->deinit) ao->deinit( ao );
|
||||
|
||||
/* Unload the module */
|
||||
if (ao->module) close_module( ao->module );
|
||||
|
||||
/* Free up memory */
|
||||
free( ao );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* allocate and initialise memory */
|
||||
audio_output_t* alloc_audio_output()
|
||||
{
|
||||
audio_output_t* ao = malloc( sizeof( audio_output_t ) );
|
||||
if (ao==NULL) error( "Failed to allocate memory for audio_output_t." );
|
||||
|
||||
/* Initialise variables */
|
||||
ao->fn = -1;
|
||||
ao->rate = -1;
|
||||
ao->gain = param.gain;
|
||||
ao->userptr = NULL;
|
||||
ao->device = NULL;
|
||||
ao->channels = -1;
|
||||
ao->format = -1;
|
||||
ao->flags = 0;
|
||||
ao->auxflags = 0;
|
||||
|
||||
/*ao->module = NULL;*/
|
||||
|
||||
/* Set the callbacks to NULL */
|
||||
ao->open = NULL;
|
||||
ao->get_formats = NULL;
|
||||
ao->write = NULL;
|
||||
ao->flush = NULL;
|
||||
ao->close = NULL;
|
||||
ao->deinit = NULL;
|
||||
|
||||
return ao;
|
||||
}
|
||||
|
||||
/*
|
||||
static void audio_output_dump(audio_output_t *ao)
|
||||
{
|
||||
fprintf(stderr, "ao->fn=%d\n", ao->fn);
|
||||
fprintf(stderr, "ao->userptr=%p\n", ao->userptr);
|
||||
fprintf(stderr, "ao->rate=%ld\n", ao->rate);
|
||||
fprintf(stderr, "ao->gain=%ld\n", ao->gain);
|
||||
fprintf(stderr, "ao->device='%s'\n", ao->device);
|
||||
fprintf(stderr, "ao->channels=%d\n", ao->channels);
|
||||
fprintf(stderr, "ao->format=%d\n", ao->format);
|
||||
}
|
||||
*/
|
||||
|
||||
struct enc_desc
|
||||
{
|
||||
int code; /* MPG123_ENC_SOMETHING */
|
||||
@@ -320,6 +89,7 @@ const char* audio_encoding_name(const int encoding, const int longer)
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
static void capline(mpg123_handle *mh, long rate)
|
||||
{
|
||||
int enci;
|
||||
@@ -347,13 +117,9 @@ void print_capabilities(audio_output_t *ao, mpg123_handle *mh)
|
||||
size_t num_rates;
|
||||
const int *encs;
|
||||
size_t num_encs;
|
||||
const char *name = "<buffer>";
|
||||
const char *dev = "<none>";
|
||||
if(!param.usebuffer)
|
||||
{
|
||||
name = ao->module ? ao->module->name : "file/raw/test";
|
||||
if(ao->device != NULL) dev = ao->device;
|
||||
}
|
||||
char *name;
|
||||
char *dev;
|
||||
out123_driver_info(ao, &name, &dev);
|
||||
mpg123_rates(&rates, &num_rates);
|
||||
mpg123_encodings(&encs, &num_encs);
|
||||
fprintf(stderr,"\nAudio driver: %s\nAudio device: %s\nAudio capabilities:\n(matrix of [S]tereo or [M]ono support for sample format and rate in Hz)\n |", name, dev);
|
||||
@@ -387,17 +153,10 @@ void audio_capabilities(audio_output_t *ao, mpg123_handle *mh)
|
||||
mpg123_format_none(mh); /* Start with nothing. */
|
||||
if(param.force_encoding != NULL)
|
||||
{
|
||||
int i;
|
||||
if(!param.quiet) fprintf(stderr, "Note: forcing output encoding %s\n", param.force_encoding);
|
||||
|
||||
for(i=0;i<KNOWN_ENCS;++i)
|
||||
if(!strncasecmp(encdesc[i].name, param.force_encoding, encdesc[i].nlen))
|
||||
{
|
||||
force_fmt = encdesc[i].code;
|
||||
break;
|
||||
}
|
||||
|
||||
if(i==KNOWN_ENCS)
|
||||
force_fmt = audio_enc_name2code(param.force_encoding);
|
||||
if(!force_fmt)
|
||||
{
|
||||
error1("Failed to find an encoding to match requested \"%s\"!\n", param.force_encoding);
|
||||
return; /* No capabilities at all... */
|
||||
@@ -411,24 +170,9 @@ void audio_capabilities(audio_output_t *ao, mpg123_handle *mh)
|
||||
decode_rate = ri < num_rates ? rates[ri] : param.force_rate;
|
||||
rate = pitch_rate(decode_rate);
|
||||
if(param.verbose > 2) fprintf(stderr, "Note: checking support for %liHz/%ich.\n", rate, channels);
|
||||
#ifndef NOXFERMEM
|
||||
if(param.usebuffer)
|
||||
{ /* Ask the buffer process. It is waiting for this. */
|
||||
buffermem->rate = rate;
|
||||
buffermem->channels = channels;
|
||||
buffermem->format = 0; /* Just have it initialized safely. */
|
||||
debug2("asking for formats for %liHz/%ich", rate, channels);
|
||||
xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_AUDIOCAP);
|
||||
xfermem_getcmd(buffermem->fd[XF_WRITER], TRUE);
|
||||
fmts = buffermem->format;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{ /* Check myself. */
|
||||
ao->rate = rate;
|
||||
ao->channels = channels;
|
||||
fmts = ao->get_formats(ao);
|
||||
}
|
||||
|
||||
fmts = out123_encodings(ao, channels, rate);
|
||||
|
||||
if(param.verbose > 2) fprintf(stderr, "Note: result 0x%x\n", fmts);
|
||||
if(force_fmt)
|
||||
{ /* Filter for forced encoding. */
|
||||
@@ -442,257 +186,11 @@ void audio_capabilities(audio_output_t *ao, mpg123_handle *mh)
|
||||
else mpg123_format(mh, decode_rate, channels, fmts);
|
||||
}
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
/* Buffer loop shall start normal operation now. */
|
||||
if(param.usebuffer)
|
||||
{
|
||||
xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_WAKEUP);
|
||||
xfermem_getcmd(buffermem->fd[XF_WRITER], TRUE);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(param.verbose > 1) print_capabilities(ao, mh);
|
||||
}
|
||||
|
||||
#if !defined(WIN32) && !defined(GENERIC)
|
||||
#ifndef NOXFERMEM
|
||||
static void catch_child(void)
|
||||
{
|
||||
while (waitpid(-1, NULL, WNOHANG) > 0);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* FIXME: Old output initialization code that needs updating */
|
||||
|
||||
int init_output(audio_output_t **ao)
|
||||
{
|
||||
static int init_done = FALSE;
|
||||
|
||||
if (init_done) return 1;
|
||||
init_done = TRUE;
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
if (param.usebuffer)
|
||||
{
|
||||
unsigned int bufferbytes;
|
||||
sigset_t newsigset, oldsigset;
|
||||
bufferbytes = (param.usebuffer * 1024);
|
||||
if (bufferbytes < bufferblock)
|
||||
{
|
||||
bufferbytes = 2*bufferblock;
|
||||
if(!param.quiet) fprintf(stderr, "Note: raising buffer to minimal size %liKiB\n", (unsigned long) bufferbytes>>10);
|
||||
}
|
||||
bufferbytes -= bufferbytes % bufferblock;
|
||||
/* No +1024 for NtoM rounding problems anymore! */
|
||||
xfermem_init (&buffermem, bufferbytes ,0,0);
|
||||
sigemptyset (&newsigset);
|
||||
/* ThOr: I'm not quite sure why we need to block that signal here. */
|
||||
sigaddset (&newsigset, SIGUSR1);
|
||||
sigprocmask (SIG_BLOCK, &newsigset, &oldsigset);
|
||||
#if !defined(WIN32) && !defined(GENERIC)
|
||||
catchsignal (SIGCHLD, catch_child);
|
||||
#endif
|
||||
switch ((buffer_pid = fork()))
|
||||
{
|
||||
case -1: /* error */
|
||||
error("cannot fork!");
|
||||
return -1;
|
||||
case 0: /* child */
|
||||
{
|
||||
/* Buffer process handles all audio stuff itself. */
|
||||
audio_output_t *bao = NULL; /* To be clear: That's the buffer's pointer. */
|
||||
param.usebuffer = 0; /* The buffer doesn't use the buffer. */
|
||||
/* Open audio output module */
|
||||
bao = open_output_module(param.output_module);
|
||||
if(!bao)
|
||||
{
|
||||
error("Failed to open audio output module.");
|
||||
exit(1); /* communicate failure? */
|
||||
}
|
||||
if(open_output(bao) < 0)
|
||||
{
|
||||
error("Unable to open audio output.");
|
||||
close_output_module(bao);
|
||||
exit(2);
|
||||
}
|
||||
xfermem_init_reader (buffermem);
|
||||
buffer_loop(bao, &oldsigset); /* Here the work happens. */
|
||||
xfermem_done_reader (buffermem);
|
||||
xfermem_done (buffermem);
|
||||
close_output(bao);
|
||||
close_output_module(bao);
|
||||
exit(0);
|
||||
}
|
||||
default: /* parent */
|
||||
xfermem_init_writer (buffermem);
|
||||
}
|
||||
/* ThOr: I want that USR1 signal back for control. */
|
||||
sigprocmask(SIG_UNBLOCK, &newsigset, NULL);
|
||||
}
|
||||
#else
|
||||
if(param.usebuffer)
|
||||
{
|
||||
error("Buffer not available in this build!");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
if(!param.usebuffer)
|
||||
{ /* Only if I handle audio device output: Get that module. */
|
||||
*ao = open_output_module(param.output_module);
|
||||
if(!(*ao))
|
||||
{
|
||||
error("Failed to open audio output module");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else *ao = NULL; /* That ensures we won't try to free it later... */
|
||||
#ifndef NOXFERMEM
|
||||
if(param.usebuffer)
|
||||
{ /* Check if buffer is alive. */
|
||||
int res = xfermem_getcmd(buffermem->fd[XF_WRITER], TRUE);
|
||||
if(res < 0)
|
||||
{
|
||||
error("Buffer process didn't initialize!");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* This has internal protection for buffer mode. */
|
||||
if(open_output(*ao) < 0) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exit_output(audio_output_t *ao, int rude)
|
||||
{
|
||||
debug("exit output");
|
||||
#ifndef NOXFERMEM
|
||||
if (param.usebuffer)
|
||||
{
|
||||
debug("ending buffer");
|
||||
buffer_stop(); /* Puts buffer into waiting-for-command mode. */
|
||||
buffer_end(rude); /* Gives command to end operation. */
|
||||
xfermem_done_writer(buffermem);
|
||||
waitpid (buffer_pid, NULL, 0);
|
||||
xfermem_done (buffermem);
|
||||
}
|
||||
#endif
|
||||
/* Close the output... doesn't matter if buffer handled it, that's taken care of. */
|
||||
close_output(ao);
|
||||
close_output_module(ao);
|
||||
}
|
||||
|
||||
void output_pause(audio_output_t *ao)
|
||||
{
|
||||
if(param.usebuffer) buffer_stop();
|
||||
else ao->flush(ao);
|
||||
}
|
||||
|
||||
void output_unpause(audio_output_t *ao)
|
||||
{
|
||||
if(param.usebuffer) buffer_start();
|
||||
}
|
||||
|
||||
int flush_output(audio_output_t *ao, unsigned char *bytes, size_t count)
|
||||
{
|
||||
if(count)
|
||||
{
|
||||
/* Error checks? */
|
||||
#ifndef NOXFERMEM
|
||||
if(param.usebuffer){ if(xfermem_write(buffermem, bytes, count)) return -1; }
|
||||
else
|
||||
#endif
|
||||
if(param.outmode != DECODE_TEST)
|
||||
{
|
||||
int sum = 0;
|
||||
int written;
|
||||
do
|
||||
{ /* Be in a loop for SIGSTOP/CONT */
|
||||
written = ao->write(ao, bytes, (int)count);
|
||||
if(written >= 0){ sum+=written; count -= written; }
|
||||
else error1("Error in writing audio (%s?)!", strerror(errno));
|
||||
} while(count>0 && written>=0);
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
return (int)count; /* That is for DECODE_TEST */
|
||||
}
|
||||
|
||||
int open_output(audio_output_t *ao)
|
||||
{
|
||||
if(param.usebuffer) return 0;
|
||||
|
||||
if(ao == NULL)
|
||||
{
|
||||
error("ao should not be NULL here!");
|
||||
exit(110);
|
||||
}
|
||||
|
||||
ao->framesize = ao->channels * mpg123_encsize(ao->format);
|
||||
|
||||
switch(param.outmode)
|
||||
{
|
||||
case DECODE_AUDIO:
|
||||
case DECODE_WAV:
|
||||
case DECODE_AU:
|
||||
case DECODE_CDR:
|
||||
case DECODE_FILE:
|
||||
debug("opening normal audio/file");
|
||||
ao->is_open = ao->open(ao) < 0 ? FALSE : TRUE;
|
||||
if(!ao->is_open)
|
||||
{
|
||||
if(!AOQUIET) error("failed to open audio device");
|
||||
return -1;
|
||||
}
|
||||
else return 0;
|
||||
break;
|
||||
case DECODE_TEST:
|
||||
debug("decoding to nowhere");
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
debug("nothing");
|
||||
return -1; /* That's an error ... unknown outmode? */
|
||||
}
|
||||
|
||||
/* is this used? */
|
||||
void close_output(audio_output_t *ao)
|
||||
{
|
||||
if(param.usebuffer) return;
|
||||
|
||||
debug("closing output");
|
||||
switch(param.outmode)
|
||||
{
|
||||
case DECODE_AUDIO:
|
||||
case DECODE_WAV:
|
||||
case DECODE_AU:
|
||||
case DECODE_CDR:
|
||||
/* Guard that close call; could be nasty. */
|
||||
if(ao->is_open)
|
||||
{
|
||||
ao->is_open = FALSE;
|
||||
if(ao->close != NULL) ao->close(ao);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Also for WAV decoding? */
|
||||
int reset_output(audio_output_t *ao)
|
||||
{
|
||||
if(!param.usebuffer)
|
||||
{
|
||||
close_output(ao);
|
||||
return open_output(ao);
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
int set_pitch(mpg123_handle *fr, audio_output_t *ao, double new_pitch)
|
||||
{
|
||||
int ret = 1;
|
||||
double old_pitch = param.pitch;
|
||||
long rate;
|
||||
int channels, format;
|
||||
@@ -706,19 +204,13 @@ int set_pitch(mpg123_handle *fr, audio_output_t *ao, double new_pitch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(param.usebuffer)
|
||||
{
|
||||
error("No runtime pitch change with output buffer, sorry.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
param.pitch = new_pitch;
|
||||
if(param.pitch < -0.99) param.pitch = -0.99;
|
||||
|
||||
if(channels == 1) smode = MPG123_MONO;
|
||||
if(channels == 2) smode = MPG123_STEREO;
|
||||
|
||||
output_pause(ao);
|
||||
out123_stop(ao);
|
||||
/* Remember: This takes param.pitch into account. */
|
||||
audio_capabilities(ao, fr);
|
||||
if(!(mpg123_format_support(fr, rate, format) & smode))
|
||||
@@ -729,13 +221,6 @@ int set_pitch(mpg123_handle *fr, audio_output_t *ao, double new_pitch)
|
||||
error("Reached a hardware limit there with pitch!");
|
||||
param.pitch = old_pitch;
|
||||
audio_capabilities(ao, fr);
|
||||
ret = 0;
|
||||
}
|
||||
ao->format = format;
|
||||
ao->channels = channels;
|
||||
ao->rate = pitch_rate(rate);
|
||||
reset_output(ao);
|
||||
output_unpause(ao);
|
||||
return ret;
|
||||
return out123_start(ao, pitch_rate(rate), channels, format);
|
||||
}
|
||||
|
||||
|
83
src/audio.h
83
src/audio.h
@@ -1,7 +1,9 @@
|
||||
/*
|
||||
audio: audio output interface
|
||||
|
||||
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
This is what is left after separating out libout123.
|
||||
|
||||
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 Michael Hipp
|
||||
*/
|
||||
@@ -16,86 +18,17 @@
|
||||
|
||||
#include "compat.h"
|
||||
#include "mpg123.h"
|
||||
#include "module.h"
|
||||
|
||||
#define AUDIO_OUT_HEADPHONES 0x01
|
||||
#define AUDIO_OUT_INTERNAL_SPEAKER 0x02
|
||||
#define AUDIO_OUT_LINE_OUT 0x04
|
||||
|
||||
enum {
|
||||
DECODE_TEST,
|
||||
DECODE_AUDIO,
|
||||
DECODE_FILE,
|
||||
DECODE_BUFFER,
|
||||
DECODE_WAV,
|
||||
DECODE_AU,
|
||||
DECODE_CDR,
|
||||
DECODE_AUDIOFILE
|
||||
};
|
||||
|
||||
/* 3% rate tolerance */
|
||||
#define AUDIO_RATE_TOLERANCE 3
|
||||
|
||||
typedef struct audio_output_struct
|
||||
{
|
||||
int fn; /* filenumber */
|
||||
void *userptr; /* driver specific pointer */
|
||||
|
||||
/* Callbacks */
|
||||
int (*open)(struct audio_output_struct *);
|
||||
int (*get_formats)(struct audio_output_struct *);
|
||||
int (*write)(struct audio_output_struct *, unsigned char *,int);
|
||||
void (*flush)(struct audio_output_struct *);
|
||||
int (*close)(struct audio_output_struct *);
|
||||
int (*deinit)(struct audio_output_struct *);
|
||||
|
||||
/* the module this belongs to */
|
||||
mpg123_module_t *module;
|
||||
|
||||
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; /* format flags */
|
||||
int framesize; /* Output needs data in chunks of framesize bytes. */
|
||||
int is_open; /* something opened? */
|
||||
#define MPG123_OUT_QUIET 1
|
||||
int auxflags; /* For now just one: quiet mode (for probing). */
|
||||
} audio_output_t;
|
||||
|
||||
/* Lazy. */
|
||||
#define AOQUIET (ao->auxflags & MPG123_OUT_QUIET)
|
||||
|
||||
struct audio_format_name {
|
||||
int val;
|
||||
char *name;
|
||||
char *sname;
|
||||
};
|
||||
#include "out123.h"
|
||||
|
||||
#define pitch_rate(rate) (param.pitch == 0 ? (rate) : (long) ((param.pitch+1.0)*(rate)))
|
||||
|
||||
/* ------ Declarations from "audio.c" ------ */
|
||||
|
||||
audio_output_t* open_output_module( const char* name );
|
||||
void close_output_module( audio_output_t* ao );
|
||||
audio_output_t* alloc_audio_output();
|
||||
void audio_capabilities(audio_output_t *ao, mpg123_handle *mh);
|
||||
int audio_fit_capabilities(audio_output_t *ao,int c,int r);
|
||||
int audio_enc_name2code(const char* name);
|
||||
const char* audio_encoding_name(const int encoding, const int longer);
|
||||
void audio_enclist(char** list);
|
||||
|
||||
void audio_capabilities(audio_output_t *ao, mpg123_handle *mh);
|
||||
void print_capabilities(audio_output_t *ao, mpg123_handle *mh);
|
||||
|
||||
int init_output(audio_output_t **ao);
|
||||
void exit_output(audio_output_t *ao, int rude);
|
||||
int flush_output(audio_output_t *ao, unsigned char *bytes, size_t count);
|
||||
int open_output(audio_output_t *ao);
|
||||
void close_output(audio_output_t *ao );
|
||||
int reset_output(audio_output_t *ao);
|
||||
void output_pause(audio_output_t *ao); /* Prepare output for inactivity. */
|
||||
void output_unpause(audio_output_t *ao); /* Reactivate output (buffer process). */
|
||||
|
||||
void audio_enclist(char** list); /* Make a string of encoding names. */
|
||||
|
||||
/*
|
||||
Twiddle audio output rate to yield speedup/down (pitch) effect.
|
||||
The actually achieved pitch value is stored in param.pitch.
|
||||
|
316
src/buffer.c
316
src/buffer.c
@@ -1,316 +0,0 @@
|
||||
/*
|
||||
buffer.c: output buffer
|
||||
|
||||
copyright 1997-2009 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;-)
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
|
||||
#include "common.h"
|
||||
#include "sysutil.h"
|
||||
#include <errno.h>
|
||||
#include "debug.h"
|
||||
|
||||
int outburst = 32768;
|
||||
|
||||
static int intflag = FALSE;
|
||||
static int usr1flag = FALSE;
|
||||
|
||||
static void catch_interrupt (void)
|
||||
{
|
||||
intflag = TRUE;
|
||||
}
|
||||
|
||||
static void catch_usr1 (void)
|
||||
{
|
||||
usr1flag = TRUE;
|
||||
}
|
||||
|
||||
/* Interfaces to writer process */
|
||||
|
||||
extern void buffer_sig(int signal, int block);
|
||||
|
||||
void real_buffer_ignore_lowmem(void)
|
||||
{
|
||||
if (!buffermem)
|
||||
return;
|
||||
if(buffermem->wakeme[XF_READER])
|
||||
xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_WAKEUP);
|
||||
}
|
||||
|
||||
void real_buffer_end(int rude)
|
||||
{
|
||||
if (!buffermem)
|
||||
return;
|
||||
xfermem_putcmd(buffermem->fd[XF_WRITER], rude ? XF_CMD_ABORT : XF_CMD_TERMINATE);
|
||||
}
|
||||
|
||||
void real_buffer_resync(void)
|
||||
{
|
||||
if(buffermem->justwait)
|
||||
{
|
||||
buffermem->wakeme[XF_WRITER] = TRUE;
|
||||
xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_RESYNC);
|
||||
xfermem_getcmd(buffermem->fd[XF_WRITER], TRUE);
|
||||
}
|
||||
else buffer_sig(SIGINT, TRUE);
|
||||
}
|
||||
|
||||
void real_plain_buffer_resync(void)
|
||||
{
|
||||
buffer_sig(SIGINT, FALSE);
|
||||
}
|
||||
|
||||
void real_buffer_reset(void)
|
||||
{
|
||||
buffer_sig(SIGUSR1, TRUE);
|
||||
}
|
||||
|
||||
void real_buffer_start(void)
|
||||
{
|
||||
if(buffermem->justwait)
|
||||
{
|
||||
debug("ending buffer's waiting");
|
||||
buffermem->justwait = FALSE;
|
||||
xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_WAKEUP);
|
||||
}
|
||||
}
|
||||
|
||||
void real_buffer_stop()
|
||||
{
|
||||
buffermem->justwait = TRUE;
|
||||
buffer_sig(SIGINT, TRUE);
|
||||
}
|
||||
|
||||
extern int buffer_pid;
|
||||
|
||||
void buffer_sig(int signal, int block)
|
||||
{
|
||||
if (!buffermem) return;
|
||||
|
||||
if (!block)
|
||||
{ /* Just signal, do not wait for anything. */
|
||||
kill(buffer_pid, signal);
|
||||
return;
|
||||
}
|
||||
|
||||
/* kill() and the waiting needs to be taken care of properly for parallel execution.
|
||||
Nobody reported issues so far, but I want to be sure. */
|
||||
if(xfermem_sigblock(XF_WRITER, buffermem, buffer_pid, signal) != XF_CMD_WAKEUP)
|
||||
perror("Could not resync/reset buffers");
|
||||
return;
|
||||
}
|
||||
|
||||
void buffer_loop(audio_output_t *ao, sigset_t *oldsigset)
|
||||
{
|
||||
int bytes, outbytes;
|
||||
int my_fd = buffermem->fd[XF_READER];
|
||||
txfermem *xf = buffermem;
|
||||
int done = FALSE;
|
||||
int preload;
|
||||
|
||||
catchsignal (SIGINT, catch_interrupt);
|
||||
catchsignal (SIGUSR1, catch_usr1);
|
||||
sigprocmask (SIG_SETMASK, oldsigset, NULL);
|
||||
|
||||
xfermem_putcmd(my_fd, XF_CMD_WAKEUP);
|
||||
|
||||
debug("audio output: waiting for cap requests");
|
||||
/* wait for audio setup queries */
|
||||
while(1)
|
||||
{
|
||||
int cmd;
|
||||
cmd = xfermem_block(XF_READER, xf);
|
||||
if(cmd == XF_CMD_AUDIOCAP)
|
||||
{
|
||||
ao->rate = xf->rate;
|
||||
ao->channels = xf->channels;
|
||||
ao->format = ao->get_formats(ao);
|
||||
debug3("formats for %liHz/%ich: 0x%x", ao->rate, ao->channels, ao->format);
|
||||
xf->format = ao->format;
|
||||
xfermem_putcmd(my_fd, XF_CMD_AUDIOCAP);
|
||||
}
|
||||
else if(cmd == XF_CMD_WAKEUP)
|
||||
{
|
||||
debug("got wakeup... leaving config mode");
|
||||
xfermem_putcmd(buffermem->fd[XF_READER], XF_CMD_WAKEUP);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
error1("unexpected command %i", cmd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill complete buffer on first run before starting to play.
|
||||
* Live mp3 streams constantly approach buffer underrun otherwise. [dk]
|
||||
*/
|
||||
preload = (int)(param.preload*xf->size);
|
||||
if(preload > xf->size) preload = xf->size;
|
||||
if(preload < 0) preload = 0;
|
||||
|
||||
for (;;) {
|
||||
if (intflag) {
|
||||
debug("handle intflag... flushing");
|
||||
intflag = FALSE;
|
||||
ao->flush(ao);
|
||||
/* Either prepare for waiting or empty buffer now. */
|
||||
if(!xf->justwait) xf->readindex = xf->freeindex;
|
||||
else
|
||||
{
|
||||
int cmd;
|
||||
debug("Prepare for waiting; draining command queue. (There's a lot of wakeup commands pending, usually.)");
|
||||
do
|
||||
{
|
||||
cmd = xfermem_getcmd(my_fd, FALSE);
|
||||
/* debug1("drain: %i", cmd); */
|
||||
} while(cmd > 0);
|
||||
}
|
||||
if(xf->wakeme[XF_WRITER]) xfermem_putcmd(my_fd, XF_CMD_WAKEUP);
|
||||
}
|
||||
if (usr1flag) {
|
||||
debug("handling usr1flag");
|
||||
usr1flag = FALSE;
|
||||
/* close and re-open in order to flush
|
||||
* the device's internal buffer before
|
||||
* changing the sample rate. [OF]
|
||||
*/
|
||||
/* writer must block when sending SIGUSR1
|
||||
* or we will lose all data processed
|
||||
* in the meantime! [dk]
|
||||
*/
|
||||
xf->readindex = xf->freeindex;
|
||||
/* We've nailed down the new starting location -
|
||||
* writer is now safe to go on. [dk]
|
||||
*/
|
||||
if (xf->wakeme[XF_WRITER])
|
||||
xfermem_putcmd(my_fd, XF_CMD_WAKEUP);
|
||||
ao->rate = xf->rate;
|
||||
ao->channels = xf->channels;
|
||||
ao->format = xf->format;
|
||||
if (reset_output(ao) < 0) {
|
||||
error1("failed to reset audio: %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if ( (bytes = xfermem_get_usedspace(xf)) < outburst ) {
|
||||
/* if we got a buffer underrun we first
|
||||
* fill 1/8 of the buffer before continue/start
|
||||
* playing */
|
||||
if (preload < xf->size>>3)
|
||||
preload = xf->size>>3;
|
||||
if(preload < outburst)
|
||||
preload = outburst;
|
||||
}
|
||||
debug1("bytes: %i", bytes);
|
||||
if(xf->justwait || bytes < preload) {
|
||||
int cmd;
|
||||
if (done && !bytes) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(xf->justwait || !done) {
|
||||
|
||||
/* Don't spill into errno check below. */
|
||||
errno = 0;
|
||||
cmd = xfermem_block(XF_READER, xf);
|
||||
debug1("got %i", cmd);
|
||||
switch(cmd) {
|
||||
|
||||
/* More input pending. */
|
||||
case XF_CMD_WAKEUP_INFO:
|
||||
continue;
|
||||
/* Yes, we know buffer is low but
|
||||
* know we don't care.
|
||||
*/
|
||||
case XF_CMD_WAKEUP:
|
||||
break; /* Proceed playing. */
|
||||
case XF_CMD_ABORT: /* Immediate end, discard buffer contents. */
|
||||
return; /* Cleanup happens outside of buffer_loop()*/
|
||||
case XF_CMD_TERMINATE: /* Graceful end, playing stuff in buffer and then return. */
|
||||
debug("going to terminate");
|
||||
done = TRUE;
|
||||
break;
|
||||
case XF_CMD_RESYNC:
|
||||
debug("ordered resync");
|
||||
if (param.outmode == DECODE_AUDIO) ao->flush(ao);
|
||||
|
||||
xf->readindex = xf->freeindex;
|
||||
if (xf->wakeme[XF_WRITER]) xfermem_putcmd(my_fd, XF_CMD_WAKEUP);
|
||||
continue;
|
||||
break;
|
||||
case -1:
|
||||
if(intflag || usr1flag) /* Got signal, handle it at top of loop... */
|
||||
{
|
||||
debug("buffer interrupted");
|
||||
continue;
|
||||
}
|
||||
if(errno)
|
||||
error1("Yuck! Error in buffer handling... or somewhere unexpected: %s", strerror(errno));
|
||||
done = TRUE;
|
||||
xf->readindex = xf->freeindex;
|
||||
xfermem_putcmd(xf->fd[XF_READER], XF_CMD_TERMINATE);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "\nEh!? Received unknown command 0x%x in buffer process.\n", cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Hack! The writer issues XF_CMD_WAKEUP when first adjust
|
||||
* audio settings. We do not want to lower the preload mark
|
||||
* just yet!
|
||||
*/
|
||||
if (xf->justwait || !bytes)
|
||||
continue;
|
||||
preload = outburst; /* set preload to lower mark */
|
||||
if (bytes > xf->size - xf->readindex)
|
||||
bytes = xf->size - xf->readindex;
|
||||
if (bytes > outburst)
|
||||
bytes = outburst;
|
||||
|
||||
/* The output can only take multiples of framesize. */
|
||||
bytes -= bytes % ao->framesize;
|
||||
|
||||
debug("write");
|
||||
outbytes = flush_output(ao, (unsigned char*) xf->data + xf->readindex, bytes);
|
||||
|
||||
if(outbytes < bytes)
|
||||
{
|
||||
if(outbytes < 0) outbytes = 0;
|
||||
if(!intflag && !usr1flag) {
|
||||
error1("Ouch ... error while writing audio data: %s", strerror(errno));
|
||||
/*
|
||||
* done==TRUE tells writer process to stop
|
||||
* sending data. There might be some latency
|
||||
* involved when resetting readindex to
|
||||
* freeindex so we might need more than one
|
||||
* cycle to terminate. (The number of cycles
|
||||
* should be finite unless I managed to mess
|
||||
* up something. ;-) [dk]
|
||||
*/
|
||||
done = TRUE;
|
||||
xf->readindex = xf->freeindex;
|
||||
xfermem_putcmd(xf->fd[XF_READER], XF_CMD_TERMINATE);
|
||||
}
|
||||
else debug("buffer interrupted");
|
||||
}
|
||||
bytes = outbytes;
|
||||
|
||||
xf->readindex = (xf->readindex + bytes) % xf->size;
|
||||
if (xf->wakeme[XF_WRITER])
|
||||
xfermem_putcmd(my_fd, XF_CMD_WAKEUP);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* EOF */
|
45
src/buffer.h
45
src/buffer.h
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
buffer.h: output buffer
|
||||
|
||||
copyright 1999-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 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_
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
void real_buffer_ignore_lowmem(void);
|
||||
void real_buffer_end(int rude);
|
||||
void real_buffer_resync(void);
|
||||
void real_plain_buffer_resync(void);
|
||||
void real_buffer_reset(void);
|
||||
void real_buffer_start(void);
|
||||
void real_buffer_stop(void);
|
||||
/* Hm, that's funny preprocessor weirdness. */
|
||||
#define buffer_start() (param.usebuffer ? real_buffer_start(),0 : 0)
|
||||
#define buffer_stop() (param.usebuffer ? real_buffer_stop(),0 : 0)
|
||||
#define buffer_reset() (param.usebuffer ? real_buffer_reset(),0 : 0)
|
||||
#define buffer_resync() (param.usebuffer ? real_buffer_resync(),0 : 0)
|
||||
#define plain_buffer_resync() (param.usebuffer ? real_plain_buffer_resync(),0 : 0)
|
||||
#define buffer_end(a) (param.usebuffer ? real_buffer_end(a),0 : 0)
|
||||
#define buffer_ignore_lowmem() (param.usebuffer ? real_buffer_ignore_lowmem(),0 : 0)
|
||||
#else
|
||||
#define buffer_start()
|
||||
#define buffer_stop()
|
||||
#define buffer_reset()
|
||||
#define buffer_resync()
|
||||
#define plain_buffer_resync()
|
||||
#define buffer_end()
|
||||
#define buffer_ignore_lowmem()
|
||||
#endif
|
||||
|
||||
#endif
|
56
src/common.c
56
src/common.c
@@ -1,18 +1,19 @@
|
||||
/*
|
||||
common: misc stuff... audio flush, status display...
|
||||
|
||||
copyright ?-2008 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
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 Michael Hipp
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123.h"
|
||||
#include <sys/stat.h>
|
||||
#include "common.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
const char* rva_name[3] = { "off", "mix", "album" };
|
||||
const char* rva_name[3] = { "v", "m", "a" }; /* vanilla, mix, album */
|
||||
static const char *modes[5] = {"Stereo", "Joint-Stereo", "Dual-Channel", "Single-Channel", "Invalid" };
|
||||
static const char *smodes[5] = { "stereo", "joint-stereo", "dual-channel", "mono", "invalid" };
|
||||
static const char *layers[4] = { "Unknown" , "I", "II", "III" };
|
||||
@@ -25,7 +26,6 @@ static const int samples_per_frame[4][4] =
|
||||
{ -1,-1,-1,-1 }, /* Unknown */
|
||||
};
|
||||
|
||||
|
||||
/* concurring to print_rheader... here for control_generic */
|
||||
const char* remote_header_help = "S <mpeg-version> <layer> <sampling freq> <mode(stereo/mono/...)> <mode_ext> <framesize> <stereo> <copyright> <error_protected> <emphasis> <bitrate> <extension> <vbr(0/1=yes/no)>";
|
||||
void print_remote_header(mpg123_handle *mh)
|
||||
@@ -128,12 +128,43 @@ static void settle_time(double tim, unsigned long *times, char *sep)
|
||||
}
|
||||
}
|
||||
|
||||
void print_stat(mpg123_handle *fr, long offset, long buffsize)
|
||||
/* Print output buffer fill. */
|
||||
void print_buf(const char* prefix, audio_output_t *ao)
|
||||
{
|
||||
double tim[2];
|
||||
long rate;
|
||||
int framesize;
|
||||
double tim;
|
||||
unsigned long times[3];
|
||||
char timesep;
|
||||
size_t buffsize;
|
||||
|
||||
buffsize = out123_buffered(ao);
|
||||
if(out123_getformat(ao, &rate, NULL, NULL, &framesize))
|
||||
return;
|
||||
tim = (double)(buffsize/framesize)/rate;
|
||||
settle_time(tim, times, ×ep);
|
||||
fprintf( stderr, "\r%s[%02lu:%02lu%c%02lu]"
|
||||
, prefix, times[0], times[1], timesep, times[2] );
|
||||
}
|
||||
|
||||
/* Note about position info with buffering:
|
||||
Negative positions mean that the previous track is still playing from the
|
||||
buffer. It's a countdown. The frame counter always relates to the last
|
||||
decoded frame, what entered the buffer right now. */
|
||||
void print_stat(mpg123_handle *fr, long offset, audio_output_t *ao)
|
||||
{
|
||||
double tim[3];
|
||||
off_t rno, no;
|
||||
double basevol, realvol;
|
||||
char *icy;
|
||||
size_t buffsize;
|
||||
long rate;
|
||||
int framesize;
|
||||
|
||||
|
||||
buffsize = out123_buffered(ao);
|
||||
if(out123_getformat(ao, &rate, NULL, NULL, &framesize))
|
||||
return;
|
||||
#ifndef WIN32
|
||||
#ifndef GENERIC
|
||||
/* Only generate new stat line when stderr is ready... don't overfill... */
|
||||
@@ -156,22 +187,25 @@ void print_stat(mpg123_handle *fr, long offset, long buffsize)
|
||||
{
|
||||
int ti;
|
||||
/* Deal with overly long times. */
|
||||
unsigned long times[2][3];
|
||||
char timesep[2];
|
||||
char sign[2] = {' ', ' '};
|
||||
for(ti=0; ti<2; ++ti)
|
||||
unsigned long times[3][3];
|
||||
char timesep[3];
|
||||
char sign[3] = {' ', ' ', ' '};
|
||||
tim[2] = (double)(buffsize/framesize)/rate;
|
||||
for(ti=0; ti<3; ++ti)
|
||||
{
|
||||
if(tim[ti] < 0.){ sign[ti] = '-'; tim[ti] = -tim[ti]; }
|
||||
settle_time(tim[ti], times[ti], ×ep[ti]);
|
||||
}
|
||||
fprintf(stderr, "\rFrame# %5"OFF_P" [%5"OFF_P"], Time:%c%02lu:%02lu%c%02lu%c[%02lu:%02lu%c%02lu], RVA:%6s, Vol: %3u(%3u)",
|
||||
fprintf(stderr, "\rf# %5"OFF_P"[%5"OFF_P"] %c%02lu:%02lu%c%02lu[%c%02lu:%02lu%c%02lu] V(%s)=%3u(%3u)",
|
||||
(off_p)no, (off_p)rno,
|
||||
sign[0],
|
||||
times[0][0], times[0][1], timesep[0], times[0][2],
|
||||
sign[1],
|
||||
times[1][0], times[1][1], timesep[1], times[1][2],
|
||||
rva_name[param.rva], roundui(basevol*100), roundui(realvol*100) );
|
||||
if(param.usebuffer) fprintf(stderr,", [%8ld] ",(long)buffsize);
|
||||
if(param.usebuffer)
|
||||
fprintf( stderr," [%02lu:%02lu%c%02lu] "
|
||||
, times[2][0], times[2][1], timesep[2], times[2][2] );
|
||||
}
|
||||
/* Check for changed tags here too? */
|
||||
if( mpg123_meta_check(fr) & MPG123_NEW_ICY && MPG123_OK == mpg123_icy(fr, &icy) )
|
||||
|
@@ -13,7 +13,7 @@
|
||||
|
||||
void print_header(mpg123_handle *);
|
||||
void print_header_compact(mpg123_handle *);
|
||||
void print_stat(mpg123_handle *fr, long offset, long buffsize);
|
||||
void print_stat(mpg123_handle *fr, long offset, audio_output_t *ao);
|
||||
void clear_stat();
|
||||
/* for control_generic */
|
||||
extern const char* remote_header_help;
|
||||
|
13
src/compat.c
Normal file
13
src/compat.c
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
Just to ensure that libraries and programs get their separate
|
||||
compatibility object. There should be a compatibility library,
|
||||
I presume, but I don't want to create another glib, I just want
|
||||
some internal functions to ease coding.
|
||||
|
||||
I'll sort it out properly sometime.
|
||||
|
||||
I smell symbol conflicts, anyway. Actually wondering why it
|
||||
worked so far.
|
||||
*/
|
||||
|
||||
#include "compat/compat_impl.h"
|
@@ -15,7 +15,13 @@
|
||||
#define MPG123_COMPAT_H
|
||||
|
||||
#include "config.h"
|
||||
#include "intsym.h"
|
||||
|
||||
/* Needed for strdup(), in strict mode ... */
|
||||
#ifndef _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE 500
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
/* realloc, size_t */
|
||||
@@ -63,6 +69,9 @@
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
#ifdef OS2
|
||||
#include <float.h>
|
||||
@@ -133,6 +142,7 @@ typedef long ssize_p;
|
||||
* @return file descriptor (>=0) or error code.
|
||||
*/
|
||||
int compat_open(const char *filename, int flags);
|
||||
FILE* compat_fopen(const char *filename, const char *mode);
|
||||
|
||||
/**
|
||||
* Closing a file handle can be platform specific.
|
||||
@@ -141,6 +151,7 @@ int compat_open(const char *filename, int flags);
|
||||
* @return 0 if the file was successfully closed. A return value of -1 indicates an error.
|
||||
*/
|
||||
int compat_close(int infd);
|
||||
int compat_fclose(FILE* stream);
|
||||
|
||||
/* Those do make sense in a separate file, but I chose to include them in compat.c because that's the one source whose object is shared between mpg123 and libmpg123 -- and both need the functionality internally. */
|
||||
|
||||
@@ -173,6 +184,12 @@ int win32_wide_utf8(const wchar_t * const wptr, char **mbptr, size_t * buflen);
|
||||
int win32_utf8_wide(const char *const mbptr, wchar_t **wptr, size_t *buflen);
|
||||
#endif
|
||||
|
||||
/* Blocking write/read of data with signal resilience.
|
||||
Both continue after being interrupted by signals and always return the
|
||||
amount of processed data (shortage indicating actual problem or EOF). */
|
||||
size_t unintr_write(int fd, void const *buffer, size_t bytes);
|
||||
size_t unintr_read (int fd, void *buffer, size_t bytes);
|
||||
|
||||
/* That one comes from Tellie on OS/2, needed in resolver. */
|
||||
#ifdef __KLIBC__
|
||||
typedef int socklen_t;
|
||||
@@ -186,4 +203,8 @@ typedef int socklen_t;
|
||||
|
||||
#include "true.h"
|
||||
|
||||
#if (!defined(WIN32) || defined (__CYGWIN__)) && defined(HAVE_SIGNAL_H)
|
||||
void (*catchsignal(int signum, void(*handler)()))();
|
||||
#endif
|
||||
|
||||
#endif
|
244
src/compat/compat_impl.h
Normal file
244
src/compat/compat_impl.h
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
compat: Some compatibility functions.
|
||||
|
||||
The mpg123 code is determined to keep it's legacy. A legacy of old, old UNIX.
|
||||
So anything possibly somewhat advanced should be considered to be put here, with proper #ifdef;-)
|
||||
|
||||
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 Thomas Orgis, Windows Unicode stuff by JonY.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "compat.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef WANT_WIN32_UNICODE
|
||||
#include <wchar.h>
|
||||
#include <windows.h>
|
||||
#include <winnls.h>
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
/* A safe realloc also for very old systems where realloc(NULL, size) returns NULL. */
|
||||
void *safe_realloc(void *ptr, size_t size)
|
||||
{
|
||||
if(ptr == NULL) return malloc(size);
|
||||
else return realloc(ptr, size);
|
||||
}
|
||||
|
||||
#ifndef HAVE_STRERROR
|
||||
const char *strerror(int errnum)
|
||||
{
|
||||
extern int sys_nerr;
|
||||
extern char *sys_errlist[];
|
||||
|
||||
return (errnum < sys_nerr) ? sys_errlist[errnum] : "";
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRDUP
|
||||
char *strdup(const char *src)
|
||||
{
|
||||
char *dest;
|
||||
|
||||
if (!(dest = (char *) malloc(strlen(src)+1)))
|
||||
return NULL;
|
||||
else
|
||||
return strcpy(dest, src);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Always add a default permission mask in case of flags|O_CREAT. */
|
||||
int compat_open(const char *filename, int flags)
|
||||
{
|
||||
int ret;
|
||||
#if defined (WANT_WIN32_UNICODE)
|
||||
wchar_t *frag = NULL;
|
||||
|
||||
ret = win32_utf8_wide(filename, &frag, NULL);
|
||||
/* Fallback to plain open when ucs-2 conversion fails */
|
||||
if((frag == NULL) || (ret == 0))
|
||||
goto open_fallback;
|
||||
|
||||
/*Try _wopen */
|
||||
ret = _wopen(frag, flags|_O_BINARY, _S_IREAD | _S_IWRITE);
|
||||
if(ret != -1 )
|
||||
goto open_ok; /* msdn says -1 means failure */
|
||||
|
||||
open_fallback:
|
||||
#endif
|
||||
|
||||
#if (defined(WIN32) && !defined (__CYGWIN__))
|
||||
/* MSDN says POSIX function is deprecated beginning in Visual C++ 2005 */
|
||||
/* Try plain old _open(), if it fails, do nothing */
|
||||
ret = _open(filename, flags|_O_BINARY, _S_IREAD | _S_IWRITE);
|
||||
#else
|
||||
ret = open(filename, flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
|
||||
#endif
|
||||
|
||||
#if defined (WANT_WIN32_UNICODE)
|
||||
open_ok:
|
||||
/* A cast to void*? Does Windows need that?! */
|
||||
free((void *)frag);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Moved over from wav.c, logic with fallbacks added from the
|
||||
example of compat_open(). */
|
||||
FILE* compat_fopen(const char *filename, const char *mode)
|
||||
{
|
||||
FILE* stream = NULL;
|
||||
#ifdef WANT_WIN32_UNICODE
|
||||
int cnt = 0;
|
||||
wchar_t *wname = NULL;
|
||||
wchat_t *wmode = NULL;
|
||||
|
||||
cnt = win32_utf8_wide(filename, &wname, NULL);
|
||||
if( (filenamew == NULL) || (cnt == 0))
|
||||
goto fopen_fallback;
|
||||
cnt = win32_utf8_wide(mode, &wmode, NULL);
|
||||
if( (wmode == NULL) || (cnt == 0))
|
||||
goto fopen_fallback;
|
||||
|
||||
stream = _wfopen(filenamew, wmode);
|
||||
if(stream) goto fopen_ok;
|
||||
|
||||
fopen_fallback:
|
||||
#endif
|
||||
#if (defined(WIN32) && !defined (__CYGWIN__))
|
||||
stream = _fopen(filename, mode);
|
||||
#else
|
||||
stream = fopen(filename, mode);
|
||||
#endif
|
||||
#ifdef WANT_WIN32_UNICODE
|
||||
|
||||
fopen_ok:
|
||||
free(wmode);
|
||||
free(wname);
|
||||
#endif
|
||||
return stream;
|
||||
}
|
||||
|
||||
int compat_close(int infd)
|
||||
{
|
||||
#if (defined(WIN32) && !defined (__CYGWIN__)) /* MSDN says POSIX function is deprecated beginning in Visual C++ 2005 */
|
||||
return _close(infd);
|
||||
#else
|
||||
return close(infd);
|
||||
#endif
|
||||
}
|
||||
|
||||
int compat_fclose(FILE *stream)
|
||||
{
|
||||
#if (defined(WIN32) && !defined (__CYGWIN__))
|
||||
return _fclose(stream);
|
||||
#else
|
||||
return fclose(stream);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Windows Unicode stuff */
|
||||
|
||||
#ifdef WANT_WIN32_UNICODE
|
||||
int win32_wide_utf8(const wchar_t * const wptr, char **mbptr, size_t * buflen)
|
||||
{
|
||||
size_t len;
|
||||
char *buf;
|
||||
int ret = 0;
|
||||
|
||||
len = WideCharToMultiByte(CP_UTF8, 0, wptr, -1, NULL, 0, NULL, NULL); /* Get utf-8 string length */
|
||||
buf = calloc(len + 1, sizeof (char)); /* Can we assume sizeof char always = 1? */
|
||||
|
||||
if(!buf) len = 0;
|
||||
else {
|
||||
if (len != 0) ret = WideCharToMultiByte(CP_UTF8, 0, wptr, -1, buf, len, NULL, NULL); /*Do actual conversion*/
|
||||
buf[len] = '0'; /* Must terminate */
|
||||
}
|
||||
*mbptr = buf; /* Set string pointer to allocated buffer */
|
||||
if(buflen != NULL) *buflen = (len) * sizeof (char); /* Give length of allocated memory if needed. */
|
||||
return ret;
|
||||
}
|
||||
|
||||
int win32_utf8_wide(const char *const mbptr, wchar_t **wptr, size_t *buflen)
|
||||
{
|
||||
size_t len;
|
||||
wchar_t *buf;
|
||||
int ret = 0;
|
||||
|
||||
len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mbptr, -1, NULL, 0); /* Get converted size */
|
||||
buf = calloc(len + 1, sizeof (wchar_t)); /* Allocate memory accordingly */
|
||||
|
||||
if(!buf) len = 0;
|
||||
else {
|
||||
if (len != 0) ret = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, mbptr, -1, buf, len); /* Do conversion */
|
||||
buf[len] = L'0'; /* Must terminate */
|
||||
}
|
||||
*wptr = buf; /* Set string pointer to allocated buffer */
|
||||
if (buflen != NULL) *buflen = len * sizeof (wchar_t); /* Give length of allocated memory if needed. */
|
||||
return ret; /* Number of characters written */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* This shall survive signals and any return value less than given byte count
|
||||
is an error */
|
||||
size_t unintr_write(int fd, void const *buffer, size_t bytes)
|
||||
{
|
||||
size_t written = 0;
|
||||
while(bytes)
|
||||
{
|
||||
ssize_t part = write(fd, (char*)buffer+written, bytes);
|
||||
if(part < 0 && errno != EINTR)
|
||||
break;
|
||||
bytes -= part;
|
||||
written += part;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
/* Same for reading the data. */
|
||||
size_t unintr_read(int fd, void *buffer, size_t bytes)
|
||||
{
|
||||
size_t got = 0;
|
||||
while(bytes)
|
||||
{
|
||||
ssize_t part = read(fd, (char*)buffer+got, bytes);
|
||||
if(part < 0 && errno != EINTR)
|
||||
break;
|
||||
bytes -= part;
|
||||
got += part;
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
#ifndef NO_CATCHSIGNAL
|
||||
#if (!defined(WIN32) || defined (__CYGWIN__)) && defined(HAVE_SIGNAL_H)
|
||||
void (*catchsignal(int signum, void(*handler)()))()
|
||||
{
|
||||
struct sigaction new_sa;
|
||||
struct sigaction old_sa;
|
||||
|
||||
#ifdef DONT_CATCH_SIGNALS
|
||||
fprintf (stderr, "Not catching any signals.\n");
|
||||
return ((void (*)()) -1);
|
||||
#endif
|
||||
|
||||
new_sa.sa_handler = handler;
|
||||
sigemptyset(&new_sa.sa_mask);
|
||||
new_sa.sa_flags = 0;
|
||||
if(sigaction(signum, &new_sa, &old_sa) == -1)
|
||||
return ((void (*)()) -1);
|
||||
return (old_sa.sa_handler);
|
||||
}
|
||||
#endif
|
||||
#endif
|
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123.h"
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#if !defined (WIN32) || defined (__CYGWIN__)
|
||||
@@ -18,14 +19,12 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "buffer.h"
|
||||
#include "genre.h"
|
||||
#include "playlist.h"
|
||||
#define MODE_STOPPED 0
|
||||
#define MODE_PLAYING 1
|
||||
#define MODE_PAUSED 2
|
||||
|
||||
extern int buffer_pid;
|
||||
extern audio_output_t *ao;
|
||||
|
||||
#ifdef FIFO
|
||||
@@ -100,7 +99,7 @@ void generic_sendstat (mpg123_handle *fr)
|
||||
{
|
||||
off_t current_frame, frames_left;
|
||||
double current_seconds, seconds_left;
|
||||
if(!mpg123_position(fr, 0, xfermem_get_usedspace(buffermem), ¤t_frame, &frames_left, ¤t_seconds, &seconds_left))
|
||||
if(!mpg123_position(fr, 0, out123_buffered(ao), ¤t_frame, &frames_left, ¤t_seconds, &seconds_left))
|
||||
generic_sendmsg("F %"OFF_P" %"OFF_P" %3.2f %3.2f", (off_p)current_frame, (off_p)frames_left, current_seconds, seconds_left);
|
||||
}
|
||||
|
||||
@@ -213,11 +212,7 @@ void generic_sendinfo (char *filename)
|
||||
|
||||
static void generic_load(mpg123_handle *fr, char *arg, int state)
|
||||
{
|
||||
if(param.usebuffer)
|
||||
{
|
||||
buffer_resync();
|
||||
if(mode == MODE_PAUSED && state != MODE_PAUSED) buffer_start();
|
||||
}
|
||||
out123_drop(ao);
|
||||
if(mode != MODE_STOPPED)
|
||||
{
|
||||
close_track();
|
||||
@@ -359,7 +354,7 @@ int control_generic (mpg123_handle *fr)
|
||||
{
|
||||
mode = MODE_PAUSED;
|
||||
/* Hm, buffer should be stopped already, shouldn't it? */
|
||||
if(param.usebuffer) buffer_stop();
|
||||
if(param.usebuffer) out123_pause(ao);
|
||||
generic_sendmsg("P 1");
|
||||
}
|
||||
else
|
||||
@@ -469,11 +464,11 @@ int control_generic (mpg123_handle *fr)
|
||||
{
|
||||
if (mode == MODE_PLAYING) {
|
||||
mode = MODE_PAUSED;
|
||||
if(param.usebuffer) buffer_stop();
|
||||
out123_pause(ao);
|
||||
generic_sendmsg("P 1");
|
||||
} else {
|
||||
mode = MODE_PLAYING;
|
||||
if(param.usebuffer) buffer_start();
|
||||
out123_continue(ao);
|
||||
generic_sendmsg("P 2");
|
||||
}
|
||||
} else generic_sendmsg("P 0");
|
||||
@@ -483,11 +478,9 @@ int control_generic (mpg123_handle *fr)
|
||||
/* STOP */
|
||||
if (!strcasecmp(comstr, "S") || !strcasecmp(comstr, "STOP")) {
|
||||
if (mode != MODE_STOPPED) {
|
||||
if(param.usebuffer)
|
||||
{
|
||||
buffer_stop();
|
||||
buffer_resync();
|
||||
}
|
||||
/* Do we want to drop here? */
|
||||
out123_drop(ao);
|
||||
out123_stop(ao);
|
||||
close_track();
|
||||
mode = MODE_STOPPED;
|
||||
generic_sendmsg("P 0");
|
||||
@@ -694,7 +687,7 @@ int control_generic (mpg123_handle *fr)
|
||||
generic_sendmsg("E Error while seeking: %s", mpg123_strerror(fr));
|
||||
mpg123_seek(fr, 0, SEEK_SET);
|
||||
}
|
||||
if(param.usebuffer) buffer_resync();
|
||||
out123_drop(ao);
|
||||
|
||||
newpos = mpg123_tell(fr);
|
||||
if(newpos <= oldpos) mpg123_meta_free(fr);
|
||||
@@ -728,7 +721,7 @@ int control_generic (mpg123_handle *fr)
|
||||
generic_sendmsg("E Error while seeking");
|
||||
mpg123_seek_frame(fr, 0, SEEK_SET);
|
||||
}
|
||||
if(param.usebuffer) buffer_resync();
|
||||
out123_drop(ao);
|
||||
|
||||
if(framenum <= oldpos) mpg123_meta_free(fr);
|
||||
generic_sendmsg("J %d", framenum);
|
||||
|
@@ -1,197 +0,0 @@
|
||||
## Makefile.am: produce Makefile.in from this
|
||||
|
||||
## copyright 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
|
||||
|
||||
#AM_CFLAGS = @AUDIO_CFLAGS@
|
||||
#AM_LDFLAGS =
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/libmpg123
|
||||
|
||||
EXTRA_DIST = mpg123.h.in
|
||||
|
||||
LIBS = @LIBMPG123_LIBS@
|
||||
|
||||
EXTRA_PROGRAMS = testcpu
|
||||
testcpu_dependencies = getcpuflags.$(OBJEXT)
|
||||
testcpu_sources = testcpu.c
|
||||
testcpu_LDADD = getcpuflags.$(OBJEXT)
|
||||
|
||||
|
||||
CLEANFILES = *.a
|
||||
|
||||
|
||||
# The library can have different names, depending on largefile setup.
|
||||
# Libtool macros think they're smart. Because of that mpg123.la does not work, it must be libmpg123.la .
|
||||
lib_LTLIBRARIES = libmpg123.la
|
||||
nodist_include_HEADERS = mpg123.h
|
||||
|
||||
libmpg123_la_LDFLAGS = -no-undefined -version-info @LIBMPG123_VERSION@ -export-symbols-regex '^mpg123_'
|
||||
libmpg123_la_LIBADD = @DECODER_LOBJ@ @LFS_LOBJ@
|
||||
libmpg123_la_DEPENDENCIES = @DECODER_LOBJ@ @LFS_LOBJ@
|
||||
|
||||
libmpg123_la_SOURCES = \
|
||||
intsym.h \
|
||||
compat.c \
|
||||
compat.h \
|
||||
mpeghead.h \
|
||||
parse.c \
|
||||
parse.h \
|
||||
frame.c \
|
||||
format.c \
|
||||
frame.h \
|
||||
reader.h \
|
||||
debug.h \
|
||||
decode.h \
|
||||
sample.h \
|
||||
dct64.c \
|
||||
synth.h \
|
||||
synth_mono.h \
|
||||
synth_ntom.h \
|
||||
synth_8bit.h \
|
||||
synths.h \
|
||||
equalizer.c \
|
||||
huffman.h \
|
||||
newhuffman.h \
|
||||
icy.h \
|
||||
icy2utf8.h \
|
||||
id3.h \
|
||||
id3.c \
|
||||
true.h \
|
||||
getbits.h \
|
||||
optimize.h \
|
||||
optimize.c \
|
||||
readers.c \
|
||||
tabinit.c \
|
||||
libmpg123.c \
|
||||
gapless.h \
|
||||
mpg123lib_intern.h \
|
||||
abi_align.h \
|
||||
mangle.h \
|
||||
getcpuflags.h \
|
||||
index.h \
|
||||
index.c
|
||||
|
||||
EXTRA_libmpg123_la_SOURCES = \
|
||||
lfs_alias.c \
|
||||
lfs_wrap.c \
|
||||
icy.c \
|
||||
icy2utf8.c \
|
||||
l2tables.h \
|
||||
layer1.c \
|
||||
layer2.c \
|
||||
layer3.c \
|
||||
dither.h \
|
||||
dither.c \
|
||||
feature.c \
|
||||
dct36_3dnowext.S \
|
||||
dct36_3dnow.S \
|
||||
dct36_sse.S \
|
||||
dct36_x86_64.S \
|
||||
dct36_avx.S \
|
||||
dct36_neon.S \
|
||||
dct36_neon64.S \
|
||||
dct64_3dnowext.S \
|
||||
dct64_3dnow.S \
|
||||
dct64_altivec.c \
|
||||
dct64_i386.c \
|
||||
dct64_i486.c \
|
||||
dct64_mmx.S \
|
||||
dct64_sse.S \
|
||||
dct64_sse_float.S \
|
||||
dct64_x86_64.S \
|
||||
dct64_x86_64_float.S \
|
||||
dct64_neon.S \
|
||||
dct64_neon_float.S \
|
||||
dct64_neon64.S \
|
||||
dct64_neon64_float.S \
|
||||
dct64_avx.S \
|
||||
dct64_avx_float.S \
|
||||
synth_3dnowext.S \
|
||||
synth_3dnow.S \
|
||||
synth_altivec.c \
|
||||
synth_i486.c \
|
||||
synth_i586_dither.S \
|
||||
synth_i586.S \
|
||||
synth_mmx.S \
|
||||
synth_sse3d.h \
|
||||
synth_sse.S \
|
||||
synth_sse_float.S \
|
||||
synth_sse_s32.S \
|
||||
synth_sse_accurate.S \
|
||||
synth_stereo_sse_float.S \
|
||||
synth_stereo_sse_s32.S \
|
||||
synth_stereo_sse_accurate.S \
|
||||
synth_x86_64.S \
|
||||
synth_x86_64_float.S \
|
||||
synth_x86_64_s32.S \
|
||||
synth_x86_64_accurate.S \
|
||||
synth_stereo_x86_64.S \
|
||||
synth_stereo_x86_64_float.S \
|
||||
synth_stereo_x86_64_s32.S \
|
||||
synth_stereo_x86_64_accurate.S \
|
||||
synth_arm.S \
|
||||
synth_arm_accurate.S \
|
||||
synth_neon.S \
|
||||
synth_neon_float.S \
|
||||
synth_neon_s32.S \
|
||||
synth_neon_accurate.S \
|
||||
synth_stereo_neon.S \
|
||||
synth_stereo_neon_float.S \
|
||||
synth_stereo_neon_s32.S \
|
||||
synth_stereo_neon_accurate.S \
|
||||
synth_neon64.S \
|
||||
synth_neon64_float.S \
|
||||
synth_neon64_s32.S \
|
||||
synth_neon64_accurate.S \
|
||||
synth_stereo_neon64.S \
|
||||
synth_stereo_neon64_float.S \
|
||||
synth_stereo_neon64_s32.S \
|
||||
synth_stereo_neon64_accurate.S \
|
||||
synth_stereo_avx.S \
|
||||
synth_stereo_avx_float.S \
|
||||
synth_stereo_avx_s32.S \
|
||||
synth_stereo_avx_accurate.S \
|
||||
ntom.c \
|
||||
synth.c \
|
||||
synth_8bit.c \
|
||||
synth_real.c \
|
||||
synth_s32.c \
|
||||
equalizer_3dnow.S \
|
||||
tabinit_mmx.S \
|
||||
stringbuf.c \
|
||||
getcpuflags.S \
|
||||
getcpuflags_x86_64.S \
|
||||
getcpuflags_arm.c \
|
||||
check_neon.S \
|
||||
l12_integer_tables.h \
|
||||
l3_integer_tables.h
|
||||
|
||||
if USE_YASM_FOR_AVX
|
||||
## Override rules for the sources that should be assembled with yasm
|
||||
|
||||
AVX_SRCS = \
|
||||
dct36_avx.S \
|
||||
dct64_avx.S \
|
||||
dct64_avx_float.S \
|
||||
synth_stereo_avx.S \
|
||||
synth_stereo_avx_float.S \
|
||||
synth_stereo_avx_s32.S \
|
||||
synth_stereo_avx_accurate.S
|
||||
|
||||
AVX_OBJS = $(AVX_SRCS:.S=.@OBJEXT@)
|
||||
|
||||
ASM_DEPS = \
|
||||
mangle.h \
|
||||
$(top_builddir)/src/config.h \
|
||||
intsym.h
|
||||
|
||||
$(AVX_OBJS): %.@OBJEXT@: %.S $(ASM_DEPS)
|
||||
$(CPP) $(DEFAULT_INCLUDES) $(INCLUDES) -DASMALIGN_BALIGN $< | @YASM@ - @YASMFLAGS@ @YASM_FORMAT@ -o $@
|
||||
|
||||
$(AVX_OBJS:.@OBJEXT@=.lo): %.lo: %.@OBJEXT@
|
||||
@echo "# Generated by ltmain.sh (GNU libtool)" >$@
|
||||
@echo "pic_object='$<'" >>$@
|
||||
@echo "non_pic_object='$<'" >>$@
|
||||
|
||||
endif
|
192
src/libmpg123/Makemodule.am
Normal file
192
src/libmpg123/Makemodule.am
Normal file
@@ -0,0 +1,192 @@
|
||||
# Module for non-recursive mpg123 build system.
|
||||
|
||||
EXTRA_DIST += src/libmpg123/mpg123.h.in
|
||||
|
||||
EXTRA_PROGRAMS += src/libmpg123/testcpu
|
||||
src_libmpg123_testcpu_DEPENDENCIES = src/libmpg123/getcpuflags.$(OBJEXT)
|
||||
src_libmpg123_testcpu_SOURCES = src/libmpg123/testcpu.c
|
||||
src_libmpg123_testcpu_LDADD = src/libmpg123/getcpuflags.$(OBJEXT)
|
||||
|
||||
# Necessary?
|
||||
CLEANFILES += src/libmpg123/*.a
|
||||
|
||||
# The library can have different names, depending on largefile setup.
|
||||
# Libtool macros think they're smart. Because of that mpg123.la
|
||||
# does not work, it must be libmpg123.la .
|
||||
lib_LTLIBRARIES += src/libmpg123/libmpg123.la
|
||||
nodist_include_HEADERS += src/libmpg123/mpg123.h
|
||||
|
||||
src_libmpg123_libmpg123_la_LDFLAGS = \
|
||||
-no-undefined \
|
||||
-version-info @LIBMPG123_VERSION@ \
|
||||
-export-symbols-regex '^mpg123_'
|
||||
src_libmpg123_libmpg123_la_LIBADD = @DECODER_LOBJ@ @LFS_LOBJ@ @LIBS@
|
||||
src_libmpg123_libmpg123_la_DEPENDENCIES = @DECODER_LOBJ@ @LFS_LOBJ@
|
||||
|
||||
src_libmpg123_libmpg123_la_SOURCES = \
|
||||
src/libmpg123/intsym.h \
|
||||
src/libmpg123/compat.c \
|
||||
src/compat/compat.h \
|
||||
src/compat/compat_impl.h \
|
||||
src/libmpg123/mpeghead.h \
|
||||
src/libmpg123/parse.c \
|
||||
src/libmpg123/parse.h \
|
||||
src/libmpg123/frame.c \
|
||||
src/libmpg123/format.c \
|
||||
src/libmpg123/frame.h \
|
||||
src/libmpg123/reader.h \
|
||||
src/libmpg123/debug.h \
|
||||
src/libmpg123/decode.h \
|
||||
src/libmpg123/sample.h \
|
||||
src/libmpg123/dct64.c \
|
||||
src/libmpg123/synth.h \
|
||||
src/libmpg123/synth_mono.h \
|
||||
src/libmpg123/synth_ntom.h \
|
||||
src/libmpg123/synth_8bit.h \
|
||||
src/libmpg123/synths.h \
|
||||
src/libmpg123/equalizer.c \
|
||||
src/libmpg123/huffman.h \
|
||||
src/libmpg123/newhuffman.h \
|
||||
src/libmpg123/icy.h \
|
||||
src/libmpg123/icy2utf8.h \
|
||||
src/libmpg123/id3.h \
|
||||
src/libmpg123/id3.c \
|
||||
src/libmpg123/true.h \
|
||||
src/libmpg123/getbits.h \
|
||||
src/libmpg123/optimize.h \
|
||||
src/libmpg123/optimize.c \
|
||||
src/libmpg123/readers.c \
|
||||
src/libmpg123/tabinit.c \
|
||||
src/libmpg123/libmpg123.c \
|
||||
src/libmpg123/gapless.h \
|
||||
src/libmpg123/mpg123lib_intern.h \
|
||||
src/libmpg123/abi_align.h \
|
||||
src/libmpg123/mangle.h \
|
||||
src/libmpg123/getcpuflags.h \
|
||||
src/libmpg123/index.h \
|
||||
src/libmpg123/index.c
|
||||
|
||||
EXTRA_src_libmpg123_libmpg123_la_SOURCES = \
|
||||
src/libmpg123/lfs_alias.c \
|
||||
src/libmpg123/lfs_wrap.c \
|
||||
src/libmpg123/icy.c \
|
||||
src/libmpg123/icy2utf8.c \
|
||||
src/libmpg123/l2tables.h \
|
||||
src/libmpg123/layer1.c \
|
||||
src/libmpg123/layer2.c \
|
||||
src/libmpg123/layer3.c \
|
||||
src/libmpg123/dither.h \
|
||||
src/libmpg123/dither_impl.h \
|
||||
src/libmpg123/dither.c \
|
||||
src/libmpg123/feature.c \
|
||||
src/libmpg123/dct36_3dnowext.S \
|
||||
src/libmpg123/dct36_3dnow.S \
|
||||
src/libmpg123/dct36_sse.S \
|
||||
src/libmpg123/dct36_x86_64.S \
|
||||
src/libmpg123/dct36_avx.S \
|
||||
src/libmpg123/dct36_neon.S \
|
||||
src/libmpg123/dct36_neon64.S \
|
||||
src/libmpg123/dct64_3dnowext.S \
|
||||
src/libmpg123/dct64_3dnow.S \
|
||||
src/libmpg123/dct64_altivec.c \
|
||||
src/libmpg123/dct64_i386.c \
|
||||
src/libmpg123/dct64_i486.c \
|
||||
src/libmpg123/dct64_mmx.S \
|
||||
src/libmpg123/dct64_sse.S \
|
||||
src/libmpg123/dct64_sse_float.S \
|
||||
src/libmpg123/dct64_x86_64.S \
|
||||
src/libmpg123/dct64_x86_64_float.S \
|
||||
src/libmpg123/dct64_neon.S \
|
||||
src/libmpg123/dct64_neon_float.S \
|
||||
src/libmpg123/dct64_neon64.S \
|
||||
src/libmpg123/dct64_neon64_float.S \
|
||||
src/libmpg123/dct64_avx.S \
|
||||
src/libmpg123/dct64_avx_float.S \
|
||||
src/libmpg123/synth_3dnowext.S \
|
||||
src/libmpg123/synth_3dnow.S \
|
||||
src/libmpg123/synth_altivec.c \
|
||||
src/libmpg123/synth_i486.c \
|
||||
src/libmpg123/synth_i586_dither.S \
|
||||
src/libmpg123/synth_i586.S \
|
||||
src/libmpg123/synth_mmx.S \
|
||||
src/libmpg123/synth_sse3d.h \
|
||||
src/libmpg123/synth_sse.S \
|
||||
src/libmpg123/synth_sse_float.S \
|
||||
src/libmpg123/synth_sse_s32.S \
|
||||
src/libmpg123/synth_sse_accurate.S \
|
||||
src/libmpg123/synth_stereo_sse_float.S \
|
||||
src/libmpg123/synth_stereo_sse_s32.S \
|
||||
src/libmpg123/synth_stereo_sse_accurate.S \
|
||||
src/libmpg123/synth_x86_64.S \
|
||||
src/libmpg123/synth_x86_64_float.S \
|
||||
src/libmpg123/synth_x86_64_s32.S \
|
||||
src/libmpg123/synth_x86_64_accurate.S \
|
||||
src/libmpg123/synth_stereo_x86_64.S \
|
||||
src/libmpg123/synth_stereo_x86_64_float.S \
|
||||
src/libmpg123/synth_stereo_x86_64_s32.S \
|
||||
src/libmpg123/synth_stereo_x86_64_accurate.S \
|
||||
src/libmpg123/synth_arm.S \
|
||||
src/libmpg123/synth_arm_accurate.S \
|
||||
src/libmpg123/synth_neon.S \
|
||||
src/libmpg123/synth_neon_float.S \
|
||||
src/libmpg123/synth_neon_s32.S \
|
||||
src/libmpg123/synth_neon_accurate.S \
|
||||
src/libmpg123/synth_stereo_neon.S \
|
||||
src/libmpg123/synth_stereo_neon_float.S \
|
||||
src/libmpg123/synth_stereo_neon_s32.S \
|
||||
src/libmpg123/synth_stereo_neon_accurate.S \
|
||||
src/libmpg123/synth_neon64.S \
|
||||
src/libmpg123/synth_neon64_float.S \
|
||||
src/libmpg123/synth_neon64_s32.S \
|
||||
src/libmpg123/synth_neon64_accurate.S \
|
||||
src/libmpg123/synth_stereo_neon64.S \
|
||||
src/libmpg123/synth_stereo_neon64_float.S \
|
||||
src/libmpg123/synth_stereo_neon64_s32.S \
|
||||
src/libmpg123/synth_stereo_neon64_accurate.S \
|
||||
src/libmpg123/synth_stereo_avx.S \
|
||||
src/libmpg123/synth_stereo_avx_float.S \
|
||||
src/libmpg123/synth_stereo_avx_s32.S \
|
||||
src/libmpg123/synth_stereo_avx_accurate.S \
|
||||
src/libmpg123/ntom.c \
|
||||
src/libmpg123/synth.c \
|
||||
src/libmpg123/synth_8bit.c \
|
||||
src/libmpg123/synth_real.c \
|
||||
src/libmpg123/synth_s32.c \
|
||||
src/libmpg123/equalizer_3dnow.S \
|
||||
src/libmpg123/tabinit_mmx.S \
|
||||
src/libmpg123/stringbuf.c \
|
||||
src/libmpg123/getcpuflags.S \
|
||||
src/libmpg123/getcpuflags_x86_64.S \
|
||||
src/libmpg123/getcpuflags_arm.c \
|
||||
src/libmpg123/check_neon.S \
|
||||
src/libmpg123/l12_integer_tables.h \
|
||||
src/libmpg123/l3_integer_tables.h
|
||||
|
||||
if USE_YASM_FOR_AVX
|
||||
## Override rules for the sources that should be assembled with yasm
|
||||
|
||||
AVX_SRCS = \
|
||||
src/libmpg123/dct36_avx.S \
|
||||
src/libmpg123/dct64_avx.S \
|
||||
src/libmpg123/dct64_avx_float.S \
|
||||
src/libmpg123/synth_stereo_avx.S \
|
||||
src/libmpg123/synth_stereo_avx_float.S \
|
||||
src/libmpg123/synth_stereo_avx_s32.S \
|
||||
src/libmpg123/synth_stereo_avx_accurate.S
|
||||
|
||||
AVX_OBJS = $(AVX_SRCS:.S=.@OBJEXT@)
|
||||
|
||||
ASM_DEPS = \
|
||||
src/libmpg123/mangle.h \
|
||||
$(top_builddir)/src/config.h \
|
||||
src/libmpg123/intsym.h
|
||||
|
||||
$(AVX_OBJS): %.@OBJEXT@: %.S $(ASM_DEPS)
|
||||
$(CPP) $(DEFAULT_INCLUDES) $(INCLUDES) -DASMALIGN_BALIGN $< | @YASM@ - @YASMFLAGS@ @YASM_FORMAT@ -o $@
|
||||
|
||||
$(AVX_OBJS:.@OBJEXT@=.lo): %.lo: %.@OBJEXT@
|
||||
@echo "# Generated by ltmain.sh (GNU libtool)" >$@
|
||||
@echo "pic_object='$<'" >>$@
|
||||
@echo "non_pic_object='$<'" >>$@
|
||||
|
||||
endif
|
@@ -1,138 +1,16 @@
|
||||
/*
|
||||
compat: Some compatibility functions.
|
||||
Just to ensure that libraries and programs get their separate
|
||||
compatibility object. There should be a compatibility library,
|
||||
I presume, but I don't want to create another glib, I just want
|
||||
some internal functions to ease coding.
|
||||
|
||||
The mpg123 code is determined to keep it's legacy. A legacy of old, old UNIX.
|
||||
So anything possibly somewhat advanced should be considered to be put here, with proper #ifdef;-)
|
||||
I'll sort it out properly sometime.
|
||||
|
||||
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 Thomas Orgis, Windows Unicode stuff by JonY.
|
||||
I smell symbol conflicts, anyway. Actually wondering why it
|
||||
worked so far.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "compat.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef WANT_WIN32_UNICODE
|
||||
#include <wchar.h>
|
||||
#include <windows.h>
|
||||
#include <winnls.h>
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
/* A safe realloc also for very old systems where realloc(NULL, size) returns NULL. */
|
||||
void *safe_realloc(void *ptr, size_t size)
|
||||
{
|
||||
if(ptr == NULL) return malloc(size);
|
||||
else return realloc(ptr, size);
|
||||
}
|
||||
|
||||
#ifndef HAVE_STRERROR
|
||||
const char *strerror(int errnum)
|
||||
{
|
||||
extern int sys_nerr;
|
||||
extern char *sys_errlist[];
|
||||
|
||||
return (errnum < sys_nerr) ? sys_errlist[errnum] : "";
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRDUP
|
||||
char *strdup(const char *src)
|
||||
{
|
||||
char *dest;
|
||||
|
||||
if (!(dest = (char *) malloc(strlen(src)+1)))
|
||||
return NULL;
|
||||
else
|
||||
return strcpy(dest, src);
|
||||
}
|
||||
#endif
|
||||
|
||||
int compat_open(const char *filename, int flags)
|
||||
{
|
||||
int ret;
|
||||
#if defined (WANT_WIN32_UNICODE)
|
||||
wchar_t *frag = NULL;
|
||||
|
||||
ret = win32_utf8_wide(filename, &frag, NULL);
|
||||
if ((frag == NULL) || (ret == 0)) goto fallback; /* Fallback to plain open when ucs-2 conversion fails */
|
||||
|
||||
ret = _wopen(frag, flags); /*Try _wopen */
|
||||
if (ret != -1 ) goto open_ok; /* msdn says -1 means failure */
|
||||
|
||||
fallback:
|
||||
#endif
|
||||
|
||||
#if (defined(WIN32) && !defined (__CYGWIN__)) /* MSDN says POSIX function is deprecated beginning in Visual C++ 2005 */
|
||||
ret = _open(filename, flags); /* Try plain old _open(), if it fails, do nothing */
|
||||
#else
|
||||
/* On UNIX, we always add a default permission mask in case flags|O_CREAT. */
|
||||
ret = open(filename, flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
|
||||
#endif
|
||||
|
||||
#if defined (WANT_WIN32_UNICODE)
|
||||
open_ok:
|
||||
free ((void *)frag); /* Freeing a NULL should be OK */
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int compat_close(int infd)
|
||||
{
|
||||
#if (defined(WIN32) && !defined (__CYGWIN__)) /* MSDN says POSIX function is deprecated beginning in Visual C++ 2005 */
|
||||
return _close(infd);
|
||||
#else
|
||||
return close(infd);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Windows Unicode stuff */
|
||||
|
||||
#ifdef WANT_WIN32_UNICODE
|
||||
int win32_wide_utf8(const wchar_t * const wptr, char **mbptr, size_t * buflen)
|
||||
{
|
||||
size_t len;
|
||||
char *buf;
|
||||
int ret = 0;
|
||||
|
||||
len = WideCharToMultiByte(CP_UTF8, 0, wptr, -1, NULL, 0, NULL, NULL); /* Get utf-8 string length */
|
||||
buf = calloc(len + 1, sizeof (char)); /* Can we assume sizeof char always = 1? */
|
||||
|
||||
if(!buf) len = 0;
|
||||
else {
|
||||
if (len != 0) ret = WideCharToMultiByte(CP_UTF8, 0, wptr, -1, buf, len, NULL, NULL); /*Do actual conversion*/
|
||||
buf[len] = '0'; /* Must terminate */
|
||||
}
|
||||
*mbptr = buf; /* Set string pointer to allocated buffer */
|
||||
if(buflen != NULL) *buflen = (len) * sizeof (char); /* Give length of allocated memory if needed. */
|
||||
return ret;
|
||||
}
|
||||
|
||||
int win32_utf8_wide(const char *const mbptr, wchar_t **wptr, size_t *buflen)
|
||||
{
|
||||
size_t len;
|
||||
wchar_t *buf;
|
||||
int ret = 0;
|
||||
|
||||
len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mbptr, -1, NULL, 0); /* Get converted size */
|
||||
buf = calloc(len + 1, sizeof (wchar_t)); /* Allocate memory accordingly */
|
||||
|
||||
if(!buf) len = 0;
|
||||
else {
|
||||
if (len != 0) ret = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, mbptr, -1, buf, len); /* Do conversion */
|
||||
buf[len] = L'0'; /* Must terminate */
|
||||
}
|
||||
*wptr = buf; /* Set string pointer to allocated buffer */
|
||||
if (buflen != NULL) *buflen = len * sizeof (wchar_t); /* Give length of allocated memory if needed. */
|
||||
return ret; /* Number of characters written */
|
||||
}
|
||||
#endif
|
||||
#include "intsym.h"
|
||||
#define NO_CATCHSIGNAL
|
||||
#include "compat/compat_impl.h"
|
||||
|
@@ -1,119 +1,3 @@
|
||||
/*
|
||||
dither: Generate shaped noise for dithering
|
||||
|
||||
copyright 2009 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
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "compat.h"
|
||||
#include "dither.h"
|
||||
|
||||
static const uint32_t init_seed = 2463534242UL;
|
||||
|
||||
#define LAP 100
|
||||
|
||||
/*
|
||||
xorshift random number generator, with output scaling to [-0.5, 0.5]
|
||||
This is the white noise...
|
||||
See http://www.jstatsoft.org/v08/i14/paper on XOR shift random number generators.
|
||||
*/
|
||||
static float rand_xorshift32(uint32_t *seed)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t i;
|
||||
float f;
|
||||
} fi;
|
||||
|
||||
fi.i = *seed;
|
||||
fi.i ^= (fi.i<<13);
|
||||
fi.i ^= (fi.i>>17);
|
||||
fi.i ^= (fi.i<<5);
|
||||
*seed = fi.i;
|
||||
|
||||
/* scale the number to [-0.5, 0.5] */
|
||||
#ifdef IEEE_FLOAT
|
||||
fi.i = (fi.i>>9)|0x3f800000;
|
||||
fi.f -= 1.5f;
|
||||
#else
|
||||
fi.f = (double)fi.i / 4294967295.0;
|
||||
fi.f -= 0.5f;
|
||||
#endif
|
||||
return fi.f;
|
||||
}
|
||||
|
||||
static void white_noise(float *table, size_t count)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t seed = init_seed;
|
||||
|
||||
for(i=0; i<count; ++i)
|
||||
table[i] = rand_xorshift32(&seed);
|
||||
}
|
||||
|
||||
static void tpdf_noise(float *table, size_t count)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t seed = init_seed;
|
||||
|
||||
for(i=0; i<count; ++i)
|
||||
table[i] = rand_xorshift32(&seed) + rand_xorshift32(&seed);
|
||||
}
|
||||
|
||||
static void highpass_tpdf_noise(float *table, size_t count)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t seed = init_seed;
|
||||
/* Ensure some minimum lap for keeping the high-pass filter circular. */
|
||||
size_t lap = count > 2*LAP ? LAP : count/2;
|
||||
|
||||
float input_noise;
|
||||
float xv[9], yv[9];
|
||||
|
||||
for(i=0;i<9;i++)
|
||||
{
|
||||
xv[i] = yv[i] = 0.0f;
|
||||
}
|
||||
|
||||
for(i=0;i<count+lap;i++)
|
||||
{
|
||||
if(i==count) seed=init_seed;
|
||||
|
||||
/* generate and add 2 random numbers, to make a TPDF noise distribution */
|
||||
input_noise = rand_xorshift32(&seed) + rand_xorshift32(&seed);
|
||||
|
||||
/* apply 8th order Chebyshev high-pass IIR filter */
|
||||
/* Coefficients are from http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
|
||||
Given parameters are: Chebyshev, Highpass, ripple=-1, order=8, samplerate=44100, corner1=19000 */
|
||||
xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5]; xv[5] = xv[6]; xv[6] = xv[7]; xv[7] = xv[8];
|
||||
xv[8] = input_noise / 1.382814179e+07;
|
||||
yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4]; yv[4] = yv[5]; yv[5] = yv[6]; yv[6] = yv[7]; yv[7] = yv[8];
|
||||
yv[8] = (xv[0] + xv[8]) - 8 * (xv[1] + xv[7]) + 28 * (xv[2] + xv[6])
|
||||
- 56 * (xv[3] + xv[5]) + 70 * xv[4]
|
||||
+ ( -0.6706204984 * yv[0]) + ( -5.3720827038 * yv[1])
|
||||
+ (-19.0865382480 * yv[2]) + (-39.2831607860 * yv[3])
|
||||
+ (-51.2308985070 * yv[4]) + (-43.3590135780 * yv[5])
|
||||
+ (-23.2632305320 * yv[6]) + ( -7.2370122050 * yv[7]);
|
||||
if(i>=lap) table[i-lap] = yv[8] * 3.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void mpg123_noise(float* table, size_t count, enum mpg123_noise_type noisetype)
|
||||
{
|
||||
switch(noisetype)
|
||||
{
|
||||
case mpg123_white_noise: white_noise(table, count); break;
|
||||
case mpg123_tpdf_noise: tpdf_noise(table, count); break;
|
||||
case mpg123_highpass_tpdf_noise:
|
||||
highpass_tpdf_noise(table, count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate white noise and shape it with a high pass filter. */
|
||||
void dither_table_init(float *dithertable)
|
||||
{
|
||||
highpass_tpdf_noise(dithertable, DITHERSIZE);
|
||||
}
|
||||
/* Hack to allow building the same code with and without libtool. */
|
||||
#include "intsym.h"
|
||||
#include "dither_impl.h"
|
||||
|
119
src/libmpg123/dither_impl.h
Normal file
119
src/libmpg123/dither_impl.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
dither: Generate shaped noise for dithering
|
||||
|
||||
copyright 2009 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
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "compat.h"
|
||||
#include "dither.h"
|
||||
|
||||
static const uint32_t init_seed = 2463534242UL;
|
||||
|
||||
#define LAP 100
|
||||
|
||||
/*
|
||||
xorshift random number generator, with output scaling to [-0.5, 0.5]
|
||||
This is the white noise...
|
||||
See http://www.jstatsoft.org/v08/i14/paper on XOR shift random number generators.
|
||||
*/
|
||||
static float rand_xorshift32(uint32_t *seed)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t i;
|
||||
float f;
|
||||
} fi;
|
||||
|
||||
fi.i = *seed;
|
||||
fi.i ^= (fi.i<<13);
|
||||
fi.i ^= (fi.i>>17);
|
||||
fi.i ^= (fi.i<<5);
|
||||
*seed = fi.i;
|
||||
|
||||
/* scale the number to [-0.5, 0.5] */
|
||||
#ifdef IEEE_FLOAT
|
||||
fi.i = (fi.i>>9)|0x3f800000;
|
||||
fi.f -= 1.5f;
|
||||
#else
|
||||
fi.f = (double)fi.i / 4294967295.0;
|
||||
fi.f -= 0.5f;
|
||||
#endif
|
||||
return fi.f;
|
||||
}
|
||||
|
||||
static void white_noise(float *table, size_t count)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t seed = init_seed;
|
||||
|
||||
for(i=0; i<count; ++i)
|
||||
table[i] = rand_xorshift32(&seed);
|
||||
}
|
||||
|
||||
static void tpdf_noise(float *table, size_t count)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t seed = init_seed;
|
||||
|
||||
for(i=0; i<count; ++i)
|
||||
table[i] = rand_xorshift32(&seed) + rand_xorshift32(&seed);
|
||||
}
|
||||
|
||||
static void highpass_tpdf_noise(float *table, size_t count)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t seed = init_seed;
|
||||
/* Ensure some minimum lap for keeping the high-pass filter circular. */
|
||||
size_t lap = count > 2*LAP ? LAP : count/2;
|
||||
|
||||
float input_noise;
|
||||
float xv[9], yv[9];
|
||||
|
||||
for(i=0;i<9;i++)
|
||||
{
|
||||
xv[i] = yv[i] = 0.0f;
|
||||
}
|
||||
|
||||
for(i=0;i<count+lap;i++)
|
||||
{
|
||||
if(i==count) seed=init_seed;
|
||||
|
||||
/* generate and add 2 random numbers, to make a TPDF noise distribution */
|
||||
input_noise = rand_xorshift32(&seed) + rand_xorshift32(&seed);
|
||||
|
||||
/* apply 8th order Chebyshev high-pass IIR filter */
|
||||
/* Coefficients are from http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
|
||||
Given parameters are: Chebyshev, Highpass, ripple=-1, order=8, samplerate=44100, corner1=19000 */
|
||||
xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5]; xv[5] = xv[6]; xv[6] = xv[7]; xv[7] = xv[8];
|
||||
xv[8] = input_noise / 1.382814179e+07;
|
||||
yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4]; yv[4] = yv[5]; yv[5] = yv[6]; yv[6] = yv[7]; yv[7] = yv[8];
|
||||
yv[8] = (xv[0] + xv[8]) - 8 * (xv[1] + xv[7]) + 28 * (xv[2] + xv[6])
|
||||
- 56 * (xv[3] + xv[5]) + 70 * xv[4]
|
||||
+ ( -0.6706204984 * yv[0]) + ( -5.3720827038 * yv[1])
|
||||
+ (-19.0865382480 * yv[2]) + (-39.2831607860 * yv[3])
|
||||
+ (-51.2308985070 * yv[4]) + (-43.3590135780 * yv[5])
|
||||
+ (-23.2632305320 * yv[6]) + ( -7.2370122050 * yv[7]);
|
||||
if(i>=lap) table[i-lap] = yv[8] * 3.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void mpg123_noise(float* table, size_t count, enum mpg123_noise_type noisetype)
|
||||
{
|
||||
switch(noisetype)
|
||||
{
|
||||
case mpg123_white_noise: white_noise(table, count); break;
|
||||
case mpg123_tpdf_noise: tpdf_noise(table, count); break;
|
||||
case mpg123_highpass_tpdf_noise:
|
||||
highpass_tpdf_noise(table, count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate white noise and shape it with a high pass filter. */
|
||||
void dither_table_init(float *dithertable)
|
||||
{
|
||||
highpass_tpdf_noise(dithertable, DITHERSIZE);
|
||||
}
|
@@ -1,11 +1,13 @@
|
||||
/*
|
||||
icy: Puny code to pretend for a serious ICY data structure.
|
||||
|
||||
copyright 2007 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
copyright 2007-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
|
||||
*/
|
||||
|
||||
#include "intsym.h"
|
||||
#include "icy.h"
|
||||
|
||||
void init_icy(struct icy_meta *icy)
|
||||
|
@@ -26,6 +26,8 @@
|
||||
* Convert from ICY encoding (windows-1252 codepage) to UTF-8
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "intsym.h"
|
||||
/* Includes string and stdlib headers... */
|
||||
#include "compat.h"
|
||||
|
||||
|
@@ -1,11 +1,13 @@
|
||||
/*
|
||||
index: frame index data structure and functions
|
||||
|
||||
copyright 2007-8 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
copyright 2007-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
|
||||
*/
|
||||
|
||||
#include "intsym.h"
|
||||
#include "index.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
@@ -1,14 +1,18 @@
|
||||
#ifndef MPG123_INTMAP_H
|
||||
#define MPG123_INTMAP_H
|
||||
#ifndef MPG123_INTSYM_H
|
||||
#define MPG123_INTSYM_H
|
||||
/* Mapping of internal mpg123 symbols to something that is less likely to conflict in case of static linking. */
|
||||
#define COS9 INT123_COS9
|
||||
#define tfcos36 INT123_tfcos36
|
||||
#define pnts INT123_pnts
|
||||
#define safe_realloc INT123_safe_realloc
|
||||
#define compat_open INT123_compat_open
|
||||
#define compat_fopen INT123_compat_fopen
|
||||
#define compat_close INT123_compat_close
|
||||
#define compat_fclose INT123_compat_fclose
|
||||
#define win32_wide_utf8 INT123_win32_wide_utf8
|
||||
#define win32_utf8_wide INT123_win32_utf8_wide
|
||||
#define unintr_write INT123_unintr_write
|
||||
#define unintr_read INT123_unintr_read
|
||||
#define ntom_set_ntom INT123_ntom_set_ntom
|
||||
#define synth_1to1 INT123_synth_1to1
|
||||
#define synth_1to1_dither INT123_synth_1to1_dither
|
||||
@@ -29,6 +33,8 @@
|
||||
#define synth_1to1_arm INT123_synth_1to1_arm
|
||||
#define synth_1to1_neon INT123_synth_1to1_neon
|
||||
#define synth_1to1_stereo_neon INT123_synth_1to1_stereo_neon
|
||||
#define synth_1to1_neon64 INT123_synth_1to1_neon64
|
||||
#define synth_1to1_stereo_neon64 INT123_synth_1to1_stereo_neon64
|
||||
#define absynth_1to1_i486 INT123_absynth_1to1_i486
|
||||
#define synth_1to1_mono INT123_synth_1to1_mono
|
||||
#define synth_1to1_m2s INT123_synth_1to1_m2s
|
||||
@@ -75,6 +81,8 @@
|
||||
#define synth_1to1_real_stereo_altivec INT123_synth_1to1_real_stereo_altivec
|
||||
#define synth_1to1_real_neon INT123_synth_1to1_real_neon
|
||||
#define synth_1to1_real_stereo_neon INT123_synth_1to1_real_stereo_neon
|
||||
#define synth_1to1_real_neon64 INT123_synth_1to1_real_neon64
|
||||
#define synth_1to1_real_stereo_neon64 INT123_synth_1to1_real_stereo_neon64
|
||||
#define synth_1to1_real_mono INT123_synth_1to1_real_mono
|
||||
#define synth_1to1_real_m2s INT123_synth_1to1_real_m2s
|
||||
#define synth_2to1_real INT123_synth_2to1_real
|
||||
@@ -100,6 +108,8 @@
|
||||
#define synth_1to1_s32_stereo_altivec INT123_synth_1to1_s32_stereo_altivec
|
||||
#define synth_1to1_s32_neon INT123_synth_1to1_s32_neon
|
||||
#define synth_1to1_s32_stereo_neon INT123_synth_1to1_s32_stereo_neon
|
||||
#define synth_1to1_s32_neon64 INT123_synth_1to1_s32_neon64
|
||||
#define synth_1to1_s32_stereo_neon64 INT123_synth_1to1_s32_stereo_neon64
|
||||
#define synth_1to1_s32_mono INT123_synth_1to1_s32_mono
|
||||
#define synth_1to1_s32_m2s INT123_synth_1to1_s32_m2s
|
||||
#define synth_2to1_s32 INT123_synth_2to1_s32
|
||||
@@ -120,8 +130,8 @@
|
||||
#define dct36 INT123_dct36
|
||||
#define dct36_3dnow INT123_dct36_3dnow
|
||||
#define dct36_3dnowext INT123_dct36_3dnowext
|
||||
#define dct36_sse INT123_dct36_sse
|
||||
#define dct36_x86_64 INT123_dct36_x86_64
|
||||
#define dct36_sse INT123_dct36_sse
|
||||
#define dct36_avx INT123_dct36_avx
|
||||
#define dct36_neon INT123_dct36_neon
|
||||
#define dct36_neon64 INT123_dct36_neon64
|
||||
@@ -198,8 +208,11 @@
|
||||
#define double_to_long_rounded INT123_double_to_long_rounded
|
||||
#define scale_rounded INT123_scale_rounded
|
||||
#define decode_update INT123_decode_update
|
||||
#define decoder_synth_bytes INT123_decoder_synth_bytes
|
||||
#define samples_to_bytes INT123_samples_to_bytes
|
||||
#define bytes_to_samples INT123_bytes_to_samples
|
||||
#define outblock_bytes INT123_outblock_bytes
|
||||
#define postprocess_buffer INT123_postprocess_buffer
|
||||
#define frame_cpu_opt INT123_frame_cpu_opt
|
||||
#define set_synth_functions INT123_set_synth_functions
|
||||
#define dectype INT123_dectype
|
||||
@@ -216,6 +229,10 @@
|
||||
#define compute_bpf INT123_compute_bpf
|
||||
#define time_to_frame INT123_time_to_frame
|
||||
#define get_songlen INT123_get_songlen
|
||||
#define bc_prepare INT123_bc_prepare
|
||||
#define bc_cleanup INT123_bc_cleanup
|
||||
#define bc_poolsize INT123_bc_poolsize
|
||||
#define bc_fill INT123_bc_fill
|
||||
#define open_stream INT123_open_stream
|
||||
#define open_stream_handle INT123_open_stream_handle
|
||||
#define open_feed INT123_open_feed
|
||||
@@ -223,20 +240,21 @@
|
||||
#define feed_forget INT123_feed_forget
|
||||
#define feed_set_pos INT123_feed_set_pos
|
||||
#define open_bad INT123_open_bad
|
||||
#define check_neon INT123_check_neon
|
||||
#define dct64_3dnow INT123_dct64_3dnow
|
||||
#define dct64_3dnowext INT123_dct64_3dnowext
|
||||
#define dct64_avx INT123_dct64_avx
|
||||
#define dct64_real_avx INT123_dct64_real_avx
|
||||
#define dct64_mmx INT123_dct64_mmx
|
||||
#define dct64_MMX INT123_dct64_MMX
|
||||
#define dct64_neon INT123_dct64_neon
|
||||
#define dct64_neon64 INT123_dct64_neon64
|
||||
#define dct64_real_neon64 INT123_dct64_real_neon64
|
||||
#define dct64_real_neon INT123_dct64_real_neon
|
||||
#define dct64_sse INT123_dct64_sse
|
||||
#define dct64_real_sse INT123_dct64_real_sse
|
||||
#define dct64_x86_64 INT123_dct64_x86_64
|
||||
#define dct64_real_x86_64 INT123_dct64_real_x86_64
|
||||
#define dct64_avx INT123_dct64_avx
|
||||
#define dct64_real_avx INT123_dct64_real_avx
|
||||
#define dct64_neon INT123_dct64_neon
|
||||
#define dct64_real_neon INT123_dct64_real_neon
|
||||
#define dct64_neon64 INT123_dct64_neon64
|
||||
#define dct64_real_neon64 INT123_dct64_real_neon64
|
||||
#define do_equalizer_3dnow INT123_do_equalizer_3dnow
|
||||
#define synth_1to1_3dnow_asm INT123_synth_1to1_3dnow_asm
|
||||
#define synth_1to1_arm_asm INT123_synth_1to1_arm_asm
|
||||
@@ -244,9 +262,29 @@
|
||||
#define synth_1to1_i586_asm INT123_synth_1to1_i586_asm
|
||||
#define synth_1to1_i586_asm_dither INT123_synth_1to1_i586_asm_dither
|
||||
#define synth_1to1_MMX INT123_synth_1to1_MMX
|
||||
#define synth_1to1_neon_asm INT123_synth_1to1_neon_asm
|
||||
#define synth_1to1_neon64_asm INT123_synth_1to1_neon64_asm
|
||||
#define synth_1to1_neon64_accurate_asm INT123_synth_1to1_neon64_accurate_asm
|
||||
#define synth_1to1_real_neon64_asm INT123_synth_1to1_real_neon64_asm
|
||||
#define synth_1to1_s32_neon64_asm INT123_synth_1to1_s32_neon64_asm
|
||||
#define synth_1to1_neon_accurate_asm INT123_synth_1to1_neon_accurate_asm
|
||||
#define synth_1to1_real_neon_asm INT123_synth_1to1_real_neon_asm
|
||||
#define synth_1to1_s32_neon_asm INT123_synth_1to1_s32_neon_asm
|
||||
#define synth_1to1_sse_accurate_asm INT123_synth_1to1_sse_accurate_asm
|
||||
#define synth_1to1_real_sse_asm INT123_synth_1to1_real_sse_asm
|
||||
#define synth_1to1_s32_sse_asm INT123_synth_1to1_s32_sse_asm
|
||||
#define synth_1to1_s_avx_asm INT123_synth_1to1_s_avx_asm
|
||||
#define synth_1to1_s_avx_accurate_asm INT123_synth_1to1_s_avx_accurate_asm
|
||||
#define synth_1to1_real_s_avx_asm INT123_synth_1to1_real_s_avx_asm
|
||||
#define synth_1to1_s32_s_avx_asm INT123_synth_1to1_s32_s_avx_asm
|
||||
#define synth_1to1_s_neon_asm INT123_synth_1to1_s_neon_asm
|
||||
#define synth_1to1_s_neon64_asm INT123_synth_1to1_s_neon64_asm
|
||||
#define synth_1to1_s_neon64_accurate_asm INT123_synth_1to1_s_neon64_accurate_asm
|
||||
#define synth_1to1_real_s_neon64_asm INT123_synth_1to1_real_s_neon64_asm
|
||||
#define synth_1to1_s32_s_neon64_asm INT123_synth_1to1_s32_s_neon64_asm
|
||||
#define synth_1to1_s_neon_accurate_asm INT123_synth_1to1_s_neon_accurate_asm
|
||||
#define synth_1to1_real_s_neon_asm INT123_synth_1to1_real_s_neon_asm
|
||||
#define synth_1to1_s32_s_neon_asm INT123_synth_1to1_s32_s_neon_asm
|
||||
#define synth_1to1_s_sse_accurate_asm INT123_synth_1to1_s_sse_accurate_asm
|
||||
#define synth_1to1_real_s_sse_asm INT123_synth_1to1_real_s_sse_asm
|
||||
#define synth_1to1_s32_s_sse_asm INT123_synth_1to1_s32_s_sse_asm
|
||||
@@ -258,27 +296,12 @@
|
||||
#define synth_1to1_x86_64_accurate_asm INT123_synth_1to1_x86_64_accurate_asm
|
||||
#define synth_1to1_real_x86_64_asm INT123_synth_1to1_real_x86_64_asm
|
||||
#define synth_1to1_s32_x86_64_asm INT123_synth_1to1_s32_x86_64_asm
|
||||
#define synth_1to1_s_avx_asm INT123_synth_1to1_s_avx_asm
|
||||
#define synth_1to1_s_avx_accurate_asm INT123_synth_1to1_s_avx_accurate_asm
|
||||
#define synth_1to1_real_s_avx_asm INT123_synth_1to1_real_s_avx_asm
|
||||
#define synth_1to1_s32_s_avx_asm INT123_synth_1to1_s32_s_avx_asm
|
||||
#define synth_1to1_neon_asm INT123_synth_1to1_neon_asm
|
||||
#define synth_1to1_neon_accurate_asm INT123_synth_1to1_neon_accurate_asm
|
||||
#define synth_1to1_real_neon_asm INT123_synth_1to1_real_neon_asm
|
||||
#define synth_1to1_s32_neon_asm INT123_synth_1to1_s32_neon_asm
|
||||
#define synth_1to1_s_neon_asm INT123_synth_1to1_s_neon_asm
|
||||
#define synth_1to1_s_neon_accurate_asm INT123_synth_1to1_s_neon_accurate_asm
|
||||
#define synth_1to1_real_s_neon_asm INT123_synth_1to1_real_s_neon_asm
|
||||
#define synth_1to1_s32_s_neon_asm INT123_synth_1to1_s32_s_neon_asm
|
||||
#define synth_1to1_neon64_asm INT123_synth_1to1_neon64_asm
|
||||
#define synth_1to1_neon64_accurate_asm INT123_synth_1to1_neon64_accurate_asm
|
||||
#define synth_1to1_real_neon64_asm INT123_synth_1to1_real_neon64_asm
|
||||
#define synth_1to1_s32_neon64_asm INT123_synth_1to1_s32_neon64_asm
|
||||
#define synth_1to1_s_neon64_asm INT123_synth_1to1_s_neon64_asm
|
||||
#define synth_1to1_s_neon64_accurate_asm INT123_synth_1to1_s_neon64_accurate_asm
|
||||
#define synth_1to1_real_s_neon64_asm INT123_synth_1to1_real_s_neon64_asm
|
||||
#define synth_1to1_s32_s_neon64_asm INT123_synth_1to1_s32_s_neon64_asm
|
||||
#define costab_mmxsse INT123_costab_mmxsse
|
||||
#define make_decode_tables_mmx_asm INT123_make_decode_tables_mmx_asm
|
||||
#define check_neon INT123_check_neon
|
||||
#ifndef HAVE_STRDUP
|
||||
#define strdup INT123_strdup
|
||||
#endif
|
||||
#ifndef HAVE_STRERROR
|
||||
#define strerror INT123_strerror
|
||||
#endif
|
||||
#endif
|
||||
|
54
src/libout123/Makemodule.am
Normal file
54
src/libout123/Makemodule.am
Normal file
@@ -0,0 +1,54 @@
|
||||
# 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/out123.h \
|
||||
src/libout123/out123_int.h \
|
||||
src/libout123/out123_intsym.h \
|
||||
src/libout123/compat.c \
|
||||
src/libout123/wav.c \
|
||||
src/libout123/wav.h \
|
||||
src/libout123/wavhead.h \
|
||||
src/libout123/buffer.c \
|
||||
src/libout123/buffer.h \
|
||||
src/libout123/xfermem.c \
|
||||
src/libout123/xfermem.h
|
||||
|
||||
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/libout123/modules/libdefaultmodule.la
|
||||
|
||||
src_libout123_libmodule_la_SOURCES = src/libout123/module.h
|
||||
|
||||
if HAVE_MODULES
|
||||
|
||||
src_libout123_libmodule_la_SOURCES += src/libout123/module.c
|
||||
src_libout123_libmodule_la_LIBADD = -lltdl
|
||||
|
||||
else
|
||||
|
||||
src_libout123_libmodule_la_SOURCES += src/libout123/legacy_module.c
|
||||
|
||||
endif
|
||||
|
||||
# Too lazy to put it into deps for every module.
|
||||
EXTRA_DIST += src/libout123/outmod_def.h
|
||||
|
||||
# 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
|
814
src/libout123/buffer.c
Normal file
814
src/libout123/buffer.c
Normal file
@@ -0,0 +1,814 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
#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
|
||||
|
||||
/* 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 write_string(audio_output_t *ao, int who, const char *buf);
|
||||
static int read_string(audio_output_t *ao
|
||||
, int who, char **buf, byte *prebuf, int *preoff, int presize);
|
||||
static int buffer_loop(audio_output_t *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(audio_output_t *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 */
|
||||
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(audio_output_t *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(audio_output_t *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(audio_output_t *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, writerfd))
|
||||
{
|
||||
ao->errcode = OUT123_BUFFER_ERROR;
|
||||
return -1;
|
||||
}
|
||||
return buffer_cmd_finish(ao);
|
||||
}
|
||||
|
||||
int buffer_open(audio_output_t *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( write_string(ao, XF_WRITER, driver)
|
||||
|| write_string(ao, XF_WRITER, device) )
|
||||
{
|
||||
ao->errcode = OUT123_BUFFER_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(buffer_cmd_finish(ao) == 0)
|
||||
{
|
||||
/* Retrieve driver and device name. */
|
||||
if( read_string(ao, XF_WRITER, &ao->driver, NULL, NULL, 0)
|
||||
|| read_string(ao, XF_WRITER, &ao->device, NULL, NULL, 0) )
|
||||
{
|
||||
ao->errcode = OUT123_BUFFER_ERROR;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int buffer_encodings(audio_output_t *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_start(audio_output_t *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(audio_output_t *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(audio_output_t *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(audio_output_t *ao)
|
||||
{
|
||||
return xfermem_get_usedspace(ao->buffermem);
|
||||
}
|
||||
|
||||
void buffer_ndrain(audio_output_t *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(audio_output_t *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(audio_output_t *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(audio_output_t *ao, size_t bytes)
|
||||
{
|
||||
int 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;
|
||||
/* Now do a normal ao->write(), with interruptions by signals
|
||||
being expected. */
|
||||
written = ao->write(ao, (unsigned char*)xf->data+xf->readindex, (int)bytes);
|
||||
debug2("buffer wrote %i B / %i B to device", written, (int)bytes);
|
||||
if(written >= 0)
|
||||
/* Advance read pointer by the amount of written bytes. */
|
||||
xf->readindex = (xf->readindex + written) % xf->size;
|
||||
else
|
||||
{
|
||||
ao->errcode = OUT123_DEV_PLAY;
|
||||
if(!(ao->flags & OUT123_QUIET))
|
||||
error1("Error in writing audio (%s?)!", strerror(errno));
|
||||
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. */
|
||||
static int write_string(audio_output_t *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 2;
|
||||
}
|
||||
return 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 string from command channel.
|
||||
Return 0 on success, set ao->errcode on issues. */
|
||||
static int read_string(audio_output_t *ao
|
||||
, int who, char **buf, byte *prebuf, int *preoff, int presize)
|
||||
{
|
||||
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 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;
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* The main loop, returns 0 when no issue occured. */
|
||||
int buffer_loop(audio_output_t *ao)
|
||||
{
|
||||
txfermem *xf = ao->buffermem;
|
||||
int my_fd = xf->fd[XF_READER];
|
||||
int preloading = FALSE;
|
||||
|
||||
/* 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(ao->state == play_live)
|
||||
{
|
||||
size_t bytes = xfermem_get_usedspace(xf);
|
||||
debug2( "Play or preload? Got %"SIZE_P" B / %"SIZE_P" B."
|
||||
, (size_p)bytes, (size_p)preload_size(ao) );
|
||||
if(preloading)
|
||||
preloading = (bytes < preload_size(ao));
|
||||
if(!preloading)
|
||||
{
|
||||
if(bytes < outburst)
|
||||
preloading = TRUE;
|
||||
else
|
||||
buffer_play(ao, bytes);
|
||||
}
|
||||
}
|
||||
/* Now always check for a pending command, in a blocking way if there is
|
||||
no playback. */
|
||||
debug1("Buffer cmd? (Interruped: %i)", intflag);
|
||||
/*
|
||||
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 || (ao->state != play_live))
|
||||
, cmd
|
||||
, sizeof(cmd) );
|
||||
if(cmdcount < 0)
|
||||
{
|
||||
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");
|
||||
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(ao->state == 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, my_fd, cmd, &i, cmdcount);
|
||||
xfermem_putcmd(my_fd, XF_CMD_OK);
|
||||
break;
|
||||
case BUF_CMD_OPEN:
|
||||
{
|
||||
char *driver = NULL;
|
||||
char *device = NULL;
|
||||
int success;
|
||||
|
||||
intflag = FALSE;
|
||||
success = (
|
||||
!read_string(ao, XF_READER, &driver, cmd, &i, cmdcount)
|
||||
&& !read_string(ao, XF_READER, &device, cmd, &i, cmdcount)
|
||||
&& !out123_open(ao, driver, device)
|
||||
);
|
||||
free(device);
|
||||
free(driver);
|
||||
if(success)
|
||||
{
|
||||
xfermem_putcmd(my_fd, XF_CMD_OK);
|
||||
if( write_string(ao, XF_READER, ao->driver)
|
||||
|| write_string(ao, XF_READER, ao->device) )
|
||||
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);
|
||||
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->channels, ao->rate);
|
||||
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_START:
|
||||
intflag = 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->format, ao->channels, ao->rate))
|
||||
{
|
||||
preloading = TRUE;
|
||||
xfermem_putcmd(my_fd, XF_CMD_OK);
|
||||
}
|
||||
else
|
||||
{
|
||||
xfermem_putcmd(my_fd, XF_CMD_ERROR);
|
||||
if(!GOOD_WRITEVAL(my_fd, ao->errcode))
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
case BUF_CMD_STOP:
|
||||
intflag = FALSE;
|
||||
if(ao->state == play_live)
|
||||
{ /* Drain is implied! */
|
||||
size_t bytes;
|
||||
while((bytes = xfermem_get_usedspace(xf)))
|
||||
buffer_play(ao, bytes);
|
||||
}
|
||||
out123_stop(ao);
|
||||
xfermem_putcmd(my_fd, XF_CMD_OK);
|
||||
break;
|
||||
case XF_CMD_CONTINUE:
|
||||
intflag = FALSE;
|
||||
out123_continue(ao);
|
||||
preloading = TRUE;
|
||||
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:
|
||||
intflag = FALSE;
|
||||
if(ao->state == play_live)
|
||||
{
|
||||
size_t bytes;
|
||||
while(
|
||||
(bytes = xfermem_get_usedspace(xf))
|
||||
&& bytes > ao->framesize
|
||||
)
|
||||
buffer_play(ao, bytes);
|
||||
out123_drain(ao);
|
||||
}
|
||||
xfermem_putcmd(my_fd, XF_CMD_OK);
|
||||
break;
|
||||
case BUF_CMD_NDRAIN:
|
||||
{
|
||||
size_t limit;
|
||||
size_t oldfill;
|
||||
|
||||
intflag = FALSE;
|
||||
if(
|
||||
!GOOD_READVAL_BUF(my_fd, limit)
|
||||
|| !GOOD_READVAL_BUF(my_fd, oldfill)
|
||||
)
|
||||
return 2;
|
||||
if(ao->state == 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);
|
||||
out123_drain(ao);
|
||||
debug2( "buffer drained %"SIZE_P" / %"SIZE_P
|
||||
, oldfill-bytes, limit );
|
||||
}
|
||||
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;
|
||||
out123_pause(ao);
|
||||
xfermem_putcmd(my_fd, XF_CMD_OK);
|
||||
break;
|
||||
case XF_CMD_DROP:
|
||||
intflag = 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? */
|
||||
}
|
||||
}
|
59
src/libout123/buffer.h
Normal file
59
src/libout123/buffer.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
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(audio_output_t *ao, size_t bytes);
|
||||
void buffer_exit(audio_output_t *ao);
|
||||
|
||||
/* Messages with payload. */
|
||||
|
||||
int buffer_sync_param(audio_output_t *ao);
|
||||
int buffer_open(audio_output_t *ao, const char* driver, const char* device);
|
||||
int buffer_encodings(audio_output_t *ao);
|
||||
int buffer_start(audio_output_t *ao);
|
||||
void buffer_ndrain(audio_output_t *ao, size_t bytes);
|
||||
|
||||
/* Simple messages to be deal with after playback. */
|
||||
|
||||
void buffer_stop(audio_output_t *ao);
|
||||
void buffer_close(audio_output_t *ao);
|
||||
void buffer_continue(audio_output_t *ao);
|
||||
/* Still undecided if that one is to be used anywhere. */
|
||||
void buffer_ignore_lowmem(audio_output_t *ao);
|
||||
void buffer_drain(audio_output_t *ao);
|
||||
void buffer_end(audio_output_t *ao);
|
||||
|
||||
/* Simple messages with interruption of playback. */
|
||||
|
||||
void buffer_pause(audio_output_t *ao);
|
||||
void buffer_drop(audio_output_t *ao);
|
||||
|
||||
/* The actual work: Hand over audio data. */
|
||||
size_t buffer_write(audio_output_t *ao, void *buffer, size_t bytes);
|
||||
|
||||
/* Thin wrapper over xfermem giving the current buffer fill. */
|
||||
size_t buffer_fill(audio_output_t *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);
|
||||
|
||||
#endif
|
15
src/libout123/compat.c
Normal file
15
src/libout123/compat.c
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
Just to ensure that libraries and programs get their separate
|
||||
compatibility object. There should be a compatibility library,
|
||||
I presume, but I don't want to create another glib, I just want
|
||||
some internal functions to ease coding.
|
||||
|
||||
I'll sort it out properly sometime.
|
||||
|
||||
I smell symbol conflicts, anyway. Actually wondering why it
|
||||
worked so far.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "out123_intsym.h"
|
||||
#include "compat/compat_impl.h"
|
95
src/libout123/legacy_module.c
Normal file
95
src/libout123/legacy_module.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
debug("list_modules()" );
|
||||
|
||||
*names = NULL;
|
||||
*descr = NULL;
|
||||
|
||||
if(
|
||||
(*names=malloc(sizeof(char*)))
|
||||
&& !((*names)[0]=NULL) /* for safe cleanup */
|
||||
&& ((*names)[0]=strdup(mpg123_output_module_info.name))
|
||||
&& (*descr=malloc(sizeof(char*)))
|
||||
&& !((*descr)[0]=NULL) /* for safe cleanup */
|
||||
&& ((*descr)[0]=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;
|
||||
}
|
||||
}
|
||||
|
||||
|
885
src/libout123/libout123.c
Normal file
885
src/libout123/libout123.c
Normal file
@@ -0,0 +1,885 @@
|
||||
/*
|
||||
audio: audio output 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 Michael Hipp
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include "out123_int.h"
|
||||
#include "wav.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
static int have_buffer(audio_output_t *ao)
|
||||
{
|
||||
return (ao->buffer_pid != -1);
|
||||
}
|
||||
|
||||
static int modverbose(audio_output_t *ao)
|
||||
{
|
||||
return AOQUIET
|
||||
? -1
|
||||
: ao->verbose;
|
||||
}
|
||||
|
||||
static void check_output_module( audio_output_t *ao
|
||||
, const char *name, const char *device, int final );
|
||||
|
||||
static void out123_clear_module(audio_output_t *ao)
|
||||
{
|
||||
ao->open = NULL;
|
||||
ao->get_formats = NULL;
|
||||
ao->write = NULL;
|
||||
ao->flush = NULL;
|
||||
ao->drain = NULL;
|
||||
ao->close = NULL;
|
||||
ao->deinit = NULL;
|
||||
|
||||
ao->module = NULL;
|
||||
ao->userptr = NULL;
|
||||
ao->fn = -1;
|
||||
}
|
||||
|
||||
audio_output_t* out123_new(void)
|
||||
{
|
||||
audio_output_t* ao = malloc( sizeof( audio_output_t ) );
|
||||
if(!ao)
|
||||
return NULL;
|
||||
ao->errcode = 0;
|
||||
#ifndef NOXFERMEM
|
||||
ao->buffer_pid = -1;
|
||||
ao->buffer_fd[0] = -1;
|
||||
ao->buffer_fd[1] = -1;
|
||||
ao->buffermem = NULL;
|
||||
#endif
|
||||
|
||||
out123_clear_module(ao);
|
||||
ao->driver = NULL;
|
||||
ao->device = NULL;
|
||||
|
||||
ao->flags = 0;
|
||||
ao->rate = -1;
|
||||
ao->gain = -1;
|
||||
ao->channels = -1;
|
||||
ao->format = -1;
|
||||
ao->framesize = 0;
|
||||
ao->state = play_dead;
|
||||
ao->auxflags = 0;
|
||||
ao->preload = 0.;
|
||||
ao->verbose = 0;
|
||||
ao->device_buffer = 0.;
|
||||
return ao;
|
||||
}
|
||||
|
||||
void out123_del(audio_output_t *ao)
|
||||
{
|
||||
debug1("out123_del(%p)", (void*)ao);
|
||||
if(!ao) return;
|
||||
|
||||
out123_close(ao); /* TODO: That talks to the buffer if present. */
|
||||
out123_set_buffer(ao, 0);
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao)) buffer_exit(ao);
|
||||
#endif
|
||||
free(ao);
|
||||
}
|
||||
|
||||
/* Error reporting */
|
||||
|
||||
/* Carefully keep that in sync with the error enum! */
|
||||
static const char *const errstring[OUT123_ERRCOUNT] =
|
||||
{
|
||||
"no problem"
|
||||
, "out of memory"
|
||||
, "bad driver name"
|
||||
, "failure loading driver module"
|
||||
, "no driver loaded"
|
||||
, "no active audio device"
|
||||
, "some device playback error"
|
||||
, "failed to open device"
|
||||
, "buffer (communication) error"
|
||||
, "basic module system error"
|
||||
};
|
||||
|
||||
const char* out123_strerror(audio_output_t *ao)
|
||||
{
|
||||
return out123_plain_strerror(out123_errcode(ao));
|
||||
}
|
||||
|
||||
int out123_errcode(audio_output_t *ao)
|
||||
{
|
||||
if(!ao) return OUT123_ERR;
|
||||
else return ao->errcode;
|
||||
}
|
||||
|
||||
const char* out123_plain_strerror(int errcode)
|
||||
{
|
||||
if(errcode >= OUT123_ERRCOUNT || errcode < 0)
|
||||
return "invalid error code";
|
||||
|
||||
/* Let's be paranoid, one _may_ forget to extend errstrings when
|
||||
adding a new entry to the enum. */
|
||||
if(errcode >= sizeof(errstring)/sizeof(char*))
|
||||
return "outdated error list (library bug)";
|
||||
|
||||
return errstring[errcode];
|
||||
}
|
||||
|
||||
static int out123_seterr(audio_output_t *ao, enum out123_error errcode)
|
||||
{
|
||||
if(!ao)
|
||||
return OUT123_ERR;
|
||||
ao->errcode = errcode;
|
||||
return errcode == OUT123_OK ? OUT123_OK : OUT123_ERR;
|
||||
}
|
||||
|
||||
/* pre-playback setup */
|
||||
|
||||
int out123_set_buffer(audio_output_t *ao, size_t buffer_bytes)
|
||||
{
|
||||
debug2("out123_set_buffer(%p, %"SIZE_P")", (void*)ao, (size_p)buffer_bytes);
|
||||
if(!ao)
|
||||
return OUT123_ERR;
|
||||
ao->errcode = 0;
|
||||
/* Close any audio output module if present, also kill of buffer if present,
|
||||
then start new buffer process with newly allocated storage if given
|
||||
size is non-zero. */
|
||||
out123_close(ao);
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao))
|
||||
buffer_exit(ao);
|
||||
if(buffer_bytes)
|
||||
return buffer_init(ao, buffer_bytes);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int out123_param( audio_output_t *ao, enum out123_parms code
|
||||
, long value, double fvalue )
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
debug4("out123_param(%p, %i, %li, %g)", (void*)ao, (int)code, value, fvalue);
|
||||
if(!ao)
|
||||
return OUT123_ERR;
|
||||
ao->errcode = 0;
|
||||
|
||||
switch(code)
|
||||
{
|
||||
case OUT123_FLAGS:
|
||||
ao->flags = (int)value;
|
||||
break;
|
||||
case OUT123_PRELOAD:
|
||||
ao->preload = fvalue;
|
||||
break;
|
||||
case OUT123_GAIN:
|
||||
ao->gain = value;
|
||||
break;
|
||||
case OUT123_VERBOSE:
|
||||
ao->verbose = (int)value;
|
||||
break;
|
||||
case OUT123_DEVICEBUFFER:
|
||||
ao->device_buffer = fvalue;
|
||||
break;
|
||||
default:
|
||||
error1("bad parameter code %i", (int)code);
|
||||
ret = -1;
|
||||
}
|
||||
#ifndef NOXFERMEM
|
||||
/* If there is a buffer, it needs to update its copy of parameters. */
|
||||
if(have_buffer(ao))
|
||||
/* No error check; if that fails, buffer is dead and we will notice
|
||||
soon enough. */
|
||||
buffer_sync_param(ao);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
int out123_getparam( audio_output_t *ao, enum out123_parms code
|
||||
, long *ret_value, double *ret_fvalue )
|
||||
{
|
||||
int ret = 0;
|
||||
long value = 0;
|
||||
double fvalue = 0.;
|
||||
|
||||
debug4( "out123_getparam(%p, %i, %p, %p)"
|
||||
, (void*)ao, (int)code, (void*)ret_value, (void*)ret_fvalue );
|
||||
if(!ao)
|
||||
return OUT123_ERR;
|
||||
ao->errcode = 0;
|
||||
|
||||
switch(code)
|
||||
{
|
||||
case OUT123_FLAGS:
|
||||
value = ao->flags;
|
||||
break;
|
||||
case OUT123_PRELOAD:
|
||||
fvalue = ao->preload;
|
||||
break;
|
||||
case OUT123_GAIN:
|
||||
value = ao->gain;
|
||||
break;
|
||||
case OUT123_VERBOSE:
|
||||
value = ao->verbose;
|
||||
break;
|
||||
case OUT123_DEVICEBUFFER:
|
||||
fvalue = ao->device_buffer;
|
||||
break;
|
||||
default:
|
||||
error1("bad parameter code %i", (int)code);
|
||||
ret = OUT123_ERR;
|
||||
}
|
||||
if(!ret)
|
||||
{
|
||||
if(ret_value) *ret_value = value;
|
||||
if(ret_fvalue) *ret_fvalue = fvalue;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int out123_param_from(audio_output_t *ao, audio_output_t* from_ao)
|
||||
{
|
||||
debug2("out123_param_from(%p, %p)", (void*)ao, (void*)from_ao);
|
||||
if(!ao || !from_ao) return -1;
|
||||
|
||||
ao->flags = from_ao->flags;
|
||||
ao->preload = from_ao->preload;
|
||||
ao->gain = from_ao->gain;
|
||||
ao->device_buffer = from_ao->device_buffer;
|
||||
ao->verbose = from_ao->verbose;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Serialization of tunable parameters to communicate them between
|
||||
main process and buffer. Make sure these two stay in sync ... */
|
||||
|
||||
int write_parameters(audio_output_t *ao, int fd)
|
||||
{
|
||||
if(
|
||||
GOOD_WRITEVAL(fd, ao->flags)
|
||||
&& GOOD_WRITEVAL(fd, ao->preload)
|
||||
&& GOOD_WRITEVAL(fd, ao->gain)
|
||||
&& GOOD_WRITEVAL(fd, ao->device_buffer)
|
||||
&& GOOD_WRITEVAL(fd, ao->verbose)
|
||||
)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read_parameters(audio_output_t *ao
|
||||
, int fd, byte *prebuf, int *preoff, int presize)
|
||||
{
|
||||
#define GOOD_READVAL_BUF(fd, val) \
|
||||
!read_buf(fd, &val, sizeof(val), prebuf, preoff, presize)
|
||||
if(
|
||||
GOOD_READVAL_BUF(fd, ao->flags)
|
||||
&& GOOD_READVAL_BUF(fd, ao->preload)
|
||||
&& GOOD_READVAL_BUF(fd, ao->gain)
|
||||
&& GOOD_READVAL_BUF(fd, ao->device_buffer)
|
||||
&& GOOD_READVAL_BUF(fd, ao->verbose)
|
||||
)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
#undef GOOD_READVAL_BUF
|
||||
}
|
||||
|
||||
int out123_open(audio_output_t *ao, const char* driver, const char* device)
|
||||
{
|
||||
debug3( "out123_open(%p, %s, %s)", (void*)ao
|
||||
, driver ? driver : "<nil>", device ? device : "<nil>" );
|
||||
if(!ao)
|
||||
return OUT123_ERR;
|
||||
ao->errcode = 0;
|
||||
|
||||
out123_close(ao);
|
||||
debug("out123_open() continuing");
|
||||
|
||||
/* Ensure that audio format is freshly set for "no format yet" mode.
|
||||
In out123_start*/
|
||||
ao->rate = -1;
|
||||
ao->channels = -1;
|
||||
ao->format = -1;
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao))
|
||||
{
|
||||
if(buffer_open(ao, driver, device))
|
||||
return OUT123_ERR;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* We just quickly check if the device can be accessed at all,
|
||||
same as out123_encodings! */
|
||||
char *nextname, *modnames;
|
||||
const char *names = driver ? driver : DEFAULT_OUTPUT_MODULE;
|
||||
|
||||
if(!names) return out123_seterr(ao, OUT123_BAD_DRIVER_NAME);
|
||||
|
||||
/* It is ridiculous how these error messages are larger than the pieces
|
||||
of memory they are about! */
|
||||
if(device && !(ao->device = strdup(device)))
|
||||
{
|
||||
if(!AOQUIET) error("OOM device name copy");
|
||||
return out123_seterr(ao, OUT123_DOOM);
|
||||
}
|
||||
|
||||
if(!(modnames = strdup(names)))
|
||||
{
|
||||
out123_close(ao); /* Frees ao->device, too. */
|
||||
if(!AOQUIET) error("OOM driver names");
|
||||
return out123_seterr(ao, OUT123_DOOM);
|
||||
}
|
||||
|
||||
/* Now loop over the list of possible modules to find one that works. */
|
||||
nextname = strtok(modnames, ",");
|
||||
while(!ao->open && nextname)
|
||||
{
|
||||
char *curname = nextname;
|
||||
nextname = strtok(NULL, ",");
|
||||
check_output_module(ao, curname, device, !nextname);
|
||||
if(ao->open)
|
||||
{
|
||||
if(AOVERBOSE(2))
|
||||
fprintf(stderr, "Chosen output module: %s\n", curname);
|
||||
/* A bit redundant, but useful when it's a fake module. */
|
||||
if(!(ao->driver = strdup(curname)))
|
||||
{
|
||||
out123_close(ao);
|
||||
if(!AOQUIET) error("OOM driver name");
|
||||
return out123_seterr(ao, OUT123_DOOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(modnames);
|
||||
|
||||
if(!ao->open) /* At least an open() routine must be present. */
|
||||
{
|
||||
if(!AOQUIET)
|
||||
error2("Found no driver out of [%s] working with device %s."
|
||||
, names, device ? device : "<default>");
|
||||
/* Proper more detailed error code could be set already. */
|
||||
if(ao->errcode == OUT123_OK)
|
||||
ao->errcode = OUT123_BAD_DRIVER;
|
||||
return OUT123_ERR;
|
||||
}
|
||||
}
|
||||
/* Got something. */
|
||||
ao->state = play_stopped;
|
||||
return OUT123_OK;
|
||||
}
|
||||
|
||||
/* Be resilient, always do cleanup work regardless of state. */
|
||||
void out123_close(audio_output_t *ao)
|
||||
{
|
||||
debug1("out123_close(%p)", (void*)ao);
|
||||
if(!ao)
|
||||
return;
|
||||
ao->errcode = 0;
|
||||
|
||||
out123_drain(ao);
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao))
|
||||
buffer_close(ao);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if(ao->close)
|
||||
{
|
||||
int ret;
|
||||
if((ret=ao->close(ao)))
|
||||
{
|
||||
if(!AOQUIET)
|
||||
error1("ao->close() returned %i", ret);
|
||||
}
|
||||
}
|
||||
if(ao->deinit)
|
||||
ao->deinit(ao);
|
||||
if(ao->module)
|
||||
close_module(ao->module, modverbose(ao));
|
||||
/* Null module methods and pointer. */
|
||||
out123_clear_module(ao);
|
||||
}
|
||||
|
||||
/* These copies exist in addition to the ones for the buffer. */
|
||||
if(ao->driver)
|
||||
free(ao->driver);
|
||||
ao->driver = NULL;
|
||||
if(ao->device)
|
||||
free(ao->device);
|
||||
ao->device = NULL;
|
||||
|
||||
ao->state = play_dead;
|
||||
}
|
||||
|
||||
int out123_start( audio_output_t *ao
|
||||
, int encoding, int channels, long rate )
|
||||
{
|
||||
debug4( "out123_start(%p, %i, %i, %li)"
|
||||
, (void*)ao, (int)encoding, channels, rate );
|
||||
if(!ao)
|
||||
return OUT123_ERR;
|
||||
ao->errcode = 0;
|
||||
|
||||
out123_stop(ao);
|
||||
debug("out123_start() continuing");
|
||||
if(ao->state != play_stopped)
|
||||
return out123_seterr(ao, OUT123_NO_DRIVER);
|
||||
|
||||
/* Stored right away as parameters for ao->open() and also for reference.
|
||||
framesize needed for out123_play(). */
|
||||
ao->rate = rate;
|
||||
ao->channels = channels;
|
||||
ao->format = encoding;
|
||||
ao->framesize = out123_samplesize(encoding)*channels;
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao))
|
||||
{
|
||||
if(!buffer_start(ao))
|
||||
{
|
||||
ao->state = play_live;
|
||||
return OUT123_OK;
|
||||
}
|
||||
else
|
||||
return OUT123_ERR;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if(ao->open(ao) < 0)
|
||||
return out123_seterr(ao, OUT123_DEV_OPEN);
|
||||
ao->state = play_live;
|
||||
return OUT123_OK;
|
||||
}
|
||||
}
|
||||
|
||||
void out123_pause(audio_output_t *ao)
|
||||
{
|
||||
debug1("out123_pause(%p)", (void*)ao);
|
||||
if(ao && ao->state == play_live)
|
||||
{
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao)) buffer_pause(ao);
|
||||
#endif
|
||||
/* TODO: Be nice and prepare output device for silence. */
|
||||
ao->state = play_paused;
|
||||
}
|
||||
}
|
||||
|
||||
void out123_continue(audio_output_t *ao)
|
||||
{
|
||||
debug1("out123_continue(%p)", (void*)ao);
|
||||
if(ao && ao->state == play_paused)
|
||||
{
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao)) buffer_continue(ao);
|
||||
#endif
|
||||
/* TODO: Revitalize device for resuming playback. */
|
||||
ao->state = play_live;
|
||||
}
|
||||
}
|
||||
|
||||
void out123_stop(audio_output_t *ao)
|
||||
{
|
||||
debug1("out123_stop(%p)", (void*)ao);
|
||||
if(!ao)
|
||||
return;
|
||||
ao->errcode = 0;
|
||||
if(!(ao->state == play_paused || ao->state == play_live))
|
||||
return;
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao))
|
||||
buffer_stop(ao);
|
||||
else
|
||||
#endif
|
||||
if(ao->close && ao->close(ao) && !AOQUIET)
|
||||
error("trouble closing device");
|
||||
ao->state = play_stopped;
|
||||
}
|
||||
|
||||
size_t out123_play(audio_output_t *ao, void *bytes, size_t count)
|
||||
{
|
||||
size_t sum = 0;
|
||||
int written;
|
||||
|
||||
debug3("out123_play(%p, %p, %"SIZE_P")", (void*)ao, bytes, (size_p)count);
|
||||
if(!ao)
|
||||
return 0;
|
||||
ao->errcode = 0;
|
||||
if(ao->state != play_live)
|
||||
{
|
||||
ao->errcode = OUT123_NOT_LIVE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ensure that we are writing whole PCM frames. */
|
||||
count -= count % ao->framesize;
|
||||
if(!count) return 0;
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao))
|
||||
return buffer_write(ao, bytes, count);
|
||||
else
|
||||
#endif
|
||||
do /* Playback in a loop to be able to continue after interruptions. */
|
||||
{
|
||||
written = ao->write(ao, (unsigned char*)bytes, (int)count);
|
||||
if(written >= 0){ sum+=written; count -= written; }
|
||||
else
|
||||
{
|
||||
ao->errcode = OUT123_DEV_PLAY;
|
||||
if(!AOQUIET)
|
||||
error1("Error in writing audio (%s?)!", strerror(errno));
|
||||
/* If written < 0, this is a serious issue ending this playback round. */
|
||||
break;
|
||||
}
|
||||
} while(count && ao->flags & OUT123_KEEP_PLAYING);
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* Drop means to flush it down. Quickly. */
|
||||
void out123_drop(audio_output_t *ao)
|
||||
{
|
||||
debug1("out123_drop(%p)", (void*)ao);
|
||||
if(!ao)
|
||||
return;
|
||||
ao->errcode = 0;
|
||||
#ifndef NO_XFERMEM
|
||||
if(have_buffer(ao))
|
||||
buffer_drop(ao);
|
||||
else
|
||||
#endif
|
||||
if(ao->state == play_live)
|
||||
{
|
||||
if(ao->flush) ao->flush(ao);
|
||||
}
|
||||
}
|
||||
|
||||
void out123_drain(audio_output_t *ao)
|
||||
{
|
||||
debug1("out123_drain(%p)", (void*)ao);
|
||||
if(!ao)
|
||||
return;
|
||||
ao->errcode = 0;
|
||||
if(ao->state != play_live)
|
||||
return;
|
||||
#ifndef NO_XFERMEM
|
||||
if(have_buffer(ao))
|
||||
buffer_drain(ao);
|
||||
else
|
||||
#endif
|
||||
if(ao->drain)
|
||||
ao->drain(ao);
|
||||
}
|
||||
|
||||
void out123_ndrain(audio_output_t *ao, size_t bytes)
|
||||
{
|
||||
debug2("out123_ndrain(%p, %"SIZE_P")", (void*)ao, (size_p)bytes);
|
||||
if(!ao)
|
||||
return;
|
||||
ao->errcode = 0;
|
||||
if(ao->state != play_live)
|
||||
return;
|
||||
#ifndef NO_XFERMEM
|
||||
if(have_buffer(ao))
|
||||
buffer_ndrain(ao, bytes);
|
||||
else
|
||||
#endif
|
||||
if(ao->drain)
|
||||
ao->drain(ao);
|
||||
}
|
||||
|
||||
|
||||
/* A function that does nothing and returns nothing. */
|
||||
static void builtin_nothing(audio_output_t *ao){}
|
||||
static int test_open(audio_output_t *ao)
|
||||
{
|
||||
debug("test_open");
|
||||
return OUT123_OK;
|
||||
}
|
||||
static int test_get_formats(audio_output_t *ao)
|
||||
{
|
||||
debug("test_get_formats");
|
||||
return MPG123_ENC_ANY;
|
||||
}
|
||||
static int test_write(audio_output_t *ao, unsigned char *buf, int len)
|
||||
{
|
||||
debug2("test_write: %i B from %p", len, (void*)buf);
|
||||
return len;
|
||||
}
|
||||
static void test_flush(audio_output_t *ao)
|
||||
{
|
||||
debug("test_flush");
|
||||
}
|
||||
static void test_drain(audio_output_t *ao)
|
||||
{
|
||||
debug("test_drain");
|
||||
}
|
||||
static int test_close(audio_output_t *ao)
|
||||
{
|
||||
debug("test_drain");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Open one of our builtin driver modules. */
|
||||
static int open_fake_module(audio_output_t *ao, const char *driver)
|
||||
{
|
||||
if(!strcmp("test", driver))
|
||||
{
|
||||
ao->open = test_open;
|
||||
ao->get_formats = test_get_formats;
|
||||
ao->write = test_write;
|
||||
ao->flush = test_flush;
|
||||
ao->drain = test_drain;
|
||||
ao->close = test_close;
|
||||
}
|
||||
else
|
||||
if(!strcmp("raw", driver))
|
||||
{
|
||||
ao->open = raw_open;
|
||||
ao->get_formats = raw_formats;
|
||||
ao->write = wav_write;
|
||||
ao->flush = builtin_nothing;
|
||||
ao->drain = wav_drain;
|
||||
ao->close = raw_close;
|
||||
}
|
||||
else
|
||||
if(!strcmp("wav", driver))
|
||||
{
|
||||
ao->open = wav_open;
|
||||
ao->get_formats = wav_formats;
|
||||
ao->write = wav_write;
|
||||
ao->flush = builtin_nothing;
|
||||
ao->drain = wav_drain;
|
||||
ao->close = wav_close;
|
||||
}
|
||||
else
|
||||
if(!strcmp("cdr", driver))
|
||||
{
|
||||
ao->open = cdr_open;
|
||||
ao->get_formats = cdr_formats;
|
||||
ao->write = wav_write;
|
||||
ao->flush = builtin_nothing;
|
||||
ao->drain = wav_drain;
|
||||
ao->close = raw_close;
|
||||
}
|
||||
else
|
||||
if(!strcmp("au", driver))
|
||||
{
|
||||
ao->open = au_open;
|
||||
ao->get_formats = au_formats;
|
||||
ao->write = wav_write;
|
||||
ao->flush = builtin_nothing;
|
||||
ao->drain = wav_drain;
|
||||
ao->close = au_close;
|
||||
}
|
||||
else return OUT123_ERR;
|
||||
|
||||
return OUT123_OK;
|
||||
}
|
||||
|
||||
/* Check if given output module is loadable and has a working device.
|
||||
final flag triggers printing and storing of errors. */
|
||||
static void check_output_module( audio_output_t *ao
|
||||
, const char *name, const char *device, int final )
|
||||
{
|
||||
int result;
|
||||
|
||||
if(AOVERBOSE(1))
|
||||
fprintf(stderr, "Trying output module: %s\n", name);
|
||||
|
||||
/* Use internal code. */
|
||||
if(open_fake_module(ao, name) == OUT123_OK)
|
||||
return;
|
||||
|
||||
/* Open the module, initial check for availability+libraries. */
|
||||
ao->module = open_module( "output", name, modverbose(ao));
|
||||
if(!ao->module)
|
||||
return;
|
||||
/* Check if module supports output */
|
||||
if(!ao->module->init_output)
|
||||
{
|
||||
if(final)
|
||||
error1("Module '%s' does not support audio output.", name);
|
||||
goto check_output_module_cleanup;
|
||||
}
|
||||
|
||||
/* Should I do funny stuff with stderr file descriptor instead? */
|
||||
if(final)
|
||||
{
|
||||
if(AOVERBOSE(2))
|
||||
fprintf(stderr
|
||||
, "Note: %s is the last output option... showing you any error messages now.\n"
|
||||
, name);
|
||||
}
|
||||
else ao->auxflags |= OUT123_QUIET; /* Probing, so don't spill stderr with errors. */
|
||||
result = ao->module->init_output(ao);
|
||||
if(result == 0)
|
||||
{ /* Try to open the device. I'm only interested in actually working modules. */
|
||||
ao->format = -1;
|
||||
result = ao->open(ao);
|
||||
debug1("ao->open() = %i", result);
|
||||
ao->close(ao);
|
||||
}
|
||||
else if(!AOQUIET)
|
||||
error2("Module '%s' init failed: %i", name, result);
|
||||
|
||||
ao->auxflags &= ~OUT123_QUIET;
|
||||
|
||||
if(result >= 0)
|
||||
return;
|
||||
|
||||
check_output_module_cleanup:
|
||||
/* Only if module did not check out we get to clean up here. */
|
||||
close_module(ao->module, modverbose(ao));
|
||||
out123_clear_module(ao);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
static void audio_output_dump(audio_output_t *ao)
|
||||
{
|
||||
fprintf(stderr, "ao->fn=%d\n", ao->fn);
|
||||
fprintf(stderr, "ao->userptr=%p\n", ao->userptr);
|
||||
fprintf(stderr, "ao->rate=%ld\n", ao->rate);
|
||||
fprintf(stderr, "ao->gain=%ld\n", ao->gain);
|
||||
fprintf(stderr, "ao->device='%s'\n", ao->device);
|
||||
fprintf(stderr, "ao->channels=%d\n", ao->channels);
|
||||
fprintf(stderr, "ao->format=%d\n", ao->format);
|
||||
}
|
||||
*/
|
||||
|
||||
int out123_drivers(audio_output_t *ao, char ***names, char ***descr)
|
||||
{
|
||||
char **tmpnames;
|
||||
char **tmpdescr;
|
||||
int count;
|
||||
int i;
|
||||
|
||||
if(!ao)
|
||||
return -1;
|
||||
|
||||
debug3("out123_drivers(%p, %p, %p)", (void*)ao, (void*)names, (void*)descr);
|
||||
/* Wrap the call to isolate the lower levels from the user not being
|
||||
interested in both lists. it's a bit wasteful, but the code looks
|
||||
ugly enough already down there. */
|
||||
count = list_modules("output", &tmpnames, &tmpdescr, modverbose(ao));
|
||||
|
||||
if(count < 0)
|
||||
return count;
|
||||
|
||||
/* Return or free gathered lists of names or descriptions. */
|
||||
if(names)
|
||||
*names = tmpnames;
|
||||
else
|
||||
{
|
||||
for(i=0; i<count; ++i)
|
||||
free(tmpnames[i]);
|
||||
free(tmpnames);
|
||||
}
|
||||
if(descr)
|
||||
*descr = tmpdescr;
|
||||
else
|
||||
{
|
||||
for(i=0; i<count; ++i)
|
||||
free(tmpdescr[i]);
|
||||
free(tmpdescr);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* We always have ao->driver and ao->device set, also with buffer.
|
||||
The latter can be positively NULL, though. */
|
||||
int out123_driver_info(audio_output_t *ao, char **driver, char **device)
|
||||
{
|
||||
debug3( "out123_driver_info(%p, %p, %p)"
|
||||
, (void*)ao, (void*)driver, (void*)device );
|
||||
if(!ao)
|
||||
return OUT123_ERR;
|
||||
if(!ao->driver)
|
||||
return out123_seterr(ao, OUT123_NO_DRIVER);
|
||||
|
||||
if(driver)
|
||||
*driver = ao->driver;
|
||||
if(device)
|
||||
*device = ao->device;
|
||||
return OUT123_OK;
|
||||
}
|
||||
|
||||
int out123_encodings(audio_output_t *ao, int channels, long rate)
|
||||
{
|
||||
debug3("out123_encodings(%p, %i, %li)", (void*)ao, channels, rate);
|
||||
if(!ao)
|
||||
return OUT123_ERR;
|
||||
ao->errcode = OUT123_OK;
|
||||
|
||||
out123_stop(ao); /* That brings the buffer into waiting state, too. */
|
||||
|
||||
if(ao->state != play_stopped)
|
||||
return out123_seterr(ao, OUT123_NO_DRIVER);
|
||||
|
||||
ao->channels = channels;
|
||||
ao->rate = rate;
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao))
|
||||
return buffer_encodings(ao);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
int enc = 0;
|
||||
ao->format = -1;
|
||||
if(ao->open(ao) >= 0)
|
||||
{
|
||||
enc = ao->get_formats(ao);
|
||||
ao->close(ao);
|
||||
return enc;
|
||||
}
|
||||
else
|
||||
return out123_seterr(ao, (ao->errcode != OUT123_OK
|
||||
? ao->errcode
|
||||
: OUT123_DEV_OPEN));
|
||||
}
|
||||
}
|
||||
|
||||
size_t out123_buffered(audio_output_t *ao)
|
||||
{
|
||||
debug1("out123_buffered(%p)", (void*)ao);
|
||||
if(!ao)
|
||||
return 0;
|
||||
ao->errcode = 0;
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao))
|
||||
return buffer_fill(ao);
|
||||
else
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int out123_getformat( audio_output_t *ao
|
||||
, long *rate, int *channels, int *encoding, int *framesize )
|
||||
{
|
||||
if(!ao)
|
||||
return OUT123_ERR;
|
||||
|
||||
if(!(ao->state == play_paused || ao->state == play_live))
|
||||
return out123_seterr(ao, OUT123_NOT_LIVE);
|
||||
|
||||
if(rate)
|
||||
*rate = ao->rate;
|
||||
if(channels)
|
||||
*channels = ao->channels;
|
||||
if(encoding)
|
||||
*encoding = ao->format;
|
||||
if(framesize)
|
||||
*framesize = ao->framesize;
|
||||
return OUT123_OK;
|
||||
}
|
@@ -5,15 +5,16 @@
|
||||
see COPYING and AUTHORS files in distribution or http://mpg123.org
|
||||
initially written by Nicholas J Humfrey
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "out123_intsym.h"
|
||||
#include "compat.h"
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <ltdl.h>
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "module.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifndef HAVE_LTDL
|
||||
@@ -32,8 +33,8 @@ static const char* modulesearch[] =
|
||||
,"plugins"
|
||||
};
|
||||
|
||||
static char *get_the_cwd(); /* further down... */
|
||||
static char *get_module_dir()
|
||||
static char *get_the_cwd(int verbose); /* further down... */
|
||||
static char *get_module_dir(int verbose)
|
||||
{
|
||||
/* Either PKGLIBDIR is accessible right away or we search for some possible plugin dirs relative to binary path. */
|
||||
DIR* dir = NULL;
|
||||
@@ -43,13 +44,16 @@ static char *get_module_dir()
|
||||
defaultdir = getenv("MPG123_MODDIR");
|
||||
if(defaultdir == NULL)
|
||||
defaultdir=PKGLIBDIR;
|
||||
else if(verbose > 1)
|
||||
fprintf(stderr, "Trying module directory from environment: %s\n", defaultdir);
|
||||
|
||||
dir = opendir(defaultdir);
|
||||
if(dir != NULL)
|
||||
{
|
||||
size_t l = strlen(defaultdir);
|
||||
|
||||
if(param.verbose > 1) fprintf(stderr, "Using default module dir: %s\n", defaultdir);
|
||||
if(verbose > 1)
|
||||
fprintf(stderr, "Using default module dir: %s\n", defaultdir);
|
||||
moddir = malloc(l+1);
|
||||
if(moddir != NULL)
|
||||
{
|
||||
@@ -65,19 +69,21 @@ static char *get_module_dir()
|
||||
{
|
||||
const char *testpath = modulesearch[i];
|
||||
size_t l;
|
||||
if(binpath != NULL) l = strlen(binpath) + strlen(testpath) + 1;
|
||||
else l = strlen(testpath);
|
||||
fprintf(stderr, "TODO: module search relative to binary path\n");
|
||||
/* if(binpath != NULL) l = strlen(binpath) + strlen(testpath) + 1;
|
||||
else */ l = strlen(testpath);
|
||||
|
||||
moddir = malloc(l+1);
|
||||
if(moddir != NULL)
|
||||
{
|
||||
if(binpath==NULL) /* a copy of testpath, when there is no prefix */
|
||||
/*if(binpath==NULL)*/ /* a copy of testpath, when there is no prefix */
|
||||
snprintf(moddir, l+1, "%s", testpath);
|
||||
else
|
||||
snprintf(moddir, l+1, "%s/%s", binpath, testpath);
|
||||
/* else
|
||||
snprintf(moddir, l+1, "%s/%s", binpath, testpath); */
|
||||
|
||||
moddir[l] = 0;
|
||||
if(param.verbose > 1) fprintf(stderr, "Looking for module dir: %s\n", moddir);
|
||||
if(verbose > 1)
|
||||
fprintf(stderr, "Looking for module dir: %s\n", moddir);
|
||||
|
||||
dir = opendir(moddir);
|
||||
if(dir != NULL)
|
||||
@@ -89,12 +95,13 @@ static char *get_module_dir()
|
||||
}
|
||||
}
|
||||
}
|
||||
if(param.verbose > 1) fprintf(stderr, "Module dir: %s\n", moddir != NULL ? moddir : "<nil>");
|
||||
if(verbose > 1)
|
||||
fprintf(stderr, "Module dir: %s\n", moddir != NULL ? moddir : "<nil>");
|
||||
return moddir;
|
||||
}
|
||||
|
||||
/* Open a module in current directory. */
|
||||
mpg123_module_t* open_module_here(const char* type, const char* name)
|
||||
mpg123_module_t* open_module_here(const char* type, const char* name, int verbose)
|
||||
{
|
||||
lt_dlhandle handle = NULL;
|
||||
mpg123_module_t *module = NULL;
|
||||
@@ -106,6 +113,7 @@ mpg123_module_t* open_module_here(const char* type, const char* name)
|
||||
/* Initialize libltdl */
|
||||
if(lt_dlinit())
|
||||
{
|
||||
if(verbose > -1)
|
||||
error("Failed to initialise libltdl");
|
||||
return NULL;
|
||||
}
|
||||
@@ -115,19 +123,21 @@ mpg123_module_t* open_module_here(const char* type, const char* name)
|
||||
module_path_len = 2 + strlen(type) + 1 + strlen(name) + strlen(MODULE_FILE_SUFFIX) + 1;
|
||||
module_path = malloc( module_path_len );
|
||||
if (module_path == NULL) {
|
||||
if(verbose > -1)
|
||||
error1( "Failed to allocate memory for module name: %s", strerror(errno) );
|
||||
return NULL;
|
||||
}
|
||||
snprintf( module_path, module_path_len, "./%s_%s%s", type, name, MODULE_FILE_SUFFIX );
|
||||
/* Display the path of the module created */
|
||||
if(param.verbose > 1) fprintf(stderr, "Module path: %s\n", module_path );
|
||||
if(verbose > 1)
|
||||
fprintf(stderr, "Module path: %s\n", module_path );
|
||||
|
||||
/* Open the module */
|
||||
handle = lt_dlopen( module_path );
|
||||
free( module_path );
|
||||
if (handle==NULL) {
|
||||
error2( "Failed to open module %s: %s", name, lt_dlerror() );
|
||||
if(param.verbose > 1)
|
||||
if(verbose > 1)
|
||||
fprintf(stderr, "Note: This could be because of braindead path in the .la file...\n");
|
||||
|
||||
return NULL;
|
||||
@@ -139,6 +149,7 @@ mpg123_module_t* open_module_here(const char* type, const char* name)
|
||||
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;
|
||||
}
|
||||
@@ -168,26 +179,31 @@ mpg123_module_t* open_module_here(const char* type, const char* name)
|
||||
|
||||
|
||||
/* Open a module, including directory search. */
|
||||
mpg123_module_t* open_module(const char* type, const char* name)
|
||||
mpg123_module_t* open_module(const char* type, const char* name, int verbose)
|
||||
{
|
||||
mpg123_module_t *module = NULL;
|
||||
char *workdir = NULL;
|
||||
char *moddir = NULL;
|
||||
|
||||
workdir = get_the_cwd();
|
||||
moddir = get_module_dir();
|
||||
workdir = get_the_cwd(verbose);
|
||||
moddir = get_module_dir(verbose);
|
||||
if(workdir == NULL || moddir == NULL)
|
||||
{
|
||||
if(verbose > -1)
|
||||
{
|
||||
error("Failure getting workdir or moddir! (Perhaps set MPG123_MODDIR?)");
|
||||
if(workdir == NULL) fprintf(stderr, "Hint: I need to know the current working directory to be able to come back after hunting modules. I will not leave because I do not know where I am.\n");
|
||||
|
||||
if(workdir == NULL)
|
||||
fprintf(stderr, "Hint: I need to know the current working directory to be able to come back after hunting modules. I will not leave because I do not know where I am.\n");
|
||||
}
|
||||
if(workdir != NULL) free(workdir);
|
||||
if(moddir != NULL) free(moddir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(chdir(moddir) == 0) module = open_module_here(type, name);
|
||||
else error2("Failed to enter module directory %s: %s", moddir, strerror(errno));
|
||||
if(chdir(moddir) == 0)
|
||||
module = open_module_here(type, name, verbose);
|
||||
else if(verbose > -1)
|
||||
error2("Failed to enter module directory %s: %s", moddir, strerror(errno));
|
||||
|
||||
chdir(workdir);
|
||||
free(moddir);
|
||||
@@ -195,17 +211,18 @@ mpg123_module_t* open_module(const char* type, const char* name)
|
||||
return module;
|
||||
}
|
||||
|
||||
void close_module( mpg123_module_t* module )
|
||||
void close_module( mpg123_module_t* module, int verbose )
|
||||
{
|
||||
lt_dlhandle handle = module->handle;
|
||||
int err = lt_dlclose( handle );
|
||||
|
||||
if (err) error1("Failed to close module: %s", lt_dlerror() );
|
||||
if(err && verbose > -1)
|
||||
error1("Failed to close module: %s", lt_dlerror());
|
||||
|
||||
}
|
||||
|
||||
#define PATH_STEP 50
|
||||
static char *get_the_cwd()
|
||||
static char *get_the_cwd(int verbose)
|
||||
{
|
||||
size_t bs = PATH_STEP;
|
||||
char *buf = malloc(bs);
|
||||
@@ -215,6 +232,7 @@ static char *get_the_cwd()
|
||||
char *buf2;
|
||||
if(errno != ERANGE)
|
||||
{
|
||||
if(verbose > -1)
|
||||
error1("getcwd returned unexpected error: %s", strerror(errno));
|
||||
free(buf);
|
||||
return NULL;
|
||||
@@ -228,85 +246,125 @@ static char *get_the_cwd()
|
||||
return buf;
|
||||
}
|
||||
|
||||
void list_modules()
|
||||
int list_modules(const char *type, char ***names, char ***descr, int verbose)
|
||||
{
|
||||
DIR* dir = NULL;
|
||||
struct dirent *dp = NULL;
|
||||
char *moddir = NULL;
|
||||
int count = 0;
|
||||
|
||||
moddir = get_module_dir();
|
||||
*names = NULL;
|
||||
*descr = NULL;
|
||||
|
||||
moddir = get_module_dir(verbose);
|
||||
if(moddir == NULL)
|
||||
{
|
||||
if(verbose > -1)
|
||||
error("Failure getting module directory! (Perhaps set MPG123_MODDIR?)");
|
||||
exit(-1); /* TODO: change this to return a value instead of exit()! */
|
||||
return -1;
|
||||
}
|
||||
/* Open the module directory */
|
||||
dir = opendir(moddir);
|
||||
dir = moddir != NULL ? opendir(moddir) : NULL;
|
||||
if (dir==NULL) {
|
||||
error2("Failed to open the module directory (%s): %s\n", PKGLIBDIR, strerror(errno));
|
||||
if(verbose > -1)
|
||||
error2("Failed to open the module directory (%s): %s\n"
|
||||
, PKGLIBDIR, strerror(errno));
|
||||
free(moddir);
|
||||
exit(-1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(chdir(moddir) != 0)
|
||||
{
|
||||
error2("Failed to enter module directory (%s): %s\n", PKGLIBDIR, strerror(errno));
|
||||
closedir( dir );
|
||||
if(verbose > -1)
|
||||
error2("Failed to enter module directory (%s): %s\n"
|
||||
, PKGLIBDIR, strerror(errno));
|
||||
closedir(dir);
|
||||
free(moddir);
|
||||
exit(-1);
|
||||
return -1;
|
||||
}
|
||||
/* Display the program title */
|
||||
/* print_title(stderr); */
|
||||
free(moddir);
|
||||
|
||||
/* List the output modules */
|
||||
printf("\n");
|
||||
printf("Available modules\n");
|
||||
printf("-----------------\n");
|
||||
|
||||
while( (dp = readdir(dir)) != NULL ) {
|
||||
struct stat fst;
|
||||
if(stat(dp->d_name, &fst) != 0) continue;
|
||||
if(S_ISREG(fst.st_mode)) /* Allow links? */
|
||||
{
|
||||
char* ext = dp->d_name + strlen( dp->d_name ) - strlen( MODULE_FILE_SUFFIX );
|
||||
if (strcmp(ext, MODULE_FILE_SUFFIX) == 0)
|
||||
while( count >= 0 && (dp = readdir(dir)) != NULL )
|
||||
{
|
||||
char *module_name = NULL;
|
||||
char *module_type = NULL;
|
||||
char *uscore_pos = NULL;
|
||||
mpg123_module_t *module = NULL;
|
||||
char* ext;
|
||||
struct stat fst;
|
||||
|
||||
/* Various checks as loop shortcuts, avoiding too much nesting. */
|
||||
|
||||
if(stat(dp->d_name, &fst) != 0)
|
||||
continue;
|
||||
if(!S_ISREG(fst.st_mode)) /* Allow links? */
|
||||
continue;
|
||||
|
||||
ext = dp->d_name
|
||||
+ strlen(dp->d_name)
|
||||
- strlen(MODULE_FILE_SUFFIX);
|
||||
if(strcmp(ext, MODULE_FILE_SUFFIX))
|
||||
continue;
|
||||
|
||||
/* Extract the module type */
|
||||
module_type = strdup( dp->d_name );
|
||||
if(!(module_type=strdup(dp->d_name)))
|
||||
continue;
|
||||
/* Only list modules of desired type. */
|
||||
if(strcmp(type, module_type))
|
||||
continue;
|
||||
uscore_pos = strchr( module_type, '_' );
|
||||
if (uscore_pos==NULL || (uscore_pos>=module_type+strlen(module_type)+1) )
|
||||
if( uscore_pos==NULL
|
||||
|| (uscore_pos>=module_type+strlen(module_type)+1) )
|
||||
{
|
||||
free(module_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
*uscore_pos = '\0';
|
||||
|
||||
/* Extract the short name of the module */
|
||||
module_name = strdup( dp->d_name + strlen( module_type ) + 1 );
|
||||
module_name[ strlen( module_name ) - strlen( MODULE_FILE_SUFFIX ) ] = '\0';
|
||||
module_name = strdup(dp->d_name + strlen(module_type) + 1);
|
||||
if(!module_name)
|
||||
{
|
||||
free(module_type);
|
||||
continue;
|
||||
}
|
||||
module_name[strlen(module_name)-strlen(MODULE_FILE_SUFFIX)] = '\0';
|
||||
|
||||
/* Open the module */
|
||||
module = open_module_here(module_type, module_name);
|
||||
if (module) {
|
||||
printf("%-15s%s %s\n", module->name, module_type, module->description );
|
||||
|
||||
if((module=open_module_here(module_type, module_name, verbose)))
|
||||
{
|
||||
char **more_names = NULL;
|
||||
char **more_descr = NULL;
|
||||
char *name = NULL;
|
||||
char *description = NULL;
|
||||
if(
|
||||
(name=strdup(module->name))
|
||||
&& (description=strdup(module->description))
|
||||
&& (more_names=safe_realloc(*names, sizeof(char*)*(count+1)))
|
||||
&& (more_descr=safe_realloc(*descr, sizeof(char*)*(count+1)))
|
||||
)
|
||||
{
|
||||
*names = more_names;
|
||||
*descr = more_descr;
|
||||
(*names)[count] = name;
|
||||
(*descr)[count] = description;
|
||||
++count;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(more_descr);
|
||||
free(more_names);
|
||||
free(description);
|
||||
free(name);
|
||||
}
|
||||
/* Close the module again */
|
||||
close_module( module );
|
||||
close_module(module, verbose);
|
||||
}
|
||||
free( module_name );
|
||||
free( module_type );
|
||||
free(module_name);
|
||||
free(module_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir( dir );
|
||||
free(moddir);
|
||||
exit(0);
|
||||
closedir(dir);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@@ -1,20 +1,20 @@
|
||||
/*
|
||||
module: module loading and listing interface
|
||||
|
||||
copyright ?-2007 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
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_
|
||||
|
||||
/* Pulled in by mpg123app.h! */
|
||||
|
||||
#ifdef HAVE_LTDL
|
||||
#include <ltdl.h>
|
||||
#endif
|
||||
|
||||
#define MPG123_MODULE_API_VERSION (1)
|
||||
/* 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;
|
||||
@@ -41,8 +41,8 @@ typedef struct mpg123_module_struct {
|
||||
|
||||
/* ------ Declarations from "module.c" ------ */
|
||||
|
||||
mpg123_module_t* open_module( const char* type, const char* name );
|
||||
void close_module( mpg123_module_t* module );
|
||||
void list_modules();
|
||||
mpg123_module_t* open_module(const char* type, const char* name, int verbose);
|
||||
void close_module(mpg123_module_t* module, int verbose);
|
||||
int list_modules(const char *type, char ***names, char ***descr, int verbose);
|
||||
|
||||
#endif
|
732
src/libout123/modules/Makemodule.am
Normal file
732
src/libout123/modules/Makemodule.am
Normal file
@@ -0,0 +1,732 @@
|
||||
# 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.
|
||||
noinst_LTLIBRARIES += src/libout123/modules/libdefaultmodule.la
|
||||
|
||||
# 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 = \@${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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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 = @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
|
||||
|
||||
|
||||
|
@@ -19,7 +19,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* use AUDIO_BSIZE to set the msec for audio buffering in Ultimedia library
|
@@ -30,7 +30,7 @@
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
initially written by Clemens Ladisch <clemens@ladisch.de>
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include "audio.h"
|
||||
#include "module.h"
|
||||
#include <errno.h>
|
||||
@@ -16,6 +16,7 @@
|
||||
#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"
|
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include <artsc.h>
|
||||
#include "debug.h"
|
||||
|
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
@@ -5,7 +5,7 @@
|
||||
see COPYING and AUTHORS files in distribution or http://mpg123.org
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include "debug.h"
|
||||
|
||||
static int open_dummy(audio_output_t *ao)
|
@@ -8,7 +8,7 @@
|
||||
|
||||
/* First the common header, including config.h
|
||||
...this is important for stuff like _FILE_OFFSET_BITS */
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
|
||||
#include <esd.h>
|
||||
#include <errno.h>
|
@@ -6,7 +6,7 @@
|
||||
initially written by Michael Hipp
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include <fcntl.h>
|
||||
#include <sys/audio.h>
|
||||
#include "debug.h"
|
@@ -12,7 +12,7 @@
|
||||
#include <jack/ringbuffer.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define MAX_CHANNELS (2)
|
@@ -8,7 +8,7 @@
|
||||
|
||||
/* derived from LINUX, VOXWARE and SUN for MiNT Audio Device by Petr Stehlik */
|
||||
#include <fcntl.h>
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include <ioctl.h>
|
||||
#include <audios.h>
|
||||
#include "debug.h"
|
@@ -6,7 +6,7 @@
|
||||
initially written by Martin Denn
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include <fcntl.h>
|
||||
#include <audio/audiolib.h>
|
||||
#include <audio/soundlib.h>
|
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
|
||||
#ifdef OPENAL_SUBDIR_OPENAL
|
||||
#include <OpenAL/al.h>
|
@@ -17,7 +17,7 @@
|
||||
|
||||
/* I guess this #ifndef could be removed now... */
|
||||
#ifndef MPG123_INCLUDED
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#endif
|
||||
#include "debug.h"
|
||||
|
@@ -9,7 +9,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
|
||||
#ifdef HAVE_LINUX_SOUNDCARD_H
|
||||
#include <linux/soundcard.h>
|
@@ -11,7 +11,7 @@
|
||||
#include <portaudio.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
@@ -14,7 +14,7 @@
|
||||
#include <pulse/error.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include "audio.h"
|
||||
#include "module.h"
|
||||
#include "debug.h"
|
@@ -7,7 +7,7 @@
|
||||
written by Mike Gorchak <mike.gorchak.qnx@gmail.com>
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include "audio.h"
|
||||
#include "module.h"
|
||||
#include <errno.h>
|
@@ -11,7 +11,7 @@
|
||||
#include <SDL.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <dmedia/audio.h>
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include "errno.h"
|
||||
#include "debug.h"
|
||||
|
@@ -17,7 +17,7 @@
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
|
||||
#include <sndio.h>
|
||||
|
@@ -6,7 +6,7 @@
|
||||
initially written by Michael Hipp
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
@@ -7,7 +7,7 @@
|
||||
initially written by Jarno Lehtinen <lehtinen@sci.fi>
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include "audio.h"
|
||||
#include "module.h"
|
||||
#include <errno.h>
|
@@ -9,7 +9,7 @@
|
||||
Closing buffer playback fixed by David Wohlferd <limegreensocks (*) yahoo dod com>
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include <windows.h>
|
||||
#include "debug.h"
|
||||
|
@@ -9,7 +9,7 @@
|
||||
#define COBJMACROS 1
|
||||
#define _WIN32_WINNT 0x601
|
||||
#include <initguid.h>
|
||||
#include "mpg123app.h"
|
||||
#include "out123_int.h"
|
||||
#include <audioclient.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <avrt.h>
|
469
src/libout123/out123.h.in
Normal file
469
src/libout123/out123.h.in
Normal file
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
out123: audio output 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 as audio.h by Michael Hipp, reworked into out123 API
|
||||
by Thomas Orgis
|
||||
*/
|
||||
|
||||
/* API TODO:
|
||||
- rename audio_output_t to out123_handle
|
||||
- ensure that mpg123_encsize() is not used when deciding not to rely on
|
||||
libmpg123; easy enough to reimplement. Should not have been a function
|
||||
from the beginning!
|
||||
- Consider zero-copy mode. Ideally, one asks the buffer process for
|
||||
a memory block to write to and avoids any copying.
|
||||
Only trouble is that a desired size might not fit in one piece into
|
||||
the ringbuffer.
|
||||
Same goes for audio driver backends, some might offer the buffers to
|
||||
the client. But this is post-release of the first incarnation:
|
||||
The library should exist and provide functionality first before
|
||||
spending time on irrelevant optimizations.
|
||||
*/
|
||||
|
||||
#ifndef _OUT123_H_
|
||||
#define _OUT123_H_
|
||||
|
||||
#define OUT123_API_VERSION @OUTAPI_VERSION@
|
||||
|
||||
/** \defgroup out123_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.
|
||||
@{
|
||||
*/
|
||||
|
||||
/*
|
||||
This includes the mpg123 header, but does only use definitions from there,
|
||||
no symbols of the actual mpg123 library.
|
||||
Actually, I have to think about that. The string memory management could be
|
||||
useful for out123_drivers(), but somehow I feel we should get by without
|
||||
linking in an MPEG decoder library. Unless: Offer transparent decoding of
|
||||
MP3 directly to the output. Hm, then, WAV and friends should also be
|
||||
supported. This widens the scope of the library. Does it also actually
|
||||
increase the usefulness?
|
||||
|
||||
Oh, and another point is that the string stuff is optional in libmpg123
|
||||
anyway. So, this option of transparently decoding things in the background
|
||||
is a separate point from easing the creation of strings with lists of
|
||||
driver modules.
|
||||
|
||||
While it might be nice to have a very minimal program to play MPEG data,
|
||||
I think the purity of having the output library just deal with PCM data
|
||||
should not be given up lightly.
|
||||
|
||||
A more sensible feature should be a WAV header parser to enable a client
|
||||
program to auto-configure output format.
|
||||
*/
|
||||
#include "mpg123.h"
|
||||
#include "module.h"
|
||||
|
||||
struct audio_output_struct;
|
||||
typedef struct audio_output_struct audio_output_t;
|
||||
|
||||
/** 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 */
|
||||
/** 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.
|
||||
*/
|
||||
, OUT123_DEVICEBUFFER
|
||||
};
|
||||
|
||||
enum out123_flags
|
||||
{
|
||||
AUDIO_OUT_HEADPHONES = 0x01 /**< output to headphones (if supported) */
|
||||
, AUDIO_OUT_INTERNAL_SPEAKER = 0x02 /**< output to speaker (if supported) */
|
||||
, AUDIO_OUT_LINE_OUT = 0x04 /**< output to line out (if supported) */
|
||||
, OUT123_QUIET = 0x08 /**< no printouts to stdandard error */
|
||||
/** 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.
|
||||
*/
|
||||
, OUT123_KEEP_PLAYING = 0x10
|
||||
};
|
||||
|
||||
/** 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
|
||||
*/
|
||||
audio_output_t *out123_new(void);
|
||||
|
||||
/** Delete output handle.
|
||||
* This implies out123_close().
|
||||
*/
|
||||
void out123_del(audio_output_t *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 */
|
||||
/** Some (really unexpected) error in buffer infrastructure. */
|
||||
, OUT123_BUFFER_ERROR
|
||||
, OUT123_MODULE_ERROR /**< basic failure in module loading */
|
||||
, OUT123_ERRCOUNT /**< placeholder for shaping arrays */
|
||||
};
|
||||
|
||||
/** Get string representation of last encountered error in the
|
||||
* context of given handle.
|
||||
* \return error string
|
||||
*/
|
||||
const char* out123_strerror(audio_output_t *ao);
|
||||
|
||||
/** Get the plain errcode intead of a string.
|
||||
* \return error code recorded in handle or OUT123_BAD_HANDLE
|
||||
*/
|
||||
int out123_errcode(audio_output_t *ao);
|
||||
|
||||
/** Return the error string for a given error code.
|
||||
* \param errcode the integer error code
|
||||
*/
|
||||
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 of 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 buffer_size size (bytes) of a memory buffer for decoded audio,
|
||||
* a value of zero disables the buffer.
|
||||
* \return 0 on success, MPG123_ERR on error
|
||||
*/
|
||||
int out123_set_buffer(audio_output_t *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 code parameter code
|
||||
* \param value input value for integer parameters
|
||||
* \param fvalue input value for floating point parameters
|
||||
* \return 0 on success, MPG123_ERR on error.
|
||||
*/
|
||||
int out123_param( audio_output_t *ao, enum out123_parms code
|
||||
, long value, double fvalue );
|
||||
|
||||
/** Get a specific parameter, for a specific out123_handle, using a parameter
|
||||
* code chosen from the out123_parms enumeration, to the specified value.
|
||||
* \param code parameter code
|
||||
* \param value output address for integer parameters
|
||||
* \param fvalue output address for floating point parameters
|
||||
* \return 0 on success, MPG123_ERR on error (bad parameter name or bad handle).
|
||||
*/
|
||||
int out123_getparam( audio_output_t *ao, enum out123_parms code
|
||||
, long *ret_value, double *ret_fvalue );
|
||||
|
||||
/** Copy parameters from another out123_handle.
|
||||
* \param from_ao the handle to copy parameters from
|
||||
*/
|
||||
int out123_param_from(audio_output_t *ao, audio_output_t* 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 names address for storing list of names
|
||||
* \param descr address for storing list of descriptions
|
||||
* \return number of drivers found, -1 on error
|
||||
*/
|
||||
int out123_drivers(audio_output_t *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 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.
|
||||
*/
|
||||
int out123_open(audio_output_t *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 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)
|
||||
*/
|
||||
int out123_driver_info(audio_output_t *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.
|
||||
*/
|
||||
void out123_close(audio_output_t *ao);
|
||||
|
||||
/** Get supported audio encodings for given rate and channel count,
|
||||
* for the currently openend audio device.
|
||||
* 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 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
|
||||
*/
|
||||
int out123_encodings(audio_output_t *ao, int channels, long rate);
|
||||
|
||||
/** Get size of one PCM sample with given encoding.
|
||||
* This is a macro that might belong into a header file separate from
|
||||
* both mpg123.h and out123.h. No need to have those generic definitions
|
||||
* tied to library code.
|
||||
* Also: Thomas really likes the ?: operator.
|
||||
* \param encoding the encoding (mpg123_enc_enum value)
|
||||
* \return size of one sample in bytes
|
||||
*/
|
||||
#define out123_samplesize(enc) ( \
|
||||
(enc) & MPG123_ENC_8 \
|
||||
? 1 \
|
||||
: ( (enc) & MPG123_ENC_16 \
|
||||
? 2 \
|
||||
: ( (enc) & MPG123_ENC_24 \
|
||||
? 3 \
|
||||
: ( ((enc) & MPG123_ENC_32 || (enc) == MPG123_ENC_FLOAT_32) \
|
||||
? 4 \
|
||||
: ( (enc) == MPG123_ENC_FLOAT_64 \
|
||||
? 8 \
|
||||
: 0 \
|
||||
) ) ) ) )
|
||||
|
||||
/** 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.
|
||||
* \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)
|
||||
*/
|
||||
int out123_start( audio_output_t *ao
|
||||
, int encoding, int channels, long rate );
|
||||
|
||||
/** Pause playback
|
||||
* Interrupt playback without waiting for the data pushed to the optional
|
||||
* buffer being sent to the audio device.
|
||||
*
|
||||
* It keeps the audio device openend, able to resume. Occuring
|
||||
* underruns at the device layer should be silently dealt by the driver.
|
||||
* The nice way to stop feeding the device is to call out123_stop() and
|
||||
* then later out123_start() again.
|
||||
*
|
||||
* But feel free to try out123_pause() and out123_continue() instead.
|
||||
* At least announcing the pause is nicer than just stopping to feed audio
|
||||
* data for some time, although it might have the same effect, again,
|
||||
* deopending on the selected driver.
|
||||
*/
|
||||
void out123_pause(audio_output_t *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. 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(), too.
|
||||
*/
|
||||
void out123_continue(audio_output_t *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.
|
||||
*/
|
||||
void out123_stop(audio_output_t *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_PLAY_OVER_SIGNALS 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 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)
|
||||
*/
|
||||
size_t out123_play( audio_output_t *ao
|
||||
, void *buffer, size_t bytes );
|
||||
|
||||
/** Drop any buffered data, making next provided data play right away.
|
||||
* This is different from out123_pause() in that it doesn't imply
|
||||
* an actual pause in playback. You are expected to play something,
|
||||
* but feel free to call out123_stop() afterwards instead for a quicker
|
||||
* exit than the implied out123_drain().
|
||||
*/
|
||||
void out123_drop(audio_output_t *ao);
|
||||
|
||||
/** Drain the output, waiting until all data went to the hardware.
|
||||
* This does not imply out123_stop(). You might continue handing in
|
||||
* new data after that (after you enforced a buffer underrun ...).
|
||||
* This might involve only the optional buffer process, or the
|
||||
* buffers on the audio driver side, too.
|
||||
*/
|
||||
void out123_drain(audio_output_t *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.
|
||||
*
|
||||
* \param bytes limit of buffered bytes to drain
|
||||
* \return number of bytes drained from buffer
|
||||
*/
|
||||
void out123_ndrain(audio_output_t *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.
|
||||
* \return number of bytes in out123 library buffer
|
||||
*/
|
||||
size_t out123_buffered(audio_output_t *ao);
|
||||
|
||||
/** Extract currently used audio format from handle.
|
||||
* TODO: make the order of out123_start() match the arguments here,
|
||||
* matching mpg123_getformat().
|
||||
* Given return addresses may be NULL to indicate no interest.
|
||||
* \param rate address for sample rate
|
||||
* \param channels address for channel count
|
||||
* \param encoding address for encoding
|
||||
* \param size of a full PCM frame (for convenience)
|
||||
*/
|
||||
int out123_getformat( audio_output_t *ao
|
||||
, long *rate, int *channels, int *encoding, int *framesize );
|
||||
|
||||
#if 0 /* Old stuff following. */
|
||||
|
||||
|
||||
/* ------ Declarations from "audio.c" ------ */
|
||||
|
||||
audio_output_t* open_output_module( const char* name );
|
||||
void close_output_module( audio_output_t* ao );
|
||||
const char* audio_encoding_name(const int encoding, const int longer);
|
||||
|
||||
int init_output(audio_output_t **ao);
|
||||
void exit_output(audio_output_t *ao, int rude);
|
||||
int flush_output(audio_output_t *ao, unsigned char *bytes, size_t count);
|
||||
int open_output(audio_output_t *ao);
|
||||
void close_output(audio_output_t *ao );
|
||||
int reset_output(audio_output_t *ao);
|
||||
void output_pause(audio_output_t *ao); /* Prepare output for inactivity. */
|
||||
void output_unpause(audio_output_t *ao); /* Reactivate output (buffer process). */
|
||||
|
||||
int audio_enc_name2code(const char* name);
|
||||
void audio_enclist(char** list); /* Make a string of encoding names. */
|
||||
|
||||
int audio_format_support(audio_output_t *ao, long rate, int channels);
|
||||
void audio_prepare_format(audio_output_t *ao, int rate, int channels, int encoding);
|
||||
const char* audio_module_name(audio_output_t *ao);
|
||||
const char* audio_device_name(audio_output_t *ao);
|
||||
|
||||
int audio_reset(audio_output_t *ao, long rate, int channels, int format);
|
||||
void audio_drain(audio_output_t *ao);
|
||||
long audio_buffered_bytes(audio_output_t *ao);
|
||||
void audio_start(audio_output_t *ao);
|
||||
void audio_stop(audio_output_t *ao);
|
||||
void audio_drop(audio_output_t *ao);
|
||||
void audio_continue(audio_output_t *ao);
|
||||
void audio_fixme_wake_buffer(audio_output_t *ao);
|
||||
|
||||
#endif
|
||||
|
||||
/* @} */
|
||||
|
||||
#endif
|
||||
|
109
src/libout123/out123_int.h
Normal file
109
src/libout123/out123_int.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
out123_int: internal header for libout123
|
||||
|
||||
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 Michael Hipp (some traces left)
|
||||
*/
|
||||
|
||||
#ifndef _MPG123_OUT123_INT_H_
|
||||
#define _MPG123_OUT123_INT_H_
|
||||
|
||||
#include "config.h"
|
||||
#include "out123_intsym.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 */
|
||||
, play_paused /* paused, ready to continue, device still active */
|
||||
, play_live /* playing right now */
|
||||
};
|
||||
|
||||
struct audio_output_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)(struct audio_output_struct *);
|
||||
int (*get_formats)(struct audio_output_struct *);
|
||||
int (*write)(struct audio_output_struct *, unsigned char *,int);
|
||||
void (*flush)(struct audio_output_struct *); /* flush == drop != drain */
|
||||
void (*drain)(struct audio_output_struct *);
|
||||
int (*close)(struct audio_output_struct *);
|
||||
int (*deinit)(struct audio_output_struct *);
|
||||
|
||||
/* the loaded that has set the above */
|
||||
mpg123_module_t *module;
|
||||
|
||||
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). */
|
||||
double preload; /* buffer fraction to preload before play */
|
||||
int verbose; /* verbosity to stderr */
|
||||
double device_buffer; /* device buffer in seconds */
|
||||
/* 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(audio_output_t *ao, int fd);
|
||||
int read_parameters(audio_output_t *ao
|
||||
, int fd, byte *prebuf, int *preoff, int presize);
|
||||
|
||||
#endif
|
||||
|
64
src/libout123/out123_intsym.h
Normal file
64
src/libout123/out123_intsym.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef OUT123_INTSYM_H
|
||||
#define OUT123_INTSYM_H
|
||||
/* Mapping of internal mpg123 symbols to something that is less likely to conflict in case of static linking. */
|
||||
#define catchsignal IOT123_catchsignal
|
||||
#define safe_realloc IOT123_safe_realloc
|
||||
#define compat_open IOT123_compat_open
|
||||
#define compat_fopen IOT123_compat_fopen
|
||||
#define compat_close IOT123_compat_close
|
||||
#define compat_fclose IOT123_compat_fclose
|
||||
#define win32_wide_utf8 IOT123_win32_wide_utf8
|
||||
#define win32_utf8_wide IOT123_win32_utf8_wide
|
||||
#define unintr_write IOT123_unintr_write
|
||||
#define unintr_read IOT123_unintr_read
|
||||
#define open_module IOT123_open_module
|
||||
#define close_module IOT123_close_module
|
||||
#define list_modules IOT123_list_modules
|
||||
#define buffer_init IOT123_buffer_init
|
||||
#define buffer_exit IOT123_buffer_exit
|
||||
#define buffer_sync_param IOT123_buffer_sync_param
|
||||
#define buffer_open IOT123_buffer_open
|
||||
#define buffer_encodings IOT123_buffer_encodings
|
||||
#define buffer_start IOT123_buffer_start
|
||||
#define buffer_stop IOT123_buffer_stop
|
||||
#define buffer_close IOT123_buffer_close
|
||||
#define buffer_continue IOT123_buffer_continue
|
||||
#define buffer_ignore_lowmem IOT123_buffer_ignore_lowmem
|
||||
#define buffer_drain IOT123_buffer_drain
|
||||
#define buffer_end IOT123_buffer_end
|
||||
#define buffer_pause IOT123_buffer_pause
|
||||
#define buffer_drop IOT123_buffer_drop
|
||||
#define buffer_write IOT123_buffer_write
|
||||
#define buffer_fill IOT123_buffer_fill
|
||||
#define xfermem_init IOT123_xfermem_init
|
||||
#define xfermem_init_writer IOT123_xfermem_init_writer
|
||||
#define xfermem_init_reader IOT123_xfermem_init_reader
|
||||
#define xfermem_get_freespace IOT123_xfermem_get_freespace
|
||||
#define xfermem_get_usedspace IOT123_xfermem_get_usedspace
|
||||
#define xfermem_getcmd IOT123_xfermem_getcmd
|
||||
#define xfermem_putcmd IOT123_xfermem_putcmd
|
||||
#define xfermem_writer_block IOT123_xfermem_writer_block
|
||||
#define xfermem_write IOT123_xfermem_write
|
||||
#define xfermem_done IOT123_xfermem_done
|
||||
#define au_open IOT123_au_open
|
||||
#define cdr_open IOT123_cdr_open
|
||||
#define raw_open IOT123_raw_open
|
||||
#define wav_open IOT123_wav_open
|
||||
#define wav_write IOT123_wav_write
|
||||
#define wav_close IOT123_wav_close
|
||||
#define au_close IOT123_au_close
|
||||
#define raw_close IOT123_raw_close
|
||||
#define cdr_formats IOT123_cdr_formats
|
||||
#define au_formats IOT123_au_formats
|
||||
#define raw_formats IOT123_raw_formats
|
||||
#define wav_formats IOT123_wav_formats
|
||||
#define wav_drain IOT123_wav_drain
|
||||
#define write_parameters IOT123_write_parameters
|
||||
#define read_parameters IOT123_read_parameters
|
||||
#ifndef HAVE_STRERROR
|
||||
#define strerror IOT123_strerror
|
||||
#endif
|
||||
#ifndef HAVE_STRDUP
|
||||
#define strdup IOT123_strdup
|
||||
#endif
|
||||
#endif
|
741
src/libout123/wav.c
Normal file
741
src/libout123/wav.c
Normal file
@@ -0,0 +1,741 @@
|
||||
/*
|
||||
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) {
|
||||
error3("Strange endianness?? %08lx %08lx %08lx\n",a,b,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)", 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(audio_output_t *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(audio_output_t *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(audio_output_t *ao)
|
||||
{
|
||||
struct wavdata *wdat = NULL;
|
||||
struct auhead *auhead = NULL;
|
||||
|
||||
if(ao->format < 0)
|
||||
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;
|
||||
|
||||
if(ao->rate < 0)
|
||||
ao->rate = 44100;
|
||||
if(ao->channels < 0)
|
||||
ao->channels = 2;
|
||||
|
||||
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:
|
||||
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(audio_output_t *ao)
|
||||
{
|
||||
struct wavdata *wdat = NULL;
|
||||
|
||||
if(ao->format < 0)
|
||||
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(audio_output_t *ao)
|
||||
{
|
||||
struct wavdata *wdat;
|
||||
|
||||
if(ao->format < 0)
|
||||
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(audio_output_t *ao)
|
||||
{
|
||||
int bps;
|
||||
struct wavdata *wdat = NULL;
|
||||
struct riff *inthead = NULL;
|
||||
struct riff_float *floathead = NULL;
|
||||
|
||||
if(ao->format < 0)
|
||||
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(audio_output_t *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(audio_output_t *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(audio_output_t *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(audio_output_t *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(audio_output_t *ao)
|
||||
{
|
||||
if(ao->rate == 44100 && ao->channels == 2)
|
||||
return MPG123_ENC_SIGNED_16;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int au_formats(audio_output_t *ao)
|
||||
{
|
||||
return MPG123_ENC_SIGNED_16|MPG123_ENC_UNSIGNED_8|MPG123_ENC_ULAW_8;
|
||||
}
|
||||
|
||||
int raw_formats(audio_output_t *ao)
|
||||
{
|
||||
return MPG123_ENC_ANY;
|
||||
}
|
||||
|
||||
int wav_formats(audio_output_t *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(audio_output_t *ao)
|
||||
{
|
||||
struct wavdata *wdat = ao->userptr;
|
||||
|
||||
if(!wdat)
|
||||
return;
|
||||
|
||||
if(fflush(wdat->wavfp) && !AOQUIET)
|
||||
error1("flushing failed: %s\n", strerror(errno));
|
||||
}
|
33
src/libout123/wav.h
Normal file
33
src/libout123/wav.h
Normal 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(audio_output_t *);
|
||||
int cdr_open(audio_output_t *);
|
||||
int raw_open(audio_output_t *);
|
||||
int wav_open(audio_output_t *);
|
||||
int wav_write(audio_output_t *, unsigned char *buf, int len);
|
||||
int wav_close(audio_output_t *);
|
||||
int au_close(audio_output_t *);
|
||||
int raw_close(audio_output_t *);
|
||||
int cdr_formats(audio_output_t *);
|
||||
int au_formats(audio_output_t *);
|
||||
int raw_formats(audio_output_t *);
|
||||
int wav_formats(audio_output_t *);
|
||||
void wav_drain(audio_output_t *);
|
||||
|
||||
#endif
|
||||
|
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
wavhead.h: wav file header, to be included twice for integer and float wavs
|
||||
|
||||
copyright ?-2008 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
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
|
||||
struct RIFF_STRUCT_NAME
|
||||
{
|
||||
byte riffheader[4];
|
||||
byte WAVElen[4]; /* should this include riffheader or not? */
|
||||
@@ -42,7 +42,7 @@ struct
|
||||
/* from here you insert your PCM data */
|
||||
} data;
|
||||
} WAVE;
|
||||
} RIFF_NAME =
|
||||
} const RIFF_NAME =
|
||||
{
|
||||
{ 'R','I','F','F' } ,
|
||||
{ sizeof(RIFF_NAME.WAVE),0,0,0 } ,
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
xfermem: unidirectional fast pipe
|
||||
|
||||
copyright ?-2008 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
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
|
||||
@@ -9,10 +9,10 @@
|
||||
See xfermem.h for documentation/description.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "config.h"
|
||||
#include "out123_intsym.h"
|
||||
#include "compat.h"
|
||||
#include "xfermem.h"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/uio.h>
|
||||
@@ -79,12 +79,10 @@ void xfermem_init (txfermem **xf, size_t bufsize, size_t msize, size_t skipbuf)
|
||||
exit (1);
|
||||
}
|
||||
(*xf)->freeindex = (*xf)->readindex = 0;
|
||||
(*xf)->wakeme[0] = (*xf)->wakeme[1] = FALSE;
|
||||
(*xf)->data = ((byte *) *xf) + sizeof(txfermem) + msize;
|
||||
(*xf)->metadata = ((byte *) *xf) + sizeof(txfermem);
|
||||
(*xf)->data = ((char *) *xf) + sizeof(txfermem) + msize;
|
||||
(*xf)->metadata = ((char *) *xf) + sizeof(txfermem);
|
||||
(*xf)->size = bufsize;
|
||||
(*xf)->metasize = msize + skipbuf;
|
||||
(*xf)->justwait = 0;
|
||||
}
|
||||
|
||||
void xfermem_done (txfermem *xf)
|
||||
@@ -92,7 +90,10 @@ void xfermem_done (txfermem *xf)
|
||||
if(!xf)
|
||||
return;
|
||||
#ifdef HAVE_MMAP
|
||||
munmap ((caddr_t) xf, xf->size + xf->metasize + sizeof(txfermem));
|
||||
/* 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()");
|
||||
@@ -105,12 +106,14 @@ 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)
|
||||
@@ -145,10 +148,10 @@ size_t xfermem_get_usedspace (txfermem *xf)
|
||||
return (xf->size - (readindex - freeindex));
|
||||
}
|
||||
|
||||
int xfermem_getcmd (int fd, int block)
|
||||
static int xfermem_getcmd_raw (int fd, int block, byte *cmds, int count)
|
||||
{
|
||||
fd_set selfds;
|
||||
byte cmd;
|
||||
int ret;
|
||||
|
||||
for (;;) {
|
||||
struct timeval selto = {0, 0};
|
||||
@@ -156,10 +159,11 @@ int xfermem_getcmd (int fd, int block)
|
||||
FD_ZERO (&selfds);
|
||||
FD_SET (fd, &selfds);
|
||||
#ifdef HPUX
|
||||
switch (select(FD_SETSIZE, (int *) &selfds, NULL, NULL, block ? NULL : &selto)) {
|
||||
switch (select(FD_SETSIZE, (int *) &selfds, NULL, NULL, block ? NULL : &selto))
|
||||
#else
|
||||
switch (select(FD_SETSIZE, &selfds, NULL, NULL, block ? NULL : &selto)) {
|
||||
switch (select(FD_SETSIZE, &selfds, NULL, NULL, block ? NULL : &selto))
|
||||
#endif
|
||||
{
|
||||
case 0:
|
||||
if (!block)
|
||||
return (0);
|
||||
@@ -170,17 +174,16 @@ int xfermem_getcmd (int fd, int block)
|
||||
return (-2);
|
||||
case 1:
|
||||
if (FD_ISSET(fd, &selfds))
|
||||
switch (read(fd, &cmd, 1)) {
|
||||
switch((ret=read(fd, cmds, count)))
|
||||
{
|
||||
case 0: /* EOF */
|
||||
return (-1);
|
||||
case -1:
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return (-3);
|
||||
case 1:
|
||||
return (cmd);
|
||||
default: /* ?!? */
|
||||
return (-4);
|
||||
default:
|
||||
return ret;
|
||||
}
|
||||
else /* ?!? */
|
||||
return (-5);
|
||||
@@ -190,64 +193,96 @@ int xfermem_getcmd (int fd, int block)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int xfermem_block (int readwrite, txfermem *xf)
|
||||
/*
|
||||
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[readwrite];
|
||||
int myfd = xf->fd[XF_WRITER];
|
||||
int result;
|
||||
|
||||
xf->wakeme[readwrite] = TRUE;
|
||||
if (xf->wakeme[1 - readwrite])
|
||||
xfermem_putcmd (myfd, XF_CMD_WAKEUP);
|
||||
xfermem_putcmd(myfd, XF_CMD_PING);
|
||||
result = xfermem_getcmd(myfd, TRUE);
|
||||
xf->wakeme[readwrite] = FALSE;
|
||||
return ((result <= 0) ? -1 : result);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Parallel-safe code to signal a process and wait for it to respond. */
|
||||
int xfermem_sigblock(int readwrite, txfermem *xf, int pid, int signal)
|
||||
/* 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)
|
||||
{
|
||||
int myfd = xf->fd[readwrite];
|
||||
int result;
|
||||
if(buffer == NULL || bytes < 1) return 0;
|
||||
|
||||
xf->wakeme[readwrite] = TRUE;
|
||||
kill(pid, signal);
|
||||
|
||||
/* not sure about that block... here */
|
||||
if (xf->wakeme[1 - readwrite])
|
||||
xfermem_putcmd (myfd, XF_CMD_WAKEUP);
|
||||
|
||||
result = xfermem_getcmd(myfd, TRUE);
|
||||
xf->wakeme[readwrite] = FALSE;
|
||||
|
||||
return ((result <= 0) ? -1 : result);
|
||||
}
|
||||
|
||||
int xfermem_write(txfermem *xf, byte *buffer, size_t bytes)
|
||||
{
|
||||
if(buffer == NULL || bytes < 1) return FALSE;
|
||||
|
||||
/* You weren't so braindead to have not allocated enough space at all, right? */
|
||||
/* You weren't so braindead not allocating enough space at all, right? */
|
||||
while (xfermem_get_freespace(xf) < bytes)
|
||||
{
|
||||
int cmd = xfermem_block(XF_WRITER, xf);
|
||||
if (cmd == XF_CMD_TERMINATE || cmd < 0)
|
||||
{
|
||||
error("failed to wait for free space");
|
||||
return TRUE; /* Failure. */
|
||||
}
|
||||
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)
|
||||
@@ -258,64 +293,13 @@ int xfermem_write(txfermem *xf, byte *buffer, size_t bytes)
|
||||
{ /* two blocks */
|
||||
size_t endblock = xf->size - xf->freeindex;
|
||||
memcpy(xf->data+xf->freeindex, buffer, endblock);
|
||||
memcpy(xf->data, buffer + endblock, bytes-endblock);
|
||||
memcpy(xf->data, (char*)buffer + endblock, bytes-endblock);
|
||||
}
|
||||
/* Advance the free space pointer, including the wrap. */
|
||||
xf->freeindex = (xf->freeindex + bytes) % xf->size;
|
||||
/* Wake up the buffer process if necessary. */
|
||||
/* Always notify the buffer process. */
|
||||
debug("write waking");
|
||||
if(xf->wakeme[XF_READER])
|
||||
return xfermem_putcmd(xf->fd[XF_WRITER], XF_CMD_WAKEUP_INFO) < 0 ? TRUE : FALSE;
|
||||
|
||||
return FALSE;
|
||||
return xfermem_putcmd(xf->fd[XF_WRITER], XF_CMD_DATA) < 0
|
||||
? -1
|
||||
: 0;
|
||||
}
|
||||
|
||||
#else /* stubs for generic / win32 */
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include "xfermem.h"
|
||||
|
||||
void xfermem_init (txfermem **xf, size_t bufsize, size_t msize, size_t skipbuf)
|
||||
{
|
||||
}
|
||||
void xfermem_done (txfermem *xf)
|
||||
{
|
||||
}
|
||||
void xfermem_init_writer (txfermem *xf)
|
||||
{
|
||||
}
|
||||
void xfermem_init_reader (txfermem *xf)
|
||||
{
|
||||
}
|
||||
size_t xfermem_get_freespace (txfermem *xf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
size_t xfermem_get_usedspace (txfermem *xf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int xfermem_write(txfermem *xf, byte *buffer, size_t bytes)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
int xfermem_getcmd (int fd, int block)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int xfermem_putcmd (int fd, byte cmd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int xfermem_block (int readwrite, txfermem *xf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int xfermem_sigblock (int readwrite, txfermem *xf, int pid, int signal)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* eof */
|
||||
|
85
src/libout123/xfermem.h
Normal file
85
src/libout123/xfermem.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
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. */
|
||||
};
|
||||
|
||||
#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
|
@@ -2,4 +2,4 @@
|
||||
# wrapper script to set MPG123_MODDIR automatically so that one can run mpg123
|
||||
# from the source directory, even if modules are enabled
|
||||
src=$(dirname $0)
|
||||
MPG123_MODDIR="$src/output/.libs" exec "$src/mpg123" "$@"
|
||||
MPG123_MODDIR="$src/libout123/modules/.libs" exec "$src/mpg123" "$@"
|
||||
|
304
src/mpg123.c
304
src/mpg123.c
@@ -9,6 +9,7 @@
|
||||
#define ME "main"
|
||||
#include "mpg123app.h"
|
||||
#include "mpg123.h"
|
||||
#include "out123.h"
|
||||
#include "local.h"
|
||||
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
@@ -34,7 +35,6 @@
|
||||
#include "common.h"
|
||||
#include "sysutil.h"
|
||||
#include "getlopt.h"
|
||||
#include "buffer.h"
|
||||
#include "term.h"
|
||||
#include "playlist.h"
|
||||
#include "httpget.h"
|
||||
@@ -56,7 +56,6 @@ struct parameter param = {
|
||||
FALSE , /* shuffle */
|
||||
FALSE , /* remote */
|
||||
FALSE , /* remote to stderr */
|
||||
DECODE_AUDIO , /* write samples to audio device */
|
||||
FALSE , /* silent operation */
|
||||
FALSE , /* xterm title on/off */
|
||||
0 , /* second level buffer size */
|
||||
@@ -77,7 +76,6 @@ struct parameter param = {
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
0, /* win32 process priority */
|
||||
#endif
|
||||
NULL, /* wav,cdr,au Filename */
|
||||
0, /* default is to play all titles in playlist */
|
||||
NULL, /* no playlist per default */
|
||||
0 /* condensed id3 per default */
|
||||
@@ -110,7 +108,7 @@ struct parameter param = {
|
||||
,0 /* force_utf8 */
|
||||
,INDEX_SIZE
|
||||
,NULL /* force_encoding */
|
||||
,1. /* preload */
|
||||
,0.2 /* preload */
|
||||
,-1 /* preframes */
|
||||
,-1 /* gain */
|
||||
,NULL /* stream dump file */
|
||||
@@ -121,20 +119,16 @@ mpg123_handle *mh = NULL;
|
||||
off_t framenum;
|
||||
off_t frames_left;
|
||||
audio_output_t *ao = NULL;
|
||||
txfermem *buffermem = NULL;
|
||||
char *prgName = NULL;
|
||||
/* ThOr: pointers are not TRUE or FALSE */
|
||||
char *equalfile = NULL;
|
||||
struct httpdata htd;
|
||||
int fresh = TRUE;
|
||||
int have_output = FALSE; /* If we are past the output init step. */
|
||||
FILE* aux_out = NULL; /* Output for interesting information, normally on stdout to be parseable. */
|
||||
|
||||
int buffer_fd[2];
|
||||
int buffer_pid;
|
||||
size_t bufferblock = 0;
|
||||
|
||||
static int intflag = FALSE;
|
||||
int intflag = FALSE;
|
||||
static int skip_tracks = 0;
|
||||
int OutputDescriptor;
|
||||
|
||||
@@ -182,6 +176,34 @@ void prev_track(void)
|
||||
next_track();
|
||||
}
|
||||
|
||||
/* Drain output device/buffer, but still give the option to interrupt things. */
|
||||
static void controlled_drain(void)
|
||||
{
|
||||
int framesize;
|
||||
size_t drain_block;
|
||||
|
||||
if(intflag || !out123_buffered(ao))
|
||||
return;
|
||||
if(out123_getformat(ao, NULL, NULL, NULL, &framesize))
|
||||
return;
|
||||
drain_block = 1152*framesize;
|
||||
if(param.verbose)
|
||||
fprintf(stderr, "\n");
|
||||
do
|
||||
{
|
||||
out123_ndrain(ao, drain_block);
|
||||
if(param.verbose)
|
||||
print_buf("Draining buffer: ", ao);
|
||||
#ifdef HAVE_TERMIOS
|
||||
if(param.term_ctrl)
|
||||
term_control(mh, ao);
|
||||
#endif
|
||||
}
|
||||
while(!intflag && out123_buffered(ao));
|
||||
if(param.verbose)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
/* Directory jumping based on comparing the directory part of the playlist
|
||||
URLs. */
|
||||
int cmp_dir(const char* patha, const char* pathb)
|
||||
@@ -227,11 +249,14 @@ void safe_exit(int code)
|
||||
char *dummy, *dammy;
|
||||
|
||||
dump_close();
|
||||
controlled_drain();
|
||||
#ifdef HAVE_TERMIOS
|
||||
if(param.term_ctrl)
|
||||
term_restore();
|
||||
#endif
|
||||
if(have_output) exit_output(ao, intflag);
|
||||
if(intflag)
|
||||
out123_drop(ao);
|
||||
out123_del(ao);
|
||||
|
||||
if(mh != NULL) mpg123_delete(mh);
|
||||
|
||||
@@ -251,7 +276,22 @@ void safe_exit(int code)
|
||||
exit(code);
|
||||
}
|
||||
|
||||
/* returns 1 if reset_audio needed instead */
|
||||
static void check_fatal(int code)
|
||||
{
|
||||
if(code) safe_exit(code);
|
||||
}
|
||||
|
||||
static void check_fatal_output(int code)
|
||||
{
|
||||
if(code)
|
||||
{
|
||||
if(!param.quiet)
|
||||
error2( "out123 error %i: %s"
|
||||
, out123_errcode(ao), out123_strerror(ao) );
|
||||
safe_exit(code);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_output_module( char *arg )
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -320,67 +360,44 @@ static void set_quiet (char *arg)
|
||||
|
||||
static void set_out_wav(char *arg)
|
||||
{
|
||||
param.outmode = DECODE_WAV;
|
||||
param.filename = arg;
|
||||
param.output_module = "wav";
|
||||
param.output_device = arg;
|
||||
}
|
||||
|
||||
void set_out_cdr(char *arg)
|
||||
{
|
||||
param.outmode = DECODE_CDR;
|
||||
param.filename = arg;
|
||||
param.output_module = "cdr";
|
||||
param.output_device = arg;
|
||||
}
|
||||
|
||||
void set_out_au(char *arg)
|
||||
{
|
||||
param.outmode = DECODE_AU;
|
||||
param.filename = arg;
|
||||
param.output_module = "au";
|
||||
param.output_device = arg;
|
||||
}
|
||||
|
||||
void set_out_test(char *arg)
|
||||
{
|
||||
param.output_module = "test";
|
||||
param.output_device = NULL;
|
||||
}
|
||||
|
||||
static void set_out_file(char *arg)
|
||||
{
|
||||
param.outmode=DECODE_FILE;
|
||||
#ifdef WIN32
|
||||
#ifdef WANT_WIN32_UNICODE
|
||||
wchar_t *argw = NULL;
|
||||
OutputDescriptor = win32_utf8_wide(arg, &argw, NULL);
|
||||
if(argw != NULL)
|
||||
{
|
||||
OutputDescriptor=_wopen(argw,_O_CREAT|_O_WRONLY|_O_BINARY|_O_TRUNC,0666);
|
||||
free(argw);
|
||||
}
|
||||
#else
|
||||
OutputDescriptor=_open(arg,_O_CREAT|_O_WRONLY|_O_BINARY|_O_TRUNC,0666);
|
||||
#endif /*WANT_WIN32_UNICODE*/
|
||||
#else /*WIN32*/
|
||||
OutputDescriptor=open(arg,O_CREAT|O_WRONLY|O_TRUNC,0666);
|
||||
#endif /*WIN32*/
|
||||
if(OutputDescriptor==-1)
|
||||
{
|
||||
error2("Can't open %s for writing (%s).\n",arg,strerror(errno));
|
||||
safe_exit(1);
|
||||
}
|
||||
param.output_module = "raw";
|
||||
param.output_device = arg;
|
||||
}
|
||||
|
||||
static void set_out_stdout(char *arg)
|
||||
{
|
||||
param.outmode=DECODE_FILE;
|
||||
param.remote_err=TRUE;
|
||||
aux_out = stderr;
|
||||
OutputDescriptor=STDOUT_FILENO;
|
||||
#ifdef WIN32
|
||||
_setmode(STDOUT_FILENO, _O_BINARY);
|
||||
#endif
|
||||
param.output_module = "raw";
|
||||
param.output_device = NULL;
|
||||
}
|
||||
|
||||
static void set_out_stdout1(char *arg)
|
||||
{
|
||||
param.outmode=DECODE_AUDIOFILE;
|
||||
param.remote_err=TRUE;
|
||||
aux_out = stderr;
|
||||
OutputDescriptor=STDOUT_FILENO;
|
||||
#ifdef WIN32
|
||||
_setmode(STDOUT_FILENO, _O_BINARY);
|
||||
#endif
|
||||
param.output_module = "raw";
|
||||
param.output_device = NULL;
|
||||
}
|
||||
|
||||
#if !defined (HAVE_SCHED_SETSCHEDULER) && !defined (HAVE_WINDOWS_H)
|
||||
@@ -407,6 +424,38 @@ static void set_appflag(char *arg)
|
||||
{
|
||||
param.appflags |= appflag;
|
||||
}
|
||||
|
||||
static void list_output_modules(char *arg)
|
||||
{
|
||||
char **names = NULL;
|
||||
char **descr = NULL;
|
||||
int count = -1;
|
||||
audio_output_t *lao;
|
||||
|
||||
if((lao=out123_new()))
|
||||
{
|
||||
printf("\n");
|
||||
printf("Available modules\n");
|
||||
printf("-----------------\n");
|
||||
out123_param(lao, OUT123_VERBOSE, param.verbose, 0.);
|
||||
out123_param(lao, OUT123_FLAGS, OUT123_QUIET, 0.);
|
||||
if((count=out123_drivers(lao, &names, &descr)) >= 0)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<count; ++i)
|
||||
printf( "%-15s%s %s\n"
|
||||
, names[i], "output", descr[i] );
|
||||
free(names);
|
||||
free(descr);
|
||||
}
|
||||
out123_del(lao);
|
||||
}
|
||||
else if(!param.quiet)
|
||||
error("Failed to create an out123 handle.");
|
||||
exit(count >= 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
|
||||
/* static void unset_appflag(char *arg)
|
||||
{
|
||||
param.appflags &= ~appflag;
|
||||
@@ -427,9 +476,9 @@ topt opts[] = {
|
||||
{'k', "skip", GLO_ARG | GLO_LONG, 0, ¶m.start_frame, 0},
|
||||
{'2', "2to1", GLO_INT, 0, ¶m.down_sample, 1},
|
||||
{'4', "4to1", GLO_INT, 0, ¶m.down_sample, 2},
|
||||
{'t', "test", GLO_INT, 0, ¶m.outmode, DECODE_TEST},
|
||||
{'s', "stdout", GLO_INT, set_out_stdout, ¶m.outmode, DECODE_FILE},
|
||||
{'S', "STDOUT", GLO_INT, set_out_stdout1, ¶m.outmode,DECODE_AUDIOFILE},
|
||||
{'t', "test", GLO_INT, set_out_test, NULL, 0},
|
||||
{'s', "stdout", GLO_INT, set_out_stdout, NULL, 0},
|
||||
{'S', "STDOUT", GLO_INT, set_out_stdout1, NULL, 0},
|
||||
{'O', "outfile", GLO_ARG | GLO_CHAR, set_out_file, NULL, 0},
|
||||
{'c', "check", GLO_INT, 0, ¶m.checkrange, TRUE},
|
||||
{'v', "verbose", 0, set_verbose, 0, 0},
|
||||
@@ -454,7 +503,7 @@ topt opts[] = {
|
||||
{0, "speaker", 0, set_output_s, 0,0},
|
||||
{0, "lineout", 0, set_output_l, 0,0},
|
||||
{'o', "output", GLO_ARG | GLO_CHAR, set_output, 0, 0},
|
||||
{0, "list-modules",0, list_modules, NULL, 0},
|
||||
{0, "list-modules",0, list_output_modules, NULL, 0},
|
||||
{'a', "audiodevice", GLO_ARG | GLO_CHAR, 0, ¶m.output_device, 0},
|
||||
{'f', "scale", GLO_ARG | GLO_LONG, 0, ¶m.outscale, 0},
|
||||
{'n', "frames", GLO_ARG | GLO_LONG, 0, ¶m.frame_number, 0},
|
||||
@@ -546,54 +595,6 @@ topt opts[] = {
|
||||
{0, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
/*
|
||||
* Change the playback sample rate.
|
||||
* Consider that changing it after starting playback is not covered by gapless code!
|
||||
*/
|
||||
static void reset_audio(long rate, int channels, int format)
|
||||
{
|
||||
#ifndef NOXFERMEM
|
||||
if (param.usebuffer) {
|
||||
/* wait until the buffer is empty,
|
||||
* then tell the buffer process to
|
||||
* change the sample rate. [OF]
|
||||
*/
|
||||
while (xfermem_get_usedspace(buffermem) > 0)
|
||||
if (xfermem_block(XF_WRITER, buffermem) == XF_CMD_TERMINATE) {
|
||||
intflag = TRUE;
|
||||
break;
|
||||
}
|
||||
buffermem->freeindex = -1;
|
||||
buffermem->readindex = 0; /* I know what I'm doing! ;-) */
|
||||
buffermem->freeindex = 0;
|
||||
if (intflag)
|
||||
return;
|
||||
buffermem->rate = pitch_rate(rate);
|
||||
buffermem->channels = channels;
|
||||
buffermem->format = format;
|
||||
buffer_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
if(ao == NULL)
|
||||
{
|
||||
error("Audio handle should not be NULL here!");
|
||||
safe_exit(98);
|
||||
}
|
||||
ao->rate = pitch_rate(rate);
|
||||
ao->channels = channels;
|
||||
ao->format = format;
|
||||
if(reset_output(ao) < 0)
|
||||
{
|
||||
error1("failed to reset audio device: %s", strerror(errno));
|
||||
safe_exit(1);
|
||||
}
|
||||
#ifndef NOXFERMEM
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int open_track_fd (void)
|
||||
{
|
||||
/* Let reader handle invalid filept */
|
||||
@@ -717,8 +718,10 @@ int play_frame(void)
|
||||
{
|
||||
fresh = FALSE;
|
||||
}
|
||||
/* Normal flushing of data, includes buffer decoding. */
|
||||
if(flush_output(ao, audio, bytes) < (int)bytes && !intflag)
|
||||
/* Interrupt here doesn't necessarily interrupt out123_play().
|
||||
I wonder if that makes us miss errors. Actual issues should
|
||||
just be postponed. */
|
||||
if(out123_play(ao, audio, bytes) < bytes && !intflag)
|
||||
{
|
||||
error("Deep trouble! Cannot flush to my output anymore!");
|
||||
safe_exit(133);
|
||||
@@ -750,7 +753,7 @@ int play_frame(void)
|
||||
if(param.verbose > 2) fprintf(stderr, "\nNote: New output format %liHz %ich, format %i\n", rate, channels, format);
|
||||
|
||||
new_header = 1;
|
||||
reset_audio(rate, channels, format);
|
||||
check_fatal_output(out123_start(ao, format, channels, rate));
|
||||
}
|
||||
}
|
||||
if(new_header && !param.quiet)
|
||||
@@ -762,24 +765,6 @@ int play_frame(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void buffer_drain(void)
|
||||
{
|
||||
#ifndef NOXFERMEM
|
||||
int s;
|
||||
while ((s = xfermem_get_usedspace(buffermem)))
|
||||
{
|
||||
struct timeval wait170 = {0, 170000};
|
||||
if(intflag) break;
|
||||
buffer_ignore_lowmem();
|
||||
if(param.verbose) print_stat(mh,0,s);
|
||||
#ifdef HAVE_TERMIOS
|
||||
if(param.term_ctrl) term_control(mh, ao);
|
||||
#endif
|
||||
select(0, NULL, NULL, NULL, &wait170);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Return TRUE if we should continue (second interrupt happens quickly), skipping tracks, or FALSE if we should die. */
|
||||
#if !defined(WIN32) && !defined(GENERIC)
|
||||
int skip_or_die(struct timeval *start_time)
|
||||
@@ -950,13 +935,6 @@ int main(int sys_argc, char ** sys_argv)
|
||||
/* Init audio as early as possible.
|
||||
If there is the buffer process to be spawned, it shouldn't carry the mpg123_handle with it. */
|
||||
bufferblock = mpg123_safe_buffer(); /* Can call that before mpg123_init(), it's stateless. */
|
||||
if(init_output(&ao) < 0)
|
||||
{
|
||||
error("Failed to initialize output, goodbye.");
|
||||
mpg123_delete_pars(mp);
|
||||
return 99; /* It's safe here... nothing nasty happened yet. */
|
||||
}
|
||||
have_output = TRUE;
|
||||
|
||||
/* ========================================================================================================= */
|
||||
/* Enterning the leaking zone... we start messing with stuff here that should be taken care of when leaving. */
|
||||
@@ -1036,9 +1014,6 @@ int main(int sys_argc, char ** sys_argv)
|
||||
/* Prepare stream dumping, possibly replacing mpg123 reader. */
|
||||
if(dump_open(mh) != 0) safe_exit(78);
|
||||
|
||||
/* Now either check caps myself or query buffer for that. */
|
||||
audio_capabilities(ao, mh);
|
||||
|
||||
load_equalizer(mh);
|
||||
|
||||
#ifdef HAVE_SETPRIORITY
|
||||
@@ -1069,6 +1044,38 @@ int main(int sys_argc, char ** sys_argv)
|
||||
win32_set_priority( param.realtime ? 3 : param.w32_priority);
|
||||
#endif
|
||||
|
||||
/* TODO: There's some memory leaking on fatal safe_exits().
|
||||
This is a cosmetic issue, though. */
|
||||
|
||||
|
||||
/* Initializing output after the priority stuff, might influence
|
||||
buffer process. */
|
||||
ao = out123_new();
|
||||
if(!ao)
|
||||
{
|
||||
if(!param.quiet)
|
||||
error("Failed to allocate output.");
|
||||
safe_exit(97);
|
||||
}
|
||||
if
|
||||
( 0
|
||||
|| out123_param(ao, OUT123_FLAGS, param.output_flags, 0.)
|
||||
|| out123_param(ao, OUT123_PRELOAD, 0, param.preload)
|
||||
|| out123_param(ao, OUT123_GAIN, param.gain, 0.)
|
||||
|| out123_param(ao, OUT123_VERBOSE, param.verbose, 0.)
|
||||
)
|
||||
{
|
||||
if(!param.quiet)
|
||||
error("Error setting output parameters. Do you need a usage reminder?");
|
||||
safe_exit(98);
|
||||
}
|
||||
check_fatal_output(out123_set_buffer(ao, param.usebuffer*1024));
|
||||
check_fatal_output(out123_open( ao
|
||||
, param.output_module, param.output_device ));
|
||||
|
||||
/* Now either check caps myself or query buffer for that. */
|
||||
audio_capabilities(ao, mh);
|
||||
|
||||
if(!param.remote) prepare_playlist(argc, argv);
|
||||
|
||||
#if !defined(WIN32) && !defined(GENERIC)
|
||||
@@ -1103,15 +1110,14 @@ int main(int sys_argc, char ** sys_argv)
|
||||
}
|
||||
if(param.delay > 0)
|
||||
{
|
||||
controlled_drain();
|
||||
/* One should enable terminal control during that sleeping phase! */
|
||||
if(param.verbose > 2) fprintf(stderr, "Note: pausing %i seconds before next track.\n", param.delay);
|
||||
output_pause(ao);
|
||||
#ifdef WIN32
|
||||
Sleep(param.delay*1000);
|
||||
#else
|
||||
sleep(param.delay);
|
||||
#endif
|
||||
output_unpause(ao);
|
||||
}
|
||||
if(!APPFLAG(MPG123APP_CONTINUE)) frames_left = param.frame_number;
|
||||
|
||||
@@ -1232,12 +1238,7 @@ int main(int sys_argc, char ** sys_argv)
|
||||
}
|
||||
if(!fresh && param.verbose)
|
||||
{
|
||||
#ifndef NOXFERMEM
|
||||
if (param.verbose > 1 || !(framenum & 0x7))
|
||||
print_stat(mh,0,xfermem_get_usedspace(buffermem));
|
||||
#else
|
||||
if(param.verbose > 1 || !(framenum & 0x7)) print_stat(mh,0,0);
|
||||
#endif
|
||||
if(param.verbose > 1 || !(framenum & 0x7)) print_stat(mh,0,ao);
|
||||
}
|
||||
#ifdef HAVE_TERMIOS
|
||||
if(!param.term_ctrl) continue;
|
||||
@@ -1245,8 +1246,9 @@ int main(int sys_argc, char ** sys_argv)
|
||||
#endif
|
||||
}
|
||||
|
||||
if(!param.smooth && param.usebuffer) buffer_drain();
|
||||
if(param.verbose) print_stat(mh,0,xfermem_get_usedspace(buffermem));
|
||||
if(!param.smooth && !intflag)
|
||||
controlled_drain();
|
||||
if(param.verbose) print_stat(mh,0,ao);
|
||||
|
||||
if(!param.quiet)
|
||||
{
|
||||
@@ -1269,21 +1271,13 @@ int main(int sys_argc, char ** sys_argv)
|
||||
|
||||
intflag = FALSE;
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
if(!param.smooth && param.usebuffer) buffer_resync();
|
||||
#endif
|
||||
if(!param.smooth)
|
||||
out123_drop(ao);
|
||||
}
|
||||
|
||||
if(end_of_files) break;
|
||||
} /* end of loop over input files */
|
||||
|
||||
/* Ensure we played everything. */
|
||||
if(param.smooth && param.usebuffer)
|
||||
{
|
||||
buffer_drain();
|
||||
buffer_resync();
|
||||
}
|
||||
|
||||
if(APPFLAG(MPG123APP_CONTINUE))
|
||||
{
|
||||
continue_msg("CONTINUE");
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
mpg123: main code of the program (not of the decoder...)
|
||||
|
||||
copyright 1995-2009 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
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 Michael Hipp
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "compat.h"
|
||||
/* import DLL symbols on windows */
|
||||
|
||||
#include "xfermem.h"
|
||||
#include "httpget.h"
|
||||
#if WIN32
|
||||
#include "win32_support.h"
|
||||
@@ -52,13 +51,12 @@ struct parameter
|
||||
int shuffle; /* shuffle/random play */
|
||||
int remote; /* remote operation */
|
||||
int remote_err; /* remote operation to stderr */
|
||||
int outmode; /* where to out the decoded sampels */
|
||||
int quiet; /* shut up! */
|
||||
int xterm_title; /* Change xterm title to song names? */
|
||||
long usebuffer; /* second level buffer size */
|
||||
int verbose; /* verbose level */
|
||||
char* output_module; /* audio output module to use */
|
||||
char* output_device; /* audio output device to use */
|
||||
const char* output_module; /* audio output module to use */
|
||||
const char* output_device; /* audio output device to use */
|
||||
int output_flags; /* legacy output destination for AIX/HP/Sun */
|
||||
#ifdef HAVE_TERMIOS
|
||||
int term_ctrl;
|
||||
@@ -73,7 +71,6 @@ struct parameter
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
int w32_priority;
|
||||
#endif
|
||||
char *filename;
|
||||
long listentry; /* possibility to choose playback of one entry in playlist (0: off, > 0 : select, < 0; just show list*/
|
||||
char* listname; /* name of playlist */
|
||||
int long_id3;
|
||||
@@ -127,14 +124,6 @@ extern char *equalfile;
|
||||
extern off_t framenum;
|
||||
extern struct httpdata htd;
|
||||
|
||||
extern int buffer_fd[2];
|
||||
extern txfermem *buffermem;
|
||||
extern int buffer_pid;
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
extern void buffer_loop(audio_output_t *ao,sigset_t *oldsigset);
|
||||
#endif
|
||||
|
||||
extern int OutputDescriptor;
|
||||
|
||||
#ifdef VARMODESUPPORT
|
||||
@@ -148,15 +137,6 @@ extern int play_frame(void);
|
||||
|
||||
extern int control_generic(mpg123_handle *fr);
|
||||
|
||||
/* Eh... I see duplicated definitions. Clean up after merge! */
|
||||
extern int cdr_open(audio_output_t *);
|
||||
extern int au_open(audio_output_t *);
|
||||
extern int wav_open(audio_output_t *);
|
||||
extern int wav_write(unsigned char *buf,int len);
|
||||
extern int cdr_close(void);
|
||||
extern int au_close(void);
|
||||
extern int wav_close(void);
|
||||
|
||||
extern struct parameter param;
|
||||
|
||||
/* avoid the SIGINT in terminal control */
|
||||
|
@@ -2,4 +2,4 @@
|
||||
# wrapper script to set MPG123_MODDIR automatically so that one can run mpg123
|
||||
# from the source directory, even if modules are enabled
|
||||
src=$(dirname $0)
|
||||
MPG123_MODDIR="$src/output/.libs" exec "$src/out123" "$@"
|
||||
MPG123_MODDIR="$src/libout123/modules/.libs" exec "$src/out123" "$@"
|
||||
|
262
src/out123.c
262
src/out123.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
out123: simple program to stream data to an audio output device
|
||||
|
||||
copyright 1995-2014 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
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 Thomas Orgis (extracted from mpg123.c)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#define ME "main"
|
||||
#include "mpg123app.h"
|
||||
#include "out123.h"
|
||||
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
@@ -49,7 +50,6 @@
|
||||
|
||||
#include "sysutil.h"
|
||||
#include "getlopt.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
@@ -72,7 +72,6 @@ struct parameter param = {
|
||||
FALSE , /* shuffle */
|
||||
FALSE , /* remote */
|
||||
FALSE , /* remote to stderr */
|
||||
DECODE_AUDIO , /* write samples to audio device */
|
||||
FALSE , /* silent operation */
|
||||
FALSE , /* xterm title on/off */
|
||||
0 , /* second level buffer size */
|
||||
@@ -93,7 +92,6 @@ struct parameter param = {
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
0, /* win32 process priority */
|
||||
#endif
|
||||
NULL, /* wav,cdr,au Filename */
|
||||
0, /* default is to play all titles in playlist */
|
||||
NULL, /* no playlist per default */
|
||||
0 /* condensed id3 per default */
|
||||
@@ -126,7 +124,7 @@ struct parameter param = {
|
||||
,0 /* force_utf8 */
|
||||
,INDEX_SIZE
|
||||
,"s16" /* force_encoding */
|
||||
,1. /* preload */
|
||||
,0.2 /* preload */
|
||||
,-1 /* preframes */
|
||||
,-1 /* gain */
|
||||
,NULL /* stream dump file */
|
||||
@@ -134,18 +132,13 @@ struct parameter param = {
|
||||
};
|
||||
|
||||
audio_output_t *ao = NULL;
|
||||
txfermem *buffermem = NULL;
|
||||
char *prgName = NULL;
|
||||
/* ThOr: pointers are not TRUE or FALSE */
|
||||
char *equalfile = NULL;
|
||||
int fresh = TRUE;
|
||||
int have_output = FALSE; /* If we are past the output init step. */
|
||||
|
||||
int buffer_fd[2];
|
||||
int buffer_pid;
|
||||
size_t bufferblock = 4096;
|
||||
|
||||
static int intflag = FALSE;
|
||||
int OutputDescriptor;
|
||||
|
||||
char *fullprogname = NULL; /* Copy of argv[0]. */
|
||||
@@ -160,8 +153,7 @@ void safe_exit(int code)
|
||||
{
|
||||
char *dummy, *dammy;
|
||||
|
||||
if(have_output) exit_output(ao, intflag);
|
||||
|
||||
out123_del(ao);
|
||||
#ifdef WANT_WIN32_UNICODE
|
||||
win32_cmdline_free(argc, argv); /* This handles the premature argv == NULL, too. */
|
||||
#endif
|
||||
@@ -172,7 +164,22 @@ void safe_exit(int code)
|
||||
exit(code);
|
||||
}
|
||||
|
||||
/* returns 1 if reset_audio needed instead */
|
||||
static void check_fatal(int code)
|
||||
{
|
||||
if(code) safe_exit(code);
|
||||
}
|
||||
|
||||
static void check_fatal_output(int code)
|
||||
{
|
||||
if(code)
|
||||
{
|
||||
if(!param.quiet)
|
||||
error2( "out123 error %i: %s"
|
||||
, out123_errcode(ao), out123_strerror(ao) );
|
||||
safe_exit(code);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_output_module( char *arg )
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -241,65 +248,44 @@ static void set_quiet (char *arg)
|
||||
|
||||
static void set_out_wav(char *arg)
|
||||
{
|
||||
param.outmode = DECODE_WAV;
|
||||
param.filename = arg;
|
||||
param.output_module = "wav";
|
||||
param.output_device = arg;
|
||||
}
|
||||
|
||||
void set_out_cdr(char *arg)
|
||||
{
|
||||
param.outmode = DECODE_CDR;
|
||||
param.filename = arg;
|
||||
param.output_module = "cdr";
|
||||
param.output_device = arg;
|
||||
}
|
||||
|
||||
void set_out_au(char *arg)
|
||||
{
|
||||
param.outmode = DECODE_AU;
|
||||
param.filename = arg;
|
||||
param.output_module = "au";
|
||||
param.output_device = arg;
|
||||
}
|
||||
|
||||
void set_out_test(char *arg)
|
||||
{
|
||||
param.output_module = "test";
|
||||
param.output_device = NULL;
|
||||
}
|
||||
|
||||
static void set_out_file(char *arg)
|
||||
{
|
||||
param.outmode=DECODE_FILE;
|
||||
#ifdef WIN32
|
||||
#ifdef WANT_WIN32_UNICODE
|
||||
wchar_t *argw = NULL;
|
||||
OutputDescriptor = win32_utf8_wide(arg, &argw, NULL);
|
||||
if(argw != NULL)
|
||||
{
|
||||
OutputDescriptor=_wopen(argw,_O_CREAT|_O_WRONLY|_O_BINARY|_O_TRUNC,0666);
|
||||
free(argw);
|
||||
}
|
||||
#else
|
||||
OutputDescriptor=_open(arg,_O_CREAT|_O_WRONLY|_O_BINARY|_O_TRUNC,0666);
|
||||
#endif /*WANT_WIN32_UNICODE*/
|
||||
#else /*WIN32*/
|
||||
OutputDescriptor=open(arg,O_CREAT|O_WRONLY|O_TRUNC,0666);
|
||||
#endif /*WIN32*/
|
||||
if(OutputDescriptor==-1)
|
||||
{
|
||||
error2("Can't open %s for writing (%s).\n",arg,strerror(errno));
|
||||
safe_exit(1);
|
||||
}
|
||||
param.output_module = "raw";
|
||||
param.output_device = arg;
|
||||
}
|
||||
|
||||
static void set_out_stdout(char *arg)
|
||||
{
|
||||
param.outmode=DECODE_FILE;
|
||||
param.remote_err=TRUE;
|
||||
OutputDescriptor=STDOUT_FILENO;
|
||||
#ifdef WIN32
|
||||
_setmode(STDOUT_FILENO, _O_BINARY);
|
||||
#endif
|
||||
param.output_module = "raw";
|
||||
param.output_device = NULL;
|
||||
}
|
||||
|
||||
static void set_out_stdout1(char *arg)
|
||||
{
|
||||
param.outmode=DECODE_AUDIOFILE;
|
||||
param.remote_err=TRUE;
|
||||
OutputDescriptor=STDOUT_FILENO;
|
||||
#ifdef WIN32
|
||||
_setmode(STDOUT_FILENO, _O_BINARY);
|
||||
#endif
|
||||
param.output_module = "raw";
|
||||
param.output_device = NULL;
|
||||
}
|
||||
|
||||
#if !defined (HAVE_SCHED_SETSCHEDULER) && !defined (HAVE_WINDOWS_H)
|
||||
@@ -322,6 +308,32 @@ static void unset_frameflag(char *arg)
|
||||
param.flags &= ~frameflag;
|
||||
} */
|
||||
|
||||
static void list_output_modules(char *arg)
|
||||
{
|
||||
char **names = NULL;
|
||||
char **descr = NULL;
|
||||
int count = -1;
|
||||
audio_output_t *lao;
|
||||
|
||||
if((lao=out123_new()))
|
||||
{
|
||||
out123_param(lao, OUT123_VERBOSE, param.verbose, 0.);
|
||||
out123_param(lao, OUT123_FLAGS, OUT123_QUIET, 0.);
|
||||
if((count=out123_drivers(lao, &names, &descr)) >= 0)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<count; ++i)
|
||||
printf( "%-15s\t%s\n", names[i], descr[i] );
|
||||
free(names);
|
||||
free(descr);
|
||||
}
|
||||
out123_del(lao);
|
||||
}
|
||||
else if(!param.quiet)
|
||||
error("Failed to create an out123 handle.");
|
||||
exit(count >= 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
/*static int appflag;*/ /* still ugly, but works */
|
||||
/*static void set_appflag(char *arg)
|
||||
{
|
||||
@@ -341,9 +353,9 @@ passed.
|
||||
* GLO_NUM no longer exists.
|
||||
*/
|
||||
topt opts[] = {
|
||||
{'t', "test", GLO_INT, 0, ¶m.outmode, DECODE_TEST},
|
||||
{'s', "stdout", GLO_INT, set_out_stdout, ¶m.outmode, DECODE_FILE},
|
||||
{'S', "STDOUT", GLO_INT, set_out_stdout1, ¶m.outmode,DECODE_AUDIOFILE},
|
||||
{'t', "test", GLO_INT, set_out_test, NULL, 0},
|
||||
{'s', "stdout", GLO_INT, set_out_stdout, NULL, 0},
|
||||
{'S', "STDOUT", GLO_INT, set_out_stdout1, NULL, 0},
|
||||
{'O', "outfile", GLO_ARG | GLO_CHAR, set_out_file, NULL, 0},
|
||||
{'v', "verbose", 0, set_verbose, 0, 0},
|
||||
{'q', "quiet", 0, set_quiet, 0, 0},
|
||||
@@ -354,7 +366,7 @@ topt opts[] = {
|
||||
{0, "speaker", 0, set_output_s, 0,0},
|
||||
{0, "lineout", 0, set_output_l, 0,0},
|
||||
{'o', "output", GLO_ARG | GLO_CHAR, set_output, 0, 0},
|
||||
{0, "list-modules",0, list_modules, NULL, 0},
|
||||
{0, "list-modules",0, list_output_modules, NULL, 0},
|
||||
{'a', "audiodevice", GLO_ARG | GLO_CHAR, 0, ¶m.output_device, 0},
|
||||
#ifndef NOXFERMEM
|
||||
{'b', "buffer", GLO_ARG | GLO_LONG, 0, ¶m.usebuffer, 0},
|
||||
@@ -382,54 +394,6 @@ topt opts[] = {
|
||||
{0, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
/*
|
||||
* Change the playback sample rate.
|
||||
* Consider that changing it after starting playback is not covered by gapless code!
|
||||
*/
|
||||
static void reset_audio(long rate, int channels, int format)
|
||||
{
|
||||
#ifndef NOXFERMEM
|
||||
if (param.usebuffer) {
|
||||
/* wait until the buffer is empty,
|
||||
* then tell the buffer process to
|
||||
* change the sample rate. [OF]
|
||||
*/
|
||||
while (xfermem_get_usedspace(buffermem) > 0)
|
||||
if (xfermem_block(XF_WRITER, buffermem) == XF_CMD_TERMINATE) {
|
||||
intflag = TRUE;
|
||||
break;
|
||||
}
|
||||
buffermem->freeindex = -1;
|
||||
buffermem->readindex = 0; /* I know what I'm doing! ;-) */
|
||||
buffermem->freeindex = 0;
|
||||
if (intflag)
|
||||
return;
|
||||
buffermem->rate = pitch_rate(rate);
|
||||
buffermem->channels = channels;
|
||||
buffermem->format = format;
|
||||
buffer_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
if(ao == NULL)
|
||||
{
|
||||
error("Audio handle should not be NULL here!");
|
||||
safe_exit(98);
|
||||
}
|
||||
ao->rate = pitch_rate(rate);
|
||||
ao->channels = channels;
|
||||
ao->format = format;
|
||||
if(reset_output(ao) < 0)
|
||||
{
|
||||
error1("failed to reset audio device: %s", strerror(errno));
|
||||
safe_exit(1);
|
||||
}
|
||||
#ifndef NOXFERMEM
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* return 1 on success, 0 on failure */
|
||||
int play_frame(void)
|
||||
{
|
||||
@@ -439,9 +403,13 @@ int play_frame(void)
|
||||
if(got_samples)
|
||||
{
|
||||
size_t got_bytes = pcmframe * got_samples;
|
||||
if(flush_output(ao, audio, got_bytes) < (int)got_bytes)
|
||||
if(out123_play(ao, audio, got_bytes) < (int)got_bytes)
|
||||
{
|
||||
error("Deep trouble! Cannot flush to my output anymore!");
|
||||
if(!param.quiet)
|
||||
{
|
||||
error2( "out123 error %i: %s"
|
||||
, out123_errcode(ao), out123_strerror(ao) );
|
||||
}
|
||||
safe_exit(133);
|
||||
}
|
||||
return 1;
|
||||
@@ -449,24 +417,17 @@ int play_frame(void)
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void buffer_drain(void)
|
||||
{
|
||||
#ifndef NOXFERMEM
|
||||
int s;
|
||||
while ((s = xfermem_get_usedspace(buffermem)))
|
||||
{
|
||||
struct timeval wait170 = {0, 170000};
|
||||
if(intflag) break;
|
||||
buffer_ignore_lowmem();
|
||||
/* if(param.verbose) print_stat(mh,0,s); */
|
||||
select(0, NULL, NULL, NULL, &wait170);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Hack: A hidden function in audio.c just for me. */
|
||||
int audio_enc_name2code(const char* name);
|
||||
|
||||
static int intflag = FALSE;
|
||||
#if !defined(WIN32) && !defined(GENERIC)
|
||||
static void catch_interrupt(void)
|
||||
{
|
||||
intflag = TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int sys_argc, char ** sys_argv)
|
||||
{
|
||||
int result;
|
||||
@@ -523,6 +484,27 @@ int main(int sys_argc, char ** sys_argv)
|
||||
usage(1);
|
||||
}
|
||||
|
||||
/* Ensure cleanup before we cause too much mess. */
|
||||
#if !defined(WIN32) && !defined(GENERIC)
|
||||
catchsignal(SIGINT, catch_interrupt);
|
||||
catchsignal(SIGTERM, catch_interrupt);
|
||||
#endif
|
||||
ao = out123_new();
|
||||
if(!ao){ error("Failed to allocate output."); exit(1); }
|
||||
|
||||
if
|
||||
( 0
|
||||
|| out123_param(ao, OUT123_FLAGS, param.output_flags, 0.)
|
||||
|| out123_param(ao, OUT123_PRELOAD, 0, param.preload)
|
||||
|| out123_param(ao, OUT123_GAIN, param.gain, 0.)
|
||||
|| out123_param(ao, OUT123_VERBOSE, param.verbose, 0.)
|
||||
)
|
||||
{
|
||||
error("Error setting output parameters. Do you need a usage reminder?");
|
||||
usage(1);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_SETPRIORITY
|
||||
if(param.aggressive) { /* tst */
|
||||
int mypid = getpid();
|
||||
@@ -564,46 +546,26 @@ int main(int sys_argc, char ** sys_argv)
|
||||
bufferblock = pcmblock*pcmframe;
|
||||
audio = (unsigned char*) malloc(bufferblock);
|
||||
|
||||
check_fatal_output(out123_set_buffer(ao, param.usebuffer*1024));
|
||||
/* This needs bufferblock set! */
|
||||
if(init_output(&ao) < 0)
|
||||
{
|
||||
error("Failed to initialize output, goodbye.");
|
||||
return 99; /* It's safe here... nothing nasty happened yet. */
|
||||
}
|
||||
have_output = TRUE;
|
||||
check_fatal_output(out123_open( ao
|
||||
, param.output_module, param.output_device ));
|
||||
|
||||
fprintf(stderr, "TODO: Check audio caps, add option to display 'em.\n");
|
||||
/* audio_capabilities(ao, mh); */
|
||||
/*
|
||||
This is in audio_capabilities(), buffer must be told to leave config mode.
|
||||
We don't ask capabilities in normal playback operation, but there'll be
|
||||
an extra mode of operation to query if an audio device supports a certain
|
||||
format, or indeed the whole matrix. Actually, we usually got independent
|
||||
support for certain encodings and for certain rates and channels. Could
|
||||
remodel the interface for that, as we're not thinking about fixed MPEG
|
||||
rates.
|
||||
*/
|
||||
#ifndef NOXFERMEM
|
||||
/* Buffer loop shall start normal operation now. */
|
||||
if(param.usebuffer)
|
||||
{
|
||||
xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_WAKEUP);
|
||||
xfermem_getcmd(buffermem->fd[XF_WRITER], TRUE);
|
||||
}
|
||||
#endif
|
||||
|
||||
reset_audio(param.force_rate, channels, encoding);
|
||||
check_fatal_output(out123_start(ao, encoding, channels, param.force_rate));
|
||||
|
||||
input = stdin;
|
||||
while(play_frame())
|
||||
while(play_frame() && !intflag)
|
||||
{
|
||||
/* be happy */
|
||||
}
|
||||
/* Ensure we played everything. */
|
||||
if(param.usebuffer)
|
||||
if(intflag) /* Make it quick! */
|
||||
{
|
||||
buffer_drain();
|
||||
buffer_resync();
|
||||
if(!param.quiet)
|
||||
fprintf(stderr, "Interrupted. Dropping the ball.\n");
|
||||
out123_drop(ao);
|
||||
}
|
||||
|
||||
safe_exit(0); /* That closes output and restores terminal, too. */
|
||||
|
@@ -12,27 +12,6 @@
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#if (!defined(WIN32) || defined (__CYGWIN__)) && defined(HAVE_SIGNAL_H)
|
||||
void (*catchsignal(int signum, void(*handler)()))()
|
||||
{
|
||||
struct sigaction new_sa;
|
||||
struct sigaction old_sa;
|
||||
|
||||
#ifdef DONT_CATCH_SIGNALS
|
||||
fprintf (stderr, "Not catching any signals.\n");
|
||||
return ((void (*)()) -1);
|
||||
#endif
|
||||
|
||||
new_sa.sa_handler = handler;
|
||||
sigemptyset(&new_sa.sa_mask);
|
||||
new_sa.sa_flags = 0;
|
||||
if (sigaction(signum, &new_sa, &old_sa) == -1)
|
||||
return ((void (*)()) -1);
|
||||
return (old_sa.sa_handler);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if 0
|
||||
/* removed the strndup for better portability */
|
||||
/*
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
sysutil: generic utilities to interact with the OS (signals, paths)
|
||||
|
||||
copyright ?-2014 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
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 Michael Hipp (dissected/renamed by Thomas Orgis)
|
||||
*/
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "mpg123app.h"
|
||||
|
||||
void (*catchsignal(int signum, void(*handler)()))();
|
||||
int split_dir_file(const char *path, char **dname, char **fname);
|
||||
/* Length of directory part in given path. */
|
||||
size_t dir_length(const char *path);
|
||||
|
115
src/term.c
115
src/term.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
term: terminal control
|
||||
|
||||
copyright ?-2008 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
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 Michael Hipp
|
||||
*/
|
||||
@@ -13,14 +13,12 @@
|
||||
#include <termios.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "buffer.h"
|
||||
#include "term.h"
|
||||
#include "common.h"
|
||||
#include "playlist.h"
|
||||
#include "metaprint.h"
|
||||
#include "debug.h"
|
||||
|
||||
extern int buffer_pid;
|
||||
extern audio_output_t *ao;
|
||||
|
||||
static int term_enable = 0;
|
||||
@@ -178,34 +176,23 @@ void pause_uncycle(void)
|
||||
off_t term_control(mpg123_handle *fr, audio_output_t *ao)
|
||||
{
|
||||
offset = 0;
|
||||
debug1("control for frame: %li", (long)mpg123_tellframe(fr));
|
||||
debug2("control for frame: %li, enable: %i", (long)mpg123_tellframe(fr), term_enable);
|
||||
if(!term_enable) return 0;
|
||||
|
||||
if(paused)
|
||||
{
|
||||
/* pause_cycle counts the remaining frames _after_ this one, thus <0, not ==0 . */
|
||||
if(--pause_cycle < 0)
|
||||
{
|
||||
pause_recycle(fr);
|
||||
if(param.usebuffer)
|
||||
{
|
||||
while(paused && xfermem_get_usedspace(buffermem))
|
||||
{
|
||||
buffer_ignore_lowmem();
|
||||
term_handle_input(fr, ao, TRUE);
|
||||
}
|
||||
/* Undo the cycling offset if we are done with cycling. */
|
||||
if(!paused) pause_uncycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
off_t old_offset = offset;
|
||||
term_handle_input(fr, ao, stopped|seeking);
|
||||
if((offset < 0) && (-offset > framenum)) offset = - framenum;
|
||||
if(param.verbose && offset != 0)
|
||||
print_stat(fr,offset,0);
|
||||
if(param.verbose && offset != old_offset)
|
||||
print_stat(fr,offset,ao);
|
||||
} while (stopped);
|
||||
|
||||
/* Make the seeking experience with buffer less annoying.
|
||||
@@ -216,18 +203,30 @@ debug1("control for frame: %li", (long)mpg123_tellframe(fr));
|
||||
debug1("seeked to %li", (long)offset);
|
||||
else error1("seek failed: %s!", mpg123_strerror(fr));
|
||||
/* Buffer resync already happened on un-stop? */
|
||||
/* if(param.usebuffer) buffer_resync();*/
|
||||
/* if(param.usebuffer) audio_drop(ao);*/
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Stop playback while seeking if buffer is involved. */
|
||||
static void seekmode(void)
|
||||
static void seekmode(mpg123_handle *mh, audio_output_t *ao)
|
||||
{
|
||||
if(param.usebuffer && !stopped)
|
||||
{
|
||||
stopped = TRUE;
|
||||
buffer_stop();
|
||||
int channels;
|
||||
int encoding;
|
||||
off_t back_samples;
|
||||
|
||||
out123_pause(ao);
|
||||
mpg123_getformat(mh, NULL, &channels, &encoding);
|
||||
back_samples = out123_buffered(ao)/(out123_samplesize(encoding)*channels);
|
||||
fprintf(stderr, "\nseeking back %"OFF_P" samples from %"OFF_P"\n"
|
||||
, (off_p)back_samples, (off_p)mpg123_tell(mh));
|
||||
mpg123_seek(mh, -back_samples, SEEK_CUR);
|
||||
out123_drop(ao);
|
||||
fprintf(stderr, "\ndropped, now at %"OFF_P"\n"
|
||||
, (off_p)mpg123_tell(mh));
|
||||
fprintf(stderr, "%s", MPG123_STOPPED_STRING);
|
||||
}
|
||||
}
|
||||
@@ -265,11 +264,11 @@ static int get_key(int do_delay, char *val)
|
||||
|
||||
static void term_handle_key(mpg123_handle *fr, audio_output_t *ao, char val)
|
||||
{
|
||||
debug1("term_handle_key: %c", val);
|
||||
switch(tolower(val))
|
||||
{
|
||||
case MPG123_BACK_KEY:
|
||||
if(!param.usebuffer) ao->flush(ao);
|
||||
else buffer_resync();
|
||||
out123_drop(ao);
|
||||
if(paused) pause_cycle=(int)(LOOP_CYCLES/mpg123_tpf(fr));
|
||||
|
||||
if(mpg123_seek_frame(fr, 0, SEEK_SET) < 0)
|
||||
@@ -278,13 +277,11 @@ static void term_handle_key(mpg123_handle *fr, audio_output_t *ao, char val)
|
||||
framenum=0;
|
||||
break;
|
||||
case MPG123_NEXT_KEY:
|
||||
if(!param.usebuffer) ao->flush(ao);
|
||||
else buffer_resync(); /* was: plain_buffer_resync */
|
||||
out123_drop(ao);
|
||||
next_track();
|
||||
break;
|
||||
case MPG123_NEXT_DIR_KEY:
|
||||
if(!param.usebuffer) ao->flush(ao);
|
||||
else buffer_resync(); /* was: plain_buffer_resync */
|
||||
out123_drop(ao);
|
||||
next_dir();
|
||||
break;
|
||||
case MPG123_QUIT_KEY:
|
||||
@@ -292,75 +289,72 @@ static void term_handle_key(mpg123_handle *fr, audio_output_t *ao, char val)
|
||||
if(stopped)
|
||||
{
|
||||
stopped = 0;
|
||||
if(param.usebuffer)
|
||||
{
|
||||
buffer_resync();
|
||||
buffer_start();
|
||||
}
|
||||
out123_drop(ao);
|
||||
}
|
||||
set_intflag();
|
||||
offset = 0;
|
||||
break;
|
||||
case MPG123_PAUSE_KEY:
|
||||
paused=1-paused;
|
||||
if(paused) {
|
||||
if(paused)
|
||||
{
|
||||
/* Not really sure if that is what is wanted
|
||||
This jumps in audio output, but has direct reaction to pausing loop. */
|
||||
if(param.usebuffer) buffer_resync();
|
||||
out123_drop(ao);
|
||||
out123_param(ao, OUT123_PRELOAD, 0, 0.);
|
||||
|
||||
pause_recycle(fr);
|
||||
}
|
||||
else
|
||||
out123_param(ao, OUT123_PRELOAD, 0, param.preload);
|
||||
if(stopped)
|
||||
{
|
||||
stopped=0;
|
||||
if(param.usebuffer) buffer_start();
|
||||
out123_continue(ao);
|
||||
}
|
||||
fprintf(stderr, "%s", (paused) ? MPG123_PAUSED_STRING : MPG123_EMPTY_STRING);
|
||||
break;
|
||||
case MPG123_STOP_KEY:
|
||||
case ' ':
|
||||
/* when seeking while stopped and then resuming, I want to prevent the chirp from the past */
|
||||
if(!param.usebuffer) ao->flush(ao);
|
||||
/* TODO: Verify/ensure that there is no "chirp from the past" when
|
||||
seeking while stopped. */
|
||||
stopped=1-stopped;
|
||||
if(paused) {
|
||||
paused=0;
|
||||
offset -= pause_cycle;
|
||||
}
|
||||
if(param.usebuffer)
|
||||
{
|
||||
if(stopped) buffer_stop();
|
||||
if(stopped)
|
||||
out123_pause(ao);
|
||||
else
|
||||
{
|
||||
/* When we stopped buffer for seeking, we must resync. */
|
||||
if(offset) buffer_resync();
|
||||
|
||||
buffer_start();
|
||||
}
|
||||
if(offset) /* If position changed, old is outdated. */
|
||||
out123_drop(ao);
|
||||
out123_continue(ao);
|
||||
}
|
||||
fprintf(stderr, "%s", (stopped) ? MPG123_STOPPED_STRING : MPG123_EMPTY_STRING);
|
||||
break;
|
||||
case MPG123_FINE_REWIND_KEY:
|
||||
if(param.usebuffer) seekmode();
|
||||
seekmode(fr, ao);
|
||||
offset--;
|
||||
break;
|
||||
case MPG123_FINE_FORWARD_KEY:
|
||||
seekmode();
|
||||
seekmode(fr, ao);
|
||||
offset++;
|
||||
break;
|
||||
case MPG123_REWIND_KEY:
|
||||
seekmode();
|
||||
seekmode(fr, ao);
|
||||
offset-=10;
|
||||
break;
|
||||
case MPG123_FORWARD_KEY:
|
||||
seekmode();
|
||||
seekmode(fr, ao);
|
||||
offset+=10;
|
||||
break;
|
||||
case MPG123_FAST_REWIND_KEY:
|
||||
seekmode();
|
||||
seekmode(fr, ao);
|
||||
offset-=50;
|
||||
break;
|
||||
case MPG123_FAST_FORWARD_KEY:
|
||||
seekmode();
|
||||
seekmode(fr, ao);
|
||||
offset+=50;
|
||||
break;
|
||||
case MPG123_VOL_UP_KEY:
|
||||
@@ -403,15 +397,12 @@ static void term_handle_key(mpg123_handle *fr, audio_output_t *ao, char val)
|
||||
mpg123_volume_change(fr, 0.);
|
||||
break;
|
||||
case MPG123_PREV_KEY:
|
||||
if(!param.usebuffer) ao->flush(ao);
|
||||
else buffer_resync(); /* was: plain_buffer_resync */
|
||||
out123_drop(ao);
|
||||
|
||||
prev_track();
|
||||
break;
|
||||
case MPG123_PREV_DIR_KEY:
|
||||
if(!param.usebuffer) ao->flush(ao);
|
||||
else buffer_resync(); /* was: plain_buffer_resync */
|
||||
|
||||
out123_drop(ao);
|
||||
prev_dir();
|
||||
break;
|
||||
case MPG123_PLAYLIST_KEY:
|
||||
@@ -425,7 +416,7 @@ static void term_handle_key(mpg123_handle *fr, audio_output_t *ao, char val)
|
||||
fprintf(stderr, "\n");
|
||||
break;
|
||||
case MPG123_MPEG_KEY:
|
||||
if(param.verbose) print_stat(fr,0,0); /* Make sure that we are talking about the correct frame. */
|
||||
if(param.verbose) print_stat(fr,0,ao); /* Make sure that we are talking about the correct frame. */
|
||||
fprintf(stderr, "\n");
|
||||
print_header(fr);
|
||||
fprintf(stderr, "\n");
|
||||
@@ -484,10 +475,14 @@ static void term_handle_key(mpg123_handle *fr, audio_output_t *ao, char val)
|
||||
num = val == '0' ? 10 : val - '0';
|
||||
--num; /* from 0 to 9 */
|
||||
|
||||
seekmode();
|
||||
/* Do not swith to seekmode() here, as we are jumping once to a
|
||||
specific position. Dropping buffer contents is enough and there
|
||||
is no race filling the buffer or waiting for more incremental
|
||||
seek orders. */
|
||||
len = mpg123_length(fr);
|
||||
if(len > 0) mpg123_seek(fr, (off_t)( (num/10.)*len ), SEEK_SET);
|
||||
|
||||
if(len > 0)
|
||||
mpg123_seek(fr, (off_t)( (num/10.)*len ), SEEK_SET);
|
||||
out123_drop(ao);
|
||||
}
|
||||
break;
|
||||
case MPG123_BOOKMARK_KEY:
|
||||
|
@@ -3,6 +3,10 @@
|
||||
#include "dither.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* Directly include the code for testing, avoiding
|
||||
build of same object with and without libtool. */
|
||||
#include "../libmpg123/dither_impl.h"
|
||||
|
||||
const char *typenames[] = { "white", "tpdf", "highpass_tpdf" };
|
||||
enum mpg123_noise_type types[] = { mpg123_white_noise, mpg123_tpdf_noise, mpg123_highpass_tpdf_noise };
|
||||
|
||||
|
476
src/wav.c
476
src/wav.c
@@ -1,476 +0,0 @@
|
||||
/*
|
||||
wav.c: write wav files
|
||||
|
||||
copyright ?-2012 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).
|
||||
|
||||
TODO: convert fully to tab indent
|
||||
*/
|
||||
|
||||
#include "mpg123app.h"
|
||||
#include <errno.h>
|
||||
#include "debug.h"
|
||||
|
||||
/* Create the two WAV headers. */
|
||||
|
||||
#define WAVE_FORMAT 1
|
||||
#define RIFF_NAME RIFF
|
||||
#include "wavhead.h"
|
||||
|
||||
#undef WAVE_FORMAT
|
||||
#undef RIFF_NAME
|
||||
#define WAVE_FORMAT 3
|
||||
#define RIFF_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];
|
||||
} auhead = {
|
||||
{ 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 }};
|
||||
|
||||
|
||||
static FILE *wavfp;
|
||||
static long datalen = 0;
|
||||
static int flipendian=0;
|
||||
int bytes_per_sample = -1;
|
||||
int floatwav = 0; /* 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 = NULL;
|
||||
size_t the_header_size = 0;
|
||||
|
||||
/* 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) {
|
||||
error3("Strange endianness?? %08lx %08lx %08lx\n",a,b,c);
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* return: 0 is good, -1 is bad */
|
||||
static int open_file(char *filename)
|
||||
{
|
||||
#if defined(HAVE_SETUID) && defined(HAVE_GETUID)
|
||||
setuid(getuid()); /* dunno whether this helps. I'm not a security expert */
|
||||
#endif
|
||||
if(!strcmp("-",filename)) {
|
||||
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(wavfp, 0L, SEEK_SET);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
#ifdef WANT_WIN32_UNICODE
|
||||
wchar_t *filenamew = NULL;
|
||||
win32_utf8_wide(filename, &filenamew, NULL);
|
||||
if(filenamew == NULL) {
|
||||
wavfp = NULL;
|
||||
} else {
|
||||
wavfp = _wfopen(filenamew,L"wb");
|
||||
free(filenamew);
|
||||
}
|
||||
#else
|
||||
wavfp = fopen(filename,"wb");
|
||||
#endif
|
||||
if(!wavfp)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* return: 0 is good, -1 is bad */
|
||||
static int close_file()
|
||||
{
|
||||
if(wavfp != NULL && wavfp != stdout)
|
||||
{
|
||||
if(fclose(wavfp))
|
||||
{
|
||||
error1("problem closing the audio file, probably because of flushing to disk: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
wavfp = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return: 0 is good, -1 is bad */
|
||||
static int write_header(const void*ptr, size_t size)
|
||||
{
|
||||
if(size > 0 && (fwrite(ptr, size, 1, wavfp) != 1 || fflush(wavfp)))
|
||||
{
|
||||
error1("cannot write header: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
int au_open(audio_output_t *ao)
|
||||
{
|
||||
if(ao->format < 0) ao->format = MPG123_ENC_SIGNED_16;
|
||||
|
||||
if(ao->format & MPG123_ENC_FLOAT)
|
||||
{
|
||||
error("AU file support for float values not there yet");
|
||||
return -1;
|
||||
}
|
||||
|
||||
flipendian = 0;
|
||||
|
||||
if(ao->rate < 0) ao->rate = 44100;
|
||||
if(ao->channels < 0) ao->channels = 2;
|
||||
|
||||
switch(ao->format) {
|
||||
case MPG123_ENC_SIGNED_16:
|
||||
{
|
||||
int endiantest = testEndian();
|
||||
if(endiantest == -1) return -1;
|
||||
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:
|
||||
error("AU output is only a hack. This audio mode isn't supported yet.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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(ao->device) < 0)
|
||||
return -1;
|
||||
|
||||
datalen = 0;
|
||||
|
||||
the_header = &auhead;
|
||||
the_header_size = sizeof(auhead);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cdr_open(audio_output_t *ao)
|
||||
{
|
||||
if(ao->format < 0 && ao->rate < 0 && ao->channels < 0)
|
||||
{
|
||||
/* param.force_stereo = 0; */
|
||||
ao->format = MPG123_ENC_SIGNED_16;
|
||||
ao->rate = 44100;
|
||||
ao->channels = 2;
|
||||
}
|
||||
if(ao->format != MPG123_ENC_SIGNED_16 || ao->rate != 44100 || ao->channels != 2) {
|
||||
fprintf(stderr,"Oops .. not forced to 16 bit, 44kHz?, stereo\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
flipendian = !testEndian(); /* big end */
|
||||
|
||||
|
||||
if(open_file(ao->device) < 0)
|
||||
return -1;
|
||||
|
||||
the_header = NULL;
|
||||
the_header_size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wav_open(audio_output_t *ao)
|
||||
{
|
||||
int bps;
|
||||
|
||||
if(ao->format < 0) ao->format = MPG123_ENC_SIGNED_16;
|
||||
|
||||
flipendian = 0;
|
||||
|
||||
/* standard MS PCM, and its format specific is BitsPerSample */
|
||||
long2littleendian(1,RIFF.WAVE.fmt.FormatTag,sizeof(RIFF.WAVE.fmt.FormatTag));
|
||||
floatwav = 0;
|
||||
if(ao->format == MPG123_ENC_FLOAT_32)
|
||||
{
|
||||
floatwav = 1;
|
||||
long2littleendian(3,RIFF_FLOAT.WAVE.fmt.FormatTag,sizeof(RIFF_FLOAT.WAVE.fmt.FormatTag));
|
||||
long2littleendian(bps=32,RIFF_FLOAT.WAVE.fmt.BitsPerSample,sizeof(RIFF_FLOAT.WAVE.fmt.BitsPerSample));
|
||||
flipendian = testEndian();
|
||||
}
|
||||
else if(ao->format == MPG123_ENC_SIGNED_32) {
|
||||
long2littleendian(bps=32,RIFF.WAVE.fmt.BitsPerSample,sizeof(RIFF.WAVE.fmt.BitsPerSample));
|
||||
flipendian = testEndian();
|
||||
}
|
||||
else if(ao->format == MPG123_ENC_SIGNED_24) {
|
||||
long2littleendian(bps=24,RIFF.WAVE.fmt.BitsPerSample,sizeof(RIFF.WAVE.fmt.BitsPerSample));
|
||||
flipendian = testEndian();
|
||||
}
|
||||
else if(ao->format == MPG123_ENC_SIGNED_16) {
|
||||
long2littleendian(bps=16,RIFF.WAVE.fmt.BitsPerSample,sizeof(RIFF.WAVE.fmt.BitsPerSample));
|
||||
flipendian = testEndian();
|
||||
}
|
||||
else if(ao->format == MPG123_ENC_UNSIGNED_8)
|
||||
long2littleendian(bps=8,RIFF.WAVE.fmt.BitsPerSample,sizeof(RIFF.WAVE.fmt.BitsPerSample));
|
||||
else
|
||||
{
|
||||
error("Format not supported.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(ao->rate < 0) ao->rate = 44100;
|
||||
if(ao->channels < 0) ao->channels = 2;
|
||||
|
||||
if(floatwav)
|
||||
{
|
||||
long2littleendian(ao->channels,RIFF_FLOAT.WAVE.fmt.Channels,sizeof(RIFF_FLOAT.WAVE.fmt.Channels));
|
||||
long2littleendian(ao->rate,RIFF_FLOAT.WAVE.fmt.SamplesPerSec,sizeof(RIFF_FLOAT.WAVE.fmt.SamplesPerSec));
|
||||
long2littleendian((int)(ao->channels * ao->rate * bps)>>3,
|
||||
RIFF_FLOAT.WAVE.fmt.AvgBytesPerSec,sizeof(RIFF_FLOAT.WAVE.fmt.AvgBytesPerSec));
|
||||
long2littleendian((int)(ao->channels * bps)>>3,
|
||||
RIFF_FLOAT.WAVE.fmt.BlockAlign,sizeof(RIFF_FLOAT.WAVE.fmt.BlockAlign));
|
||||
}
|
||||
else
|
||||
{
|
||||
long2littleendian(ao->channels,RIFF.WAVE.fmt.Channels,sizeof(RIFF.WAVE.fmt.Channels));
|
||||
long2littleendian(ao->rate,RIFF.WAVE.fmt.SamplesPerSec,sizeof(RIFF.WAVE.fmt.SamplesPerSec));
|
||||
long2littleendian((int)(ao->channels * ao->rate * bps)>>3,
|
||||
RIFF.WAVE.fmt.AvgBytesPerSec,sizeof(RIFF.WAVE.fmt.AvgBytesPerSec));
|
||||
long2littleendian((int)(ao->channels * bps)>>3,
|
||||
RIFF.WAVE.fmt.BlockAlign,sizeof(RIFF.WAVE.fmt.BlockAlign));
|
||||
}
|
||||
|
||||
if(open_file(ao->device) < 0)
|
||||
return -1;
|
||||
|
||||
if(floatwav)
|
||||
{
|
||||
long2littleendian(datalen,RIFF_FLOAT.WAVE.data.datalen,sizeof(RIFF_FLOAT.WAVE.data.datalen));
|
||||
long2littleendian(datalen+sizeof(RIFF_FLOAT.WAVE),RIFF_FLOAT.WAVElen,sizeof(RIFF_FLOAT.WAVElen));
|
||||
}
|
||||
else
|
||||
{
|
||||
long2littleendian(datalen,RIFF.WAVE.data.datalen,sizeof(RIFF.WAVE.data.datalen));
|
||||
long2littleendian(datalen+sizeof(RIFF.WAVE),RIFF.WAVElen,sizeof(RIFF.WAVElen));
|
||||
}
|
||||
|
||||
if(floatwav)
|
||||
{
|
||||
the_header = &RIFF_FLOAT;
|
||||
the_header_size = sizeof(RIFF_FLOAT);
|
||||
}
|
||||
else
|
||||
{
|
||||
the_header = &RIFF;
|
||||
the_header_size = sizeof(RIFF);
|
||||
}
|
||||
|
||||
datalen = 0;
|
||||
bytes_per_sample = bps>>3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wav_write(unsigned char *buf,int len)
|
||||
{
|
||||
int temp;
|
||||
int i;
|
||||
|
||||
if(!wavfp)
|
||||
return 0;
|
||||
|
||||
if(datalen == 0)
|
||||
{
|
||||
if(write_header(the_header, the_header_size) < 0) return -1;
|
||||
}
|
||||
|
||||
if(flipendian)
|
||||
{
|
||||
if(bytes_per_sample == 4) /* 32 bit */
|
||||
{
|
||||
if(len & 3)
|
||||
{
|
||||
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, wavfp);
|
||||
if(temp <= 0) return temp;
|
||||
/* That would kill it of early when running out of disk space. */
|
||||
#if 0
|
||||
if(fflush(wavfp))
|
||||
{
|
||||
fprintf(stderr, "flushing failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
datalen += temp;
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
int wav_close(void)
|
||||
{
|
||||
if(!wavfp) return -1;
|
||||
|
||||
/* flush before seeking to catch out-of-disk explicitly at least at the end */
|
||||
if(fflush(wavfp))
|
||||
{
|
||||
error1("cannot flush WAV stream: %s", strerror(errno));
|
||||
fclose(wavfp);
|
||||
return -1;
|
||||
}
|
||||
if(fseek(wavfp, 0L, SEEK_SET) >= 0)
|
||||
{
|
||||
if(floatwav)
|
||||
{
|
||||
long2littleendian(datalen,RIFF_FLOAT.WAVE.data.datalen,sizeof(RIFF_FLOAT.WAVE.data.datalen));
|
||||
long2littleendian(datalen+sizeof(RIFF_FLOAT.WAVE),RIFF_FLOAT.WAVElen,sizeof(RIFF_FLOAT.WAVElen));
|
||||
long2littleendian(datalen/(from_little(RIFF_FLOAT.WAVE.fmt.Channels,2)*from_little(RIFF_FLOAT.WAVE.fmt.BitsPerSample,2)/8),
|
||||
RIFF_FLOAT.WAVE.fact.samplelen,sizeof(RIFF_FLOAT.WAVE.fact.samplelen));
|
||||
/* Always (over)writing the header here; also for stdout, when fseek worked, this overwrite works. */
|
||||
write_header(&RIFF_FLOAT, sizeof(RIFF_FLOAT));
|
||||
}
|
||||
else
|
||||
{
|
||||
long2littleendian(datalen,RIFF.WAVE.data.datalen,sizeof(RIFF.WAVE.data.datalen));
|
||||
long2littleendian(datalen+sizeof(RIFF.WAVE),RIFF.WAVElen,sizeof(RIFF.WAVElen));
|
||||
/* Always (over)writing the header here; also for stdout, when fseek worked, this overwrite works. */
|
||||
write_header(&RIFF, sizeof(RIFF));
|
||||
}
|
||||
}
|
||||
else
|
||||
warning("Cannot rewind WAV file. File-format isn't fully conform now.");
|
||||
|
||||
return close_file();
|
||||
}
|
||||
|
||||
int au_close(void)
|
||||
{
|
||||
if(!wavfp) return -1;
|
||||
|
||||
/* flush before seeking to catch out-of-disk explicitly at least at the end */
|
||||
if(fflush(wavfp))
|
||||
{
|
||||
error1("cannot flush WAV stream: %s", strerror(errno));
|
||||
fclose(wavfp);
|
||||
return -1;
|
||||
}
|
||||
if(fseek(wavfp, 0L, SEEK_SET) >= 0) {
|
||||
long2bigendian(datalen,auhead.datalen,sizeof(auhead.datalen));
|
||||
/* Always (over)writing the header here; also for stdout, when fseek worked, this overwrite works. */
|
||||
write_header(&auhead, sizeof(auhead));
|
||||
}
|
||||
else
|
||||
warning("Cannot rewind AU file. File-format isn't fully conform now.");
|
||||
|
||||
return close_file();
|
||||
}
|
||||
|
||||
int cdr_close(void)
|
||||
{
|
||||
if(!wavfp) return -1;
|
||||
|
||||
return close_file();
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
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_
|
||||
|
||||
#ifndef TRUE
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
size_t freeindex; /* [W] next free index */
|
||||
size_t readindex; /* [R] next index to read */
|
||||
int fd[2];
|
||||
int wakeme[2];
|
||||
byte *data;
|
||||
byte *metadata;
|
||||
size_t size;
|
||||
size_t metasize;
|
||||
long rate;
|
||||
int channels;
|
||||
int format;
|
||||
int justwait;
|
||||
} 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);
|
||||
#define XF_CMD_WAKEUP_INFO 0x04
|
||||
#define XF_CMD_WAKEUP 0x02
|
||||
#define XF_CMD_TERMINATE 0x03
|
||||
#define XF_CMD_AUDIOCAP 0x05
|
||||
#define XF_CMD_RESYNC 0x06
|
||||
#define XF_CMD_ABORT 0x07
|
||||
#define XF_WRITER 0
|
||||
#define XF_READER 1
|
||||
int xfermem_getcmd (int fd, int block);
|
||||
int xfermem_putcmd (int fd, byte cmd);
|
||||
int xfermem_block (int fd, txfermem *xf);
|
||||
int xfermem_sigblock (int fd, txfermem *xf, int pid, int signal);
|
||||
/* returns TRUE for being interrupted */
|
||||
int xfermem_write(txfermem *xf, byte *buffer, size_t bytes);
|
||||
|
||||
void xfermem_done (txfermem *xf);
|
||||
#define xfermem_done_writer xfermem_init_reader
|
||||
#define xfermem_done_reader xfermem_init_writer
|
||||
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user