1
0
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:
thor
2015-09-04 07:06:24 +00:00
parent 7a43c67d9f
commit 382a464cfd
83 changed files with 6365 additions and 3257 deletions

View File

@@ -3,98 +3,128 @@
## copyright by the mpg123 project - free software under the terms of the LGPL 2.1 ## 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 ## see COPYING and AUTHORS files in distribution or http://mpg123.org
## initially written by Nicholas J. Humfrey ## 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 # Mention all global variables first before including Make modules.
DIST_SUBDIRS = src doc
ACLOCAL_AMFLAGS = -I m4 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 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! # mpg123.spec is autogenerated but needs to be present in tarball!
EXTRA_DIST = \ EXTRA_DIST += \
mpg123.spec \ mpg123.spec \
makedll.sh \ makedll.sh \
windows-builds.sh \ windows-builds.sh \
equalize.dat \ equalize.dat \
NEWS.libmpg123 \ NEWS.libmpg123 \
ports/MSVC++/mpg123.h \ ports/MSVC++/mpg123.h \
ports/MSVC++/config.h \ ports/MSVC++/config.h \
ports/MSVC++/msvc.c \ ports/MSVC++/msvc.c \
ports/MSVC++/examples/scan.c \ ports/MSVC++/examples/scan.c \
ports/MSVC++/examples/feedseek.c \ ports/MSVC++/examples/feedseek.c \
ports/MSVC++/2005/libmpg123/libmpg123.vcproj \ ports/MSVC++/2005/libmpg123/libmpg123.vcproj \
ports/MSVC++/2008/mpg123.sln \ ports/MSVC++/2008/mpg123.sln \
ports/MSVC++/2008/feedseek/feedseek.vcproj \ ports/MSVC++/2008/feedseek/feedseek.vcproj \
ports/MSVC++/2008/mpglib/mpglib.vcproj \ ports/MSVC++/2008/mpglib/mpglib.vcproj \
ports/MSVC++/2008/libmpg123/libmpg123.vcproj \ ports/MSVC++/2008/libmpg123/libmpg123.vcproj \
ports/MSVC++/2008/scan/scan.vcproj \ ports/MSVC++/2008/scan/scan.vcproj \
ports/MSVC++/2008/dump_seekindex/dump_seekindex.vcproj \ ports/MSVC++/2008/dump_seekindex/dump_seekindex.vcproj \
ports/MSVC++/2008clr/2008clr.sln \ ports/MSVC++/2008clr/2008clr.sln \
ports/MSVC++/2008clr/mpg123clr/advanced.cpp \ ports/MSVC++/2008clr/mpg123clr/advanced.cpp \
ports/MSVC++/2008clr/mpg123clr/advanced.h \ ports/MSVC++/2008clr/mpg123clr/advanced.h \
ports/MSVC++/2008clr/mpg123clr/AssemblyInfo.cpp \ ports/MSVC++/2008clr/mpg123clr/AssemblyInfo.cpp \
ports/MSVC++/2008clr/mpg123clr/dllmain.cpp \ ports/MSVC++/2008clr/mpg123clr/dllmain.cpp \
ports/MSVC++/2008clr/mpg123clr/enum.h \ ports/MSVC++/2008clr/mpg123clr/enum.h \
ports/MSVC++/2008clr/mpg123clr/error.cpp \ ports/MSVC++/2008clr/mpg123clr/error.cpp \
ports/MSVC++/2008clr/mpg123clr/error.h \ ports/MSVC++/2008clr/mpg123clr/error.h \
ports/MSVC++/2008clr/mpg123clr/id3v1.cpp \ ports/MSVC++/2008clr/mpg123clr/id3v1.cpp \
ports/MSVC++/2008clr/mpg123clr/id3v1.h \ ports/MSVC++/2008clr/mpg123clr/id3v1.h \
ports/MSVC++/2008clr/mpg123clr/id3v2.cpp \ ports/MSVC++/2008clr/mpg123clr/id3v2.cpp \
ports/MSVC++/2008clr/mpg123clr/id3v2.h \ ports/MSVC++/2008clr/mpg123clr/id3v2.h \
ports/MSVC++/2008clr/mpg123clr/mpg123clr.cpp \ ports/MSVC++/2008clr/mpg123clr/mpg123clr.cpp \
ports/MSVC++/2008clr/mpg123clr/mpg123clr.h \ ports/MSVC++/2008clr/mpg123clr/mpg123clr.h \
ports/MSVC++/2008clr/mpg123clr/mpg123clr.rc \ ports/MSVC++/2008clr/mpg123clr/mpg123clr.rc \
ports/MSVC++/2008clr/mpg123clr/mpg123clr.vcproj \ ports/MSVC++/2008clr/mpg123clr/mpg123clr.vcproj \
ports/MSVC++/2008clr/mpg123clr/ReadMe.txt \ ports/MSVC++/2008clr/mpg123clr/ReadMe.txt \
ports/MSVC++/2008clr/mpg123clr/resource.h \ ports/MSVC++/2008clr/mpg123clr/resource.h \
ports/MSVC++/2008clr/mpg123clr/stdafx.cpp \ ports/MSVC++/2008clr/mpg123clr/stdafx.cpp \
ports/MSVC++/2008clr/mpg123clr/stdafx.h \ ports/MSVC++/2008clr/mpg123clr/stdafx.h \
ports/MSVC++/2008clr/mpg123clr/string.cpp \ ports/MSVC++/2008clr/mpg123clr/string.cpp \
ports/MSVC++/2008clr/mpg123clr/string.h \ ports/MSVC++/2008clr/mpg123clr/string.h \
ports/MSVC++/2008clr/mpg123clr/targetver.h \ ports/MSVC++/2008clr/mpg123clr/targetver.h \
ports/MSVC++/2008clr/mpg123clr/text.cpp \ ports/MSVC++/2008clr/mpg123clr/text.cpp \
ports/MSVC++/2008clr/mpg123clr/text.h \ ports/MSVC++/2008clr/mpg123clr/text.h \
ports/MSVC++/2008clr/examples/feedseekclr/feedseekclr.csproj \ ports/MSVC++/2008clr/examples/feedseekclr/feedseekclr.csproj \
ports/MSVC++/2008clr/examples/feedseekclr/Program.cs \ ports/MSVC++/2008clr/examples/feedseekclr/Program.cs \
ports/MSVC++/2008clr/examples/feedseekclr/Properties/AssemblyInfo.cs \ ports/MSVC++/2008clr/examples/feedseekclr/Properties/AssemblyInfo.cs \
ports/MSVC++/2008clr/examples/ReplaceReaderclr/ReplaceReaderclr.csproj \ ports/MSVC++/2008clr/examples/ReplaceReaderclr/ReplaceReaderclr.csproj \
ports/MSVC++/2008clr/examples/ReplaceReaderclr/Program.cs \ ports/MSVC++/2008clr/examples/ReplaceReaderclr/Program.cs \
ports/MSVC++/2008clr/examples/ReplaceReaderclr/Properties/AssemblyInfo.cs \ ports/MSVC++/2008clr/examples/ReplaceReaderclr/Properties/AssemblyInfo.cs \
ports/MSVC++/2008clr/examples/scanclr/scanclr.csproj \ ports/MSVC++/2008clr/examples/scanclr/scanclr.csproj \
ports/MSVC++/2008clr/examples/scanclr/Program.cs \ ports/MSVC++/2008clr/examples/scanclr/Program.cs \
ports/MSVC++/2008clr/examples/scanclr/Properties/AssemblyInfo.cs \ ports/MSVC++/2008clr/examples/scanclr/Properties/AssemblyInfo.cs \
ports/MSVC++/2010/mpg123.sln \ ports/MSVC++/2010/mpg123.sln \
ports/MSVC++/2010/dump_seekindex/dump_seekindex.vcxproj \ ports/MSVC++/2010/dump_seekindex/dump_seekindex.vcxproj \
ports/MSVC++/2010/dump_seekindex/dump_seekindex.vcxproj.filters \ ports/MSVC++/2010/dump_seekindex/dump_seekindex.vcxproj.filters \
ports/MSVC++/2010/feedseek/feedseek.vcxproj \ ports/MSVC++/2010/feedseek/feedseek.vcxproj \
ports/MSVC++/2010/feedseek/feedseek.vcxproj.filters \ ports/MSVC++/2010/feedseek/feedseek.vcxproj.filters \
ports/MSVC++/2010/libmpg123/libmpg123.vcxproj \ ports/MSVC++/2010/libmpg123/libmpg123.vcxproj \
ports/MSVC++/2010/scan/scan.vcxproj \ ports/MSVC++/2010/scan/scan.vcxproj \
ports/MSVC++/2010/scan/scan.vcxproj.filters \ ports/MSVC++/2010/scan/scan.vcxproj.filters \
ports/MSVC++/CMP3Stream/libMPG123/libMPG123.vcproj \ ports/MSVC++/CMP3Stream/libMPG123/libMPG123.vcproj \
ports/MSVC++/CMP3Stream/libMPG123/PLACE_LIBMPG123_SOURCES_HERE \ ports/MSVC++/CMP3Stream/libMPG123/PLACE_LIBMPG123_SOURCES_HERE \
ports/MSVC++/CMP3Stream/README \ ports/MSVC++/CMP3Stream/README \
ports/MSVC++/CMP3Stream/SOURCE/CORE_Log.CPP \ ports/MSVC++/CMP3Stream/SOURCE/CORE_Log.CPP \
ports/MSVC++/CMP3Stream/SOURCE/CORE_FileIn.CPP \ ports/MSVC++/CMP3Stream/SOURCE/CORE_FileIn.CPP \
ports/MSVC++/CMP3Stream/SOURCE/SourceFilter_MP3Stream.CPP \ ports/MSVC++/CMP3Stream/SOURCE/SourceFilter_MP3Stream.CPP \
ports/MSVC++/CMP3Stream/SOURCE/CORE_Mutex.CPP \ ports/MSVC++/CMP3Stream/SOURCE/CORE_Mutex.CPP \
ports/MSVC++/CMP3Stream/INCLUDE/CORE/CORE_FileIn.H \ ports/MSVC++/CMP3Stream/INCLUDE/CORE/CORE_FileIn.H \
ports/MSVC++/CMP3Stream/INCLUDE/CORE/SourceFilter_MP3.H \ ports/MSVC++/CMP3Stream/INCLUDE/CORE/SourceFilter_MP3.H \
ports/MSVC++/CMP3Stream/INCLUDE/IIEP_FileIn.H \ ports/MSVC++/CMP3Stream/INCLUDE/IIEP_FileIn.H \
ports/MSVC++/CMP3Stream/INCLUDE/IIEP_Def.H \ ports/MSVC++/CMP3Stream/INCLUDE/IIEP_Def.H \
ports/README \ ports/README \
ports/Sony_PSP/config.h \ ports/Sony_PSP/config.h \
ports/Sony_PSP/README \ ports/Sony_PSP/README \
ports/Sony_PSP/Makefile.psp \ ports/Sony_PSP/Makefile.psp \
ports/Sony_PSP/readers.c.patch \ ports/Sony_PSP/readers.c.patch \
ports/mpg123_.pas \ ports/mpg123_.pas \
ports/Xcode/config.h \ ports/Xcode/config.h \
ports/Xcode/mpg123.h \ ports/Xcode/mpg123.h \
ports/Xcode/mpg123.xcodeproj/project.pbxproj \ ports/Xcode/mpg123.xcodeproj/project.pbxproj \
scripts/benchmark-cpu.pl \ scripts/benchmark-cpu.pl \
scripts/tag_lyrics.py \ scripts/tag_lyrics.py \
scripts/conplay \ scripts/conplay \
scripts/mpg123info scripts/mpg123info

17
NEWS
View File

@@ -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 1.23.0
--- ---
- Added mpg123 --no-infoframe. - Added mpg123 --no-infoframe.
@@ -8,6 +18,13 @@
- Warning messages also start with a line break now to better fit in with - Warning messages also start with a line break now to better fit in with
verbose playback. verbose playback.
- Reporting of clipped samples also includes a line break now. - 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). - Silently skip APE tags (thanks to Hans de Goede).
- Some reduction in bitrot on AIX (typos in output module, build with - Some reduction in bitrot on AIX (typos in output module, build with
--disable-largefile --with-audio=aix, real test welcome). --disable-largefile --with-audio=aix, real test welcome).

View File

@@ -10,12 +10,23 @@ AC_PREREQ(2.57)
dnl ############# Initialisation dnl ############# Initialisation
AC_INIT([mpg123], [1.23.0], [mpg123-devel@lists.sourceforge.net]) AC_INIT([mpg123], [1.23.0], [mpg123-devel@lists.sourceforge.net])
dnl Increment API_VERSION when the API gets changes (new functions). dnl Increment API_VERSION when the API gets changes (new functions).
dnl libmpg123
API_VERSION=41 API_VERSION=41
LIB_PATCHLEVEL=4 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. dnl Since we want to be backwards compatible, both sides get set to API_VERSION.
LIBMPG123_VERSION=$API_VERSION:$LIB_PATCHLEVEL:$API_VERSION LIBMPG123_VERSION=$API_VERSION:$LIB_PATCHLEVEL:$API_VERSION
LIBOUT123_VERSION=$OUTAPI_VERSION:$OUTLIB_PATCHLEVEL:$OUTAPI_VERSION
AC_SUBST(LIBMPG123_VERSION) AC_SUBST(LIBMPG123_VERSION)
AC_SUBST(API_VERSION) AC_SUBST(API_VERSION)
AC_SUBST(LIBOUT123_VERSION)
AC_SUBST(OUTAPI_VERSION)
AC_CONFIG_SRCDIR(src/mpg123.c) AC_CONFIG_SRCDIR(src/mpg123.c)
AC_CONFIG_AUX_DIR(build) AC_CONFIG_AUX_DIR(build)
@@ -24,7 +35,9 @@ AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_HOST AC_CANONICAL_HOST
dnl Version 1.7 of automake is recommended 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]) AC_CONFIG_HEADERS([src/config.h])
@@ -164,17 +177,8 @@ dnl Configure libtool
AC_LIBTOOL_WIN32_DLL AC_LIBTOOL_WIN32_DLL
AM_PROG_LIBTOOL 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] ) AM_CONDITIONAL( [HAVE_MODULES], [test "x$modules" = xenabled] )
AC_SUBST(MODULE_OBJ)
AC_SUBST(LT_LDFLAGS) AC_SUBST(LT_LDFLAGS)
AC_SUBST(EXEC_LT_LDFLAGS) AC_SUBST(EXEC_LT_LDFLAGS)
@@ -1057,6 +1061,8 @@ else
AC_MSG_RESULT([no]) AC_MSG_RESULT([no])
fi 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) AC_SUBST(LFS_LOBJ)
@@ -1472,6 +1478,12 @@ do
word_in_list "$i.lo" $DECODER_LOBJ || DECODER_LOBJ="$DECODER_LOBJ $i.lo" word_in_list "$i.lo" $DECODER_LOBJ || DECODER_LOBJ="$DECODER_LOBJ $i.lo"
done 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_OBJ)
AC_SUBST(DECODER_LOBJ) AC_SUBST(DECODER_LOBJ)
@@ -1954,168 +1966,56 @@ fi
# That's (beginning of) the list for mpg123's internal default. # That's (beginning of) the list for mpg123's internal default.
default_output_modules=$default_output_module default_output_modules=$default_output_module
OUTPUT_OBJ=
OUTPUT_MOD=
OUTPUT_CFLAGS=
OUTPUT_LIBS=
OUTPUT_LDFLAGS=
# Setup the static build. # Setup the static build.
if test "x$modules" = xdisabled # The conditionals always need to be defined by configure, even if
then # HAVE_MODULES is FALSE!
echo "Preparing static output $default_output_module" # Here's a script for that tedious list, perhaps to be outsourced together with the one in #src/output/Makefile.am
OUTPUT_MOD="$default_output_module" #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
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
#do echo $i; done | #do echo $i; done |
#perl -ne 'chomp; $big = uc($_); print <<EOT; #perl -ne 'chomp; $big = uc($_); print <<EOT;
# $_) #AM_CONDITIONAL([BUILD_${big}], [ test "$_" = \$default_output_module ])
# OUTPUT_LIBS="\$${big}_LIBS"
# OUTPUT_LDFLAGS="\$${big}_LDFLAGS"
# OUTPUT_CFLAGS="\$${big}_CFLAGS"
# ;;
#EOT #EOT
#' #'
tinyalsa)
OUTPUT_LIBS="$TINYALSA_LIBS" AM_CONDITIONAL([BUILD_DUMMY], [ test "dummy" = $default_output_module ])
OUTPUT_LDFLAGS="$TINYALSA_LDFLAGS" AM_CONDITIONAL([BUILD_TINYALSA], [ test "tinyalsa" = $default_output_module ])
OUTPUT_CFLAGS="$TINYALSA_CFLAGS" AM_CONDITIONAL([BUILD_ALSA], [ test "alsa" = $default_output_module ])
;; AM_CONDITIONAL([BUILD_QSA], [ test "qsa" = $default_output_module ])
alsa) AM_CONDITIONAL([BUILD_COREAUDIO], [ test "coreaudio" = $default_output_module ])
OUTPUT_LIBS="$ALSA_LIBS" AM_CONDITIONAL([BUILD_ESD], [ test "esd" = $default_output_module ])
OUTPUT_LDFLAGS="$ALSA_LDFLAGS" AM_CONDITIONAL([BUILD_JACK], [ test "jack" = $default_output_module ])
OUTPUT_CFLAGS="$ALSA_CFLAGS" AM_CONDITIONAL([BUILD_NAS], [ test "nas" = $default_output_module ])
;; AM_CONDITIONAL([BUILD_OSS], [ test "oss" = $default_output_module ])
qsa) AM_CONDITIONAL([BUILD_PORTAUDIO], [ test "portaudio" = $default_output_module ])
OUTPUT_LIBS="$QSA_LIBS" AM_CONDITIONAL([BUILD_PULSE], [ test "pulse" = $default_output_module ])
OUTPUT_LDFLAGS="$QSA_LDFLAGS" AM_CONDITIONAL([BUILD_SDL], [ test "sdl" = $default_output_module ])
OUTPUT_CFLAGS="$QSA_CFLAGS" AM_CONDITIONAL([BUILD_SNDIO], [ test "sndio" = $default_output_module ])
;; AM_CONDITIONAL([BUILD_SUN], [ test "sun" = $default_output_module ])
coreaudio) AM_CONDITIONAL([BUILD_WIN32], [ test "win32" = $default_output_module ])
OUTPUT_LIBS="$COREAUDIO_LIBS" AM_CONDITIONAL([BUILD_WIN32_WASAPI], [ test "win32_wasapi" = $default_output_module ])
OUTPUT_LDFLAGS="$COREAUDIO_LDFLAGS" AM_CONDITIONAL([BUILD_AIX], [ test "aix" = $default_output_module ])
OUTPUT_CFLAGS="$COREAUDIO_CFLAGS" AM_CONDITIONAL([BUILD_ALIB], [ test "alib" = $default_output_module ])
;; AM_CONDITIONAL([BUILD_ARTS], [ test "arts" = $default_output_module ])
esd) AM_CONDITIONAL([BUILD_HP], [ test "hp" = $default_output_module ])
OUTPUT_LIBS="$ESD_LIBS" AM_CONDITIONAL([BUILD_OS2], [ test "os2" = $default_output_module ])
OUTPUT_LDFLAGS="$ESD_LDFLAGS" AM_CONDITIONAL([BUILD_SGI], [ test "sgi" = $default_output_module ])
OUTPUT_CFLAGS="$ESD_CFLAGS" AM_CONDITIONAL([BUILD_MINT], [ test "mint" = $default_output_module ])
;; AM_CONDITIONAL([BUILD_OPENAL], [ test "openal" = $default_output_module ])
jack)
OUTPUT_LIBS="$JACK_LIBS" if test "x$modules" = xenabled
OUTPUT_LDFLAGS="$JACK_LDFLAGS" then
OUTPUT_CFLAGS="$JACK_CFLAGS"
;; # Now make a comma-separated list again... eliminating the possible duplicate and dummy.
nas) for i in $output_modules
OUTPUT_LIBS="$NAS_LIBS" do
OUTPUT_LDFLAGS="$NAS_LDFLAGS" if test $i != $default_output_module && test $i != dummy; then
OUTPUT_CFLAGS="$NAS_CFLAGS" default_output_modules=$default_output_modules,$i
;; fi
oss) done
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
# Now make a comma-separated list again... eliminating the possible duplicate and dummy.
for i in $output_modules
do
if test $i != $default_output_module && test $i != dummy; then
default_output_modules=$default_output_modules,$i
fi
done
fi fi
AC_DEFINE_UNQUOTED( DEFAULT_OUTPUT_MODULE, "$default_output_modules", [The default audio output module(s) to use] ) 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 dnl ############## Compiler Optimizations
CFLAGS="$ADD_CFLAGS $CFLAGS" CFLAGS="$ADD_CFLAGS $CFLAGS"
@@ -2398,22 +2298,12 @@ fi
dnl ############## Final Output 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([ AC_CONFIG_FILES([
Makefile Makefile
libmpg123.pc libmpg123.pc
mpg123.spec mpg123.spec
doc/Makefile
src/Makefile
src/output/Makefile
src/libmpg123/Makefile
src/libmpg123/mpg123.h src/libmpg123/mpg123.h
src/libout123/out123.h
]) ])
AC_OUTPUT AC_OUTPUT

View File

@@ -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
View 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
View 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.

View File

@@ -2,64 +2,105 @@
use strict; use strict;
my $dir = 'src/libmpg123'; print STDERR "Do not forget to handle renaming of symbols from a statically linked output module!\n";
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);
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. for my $i (@instances)
my @symbols = qw(COS9 tfcos36 pnts);
foreach my $header (@headers)
{ {
print STDERR "==== working on header $header\n"; my $dir = $i->{dir};
open(DAT, '<', $dir.'/'.$header.'.h') or die "Cannot open $header.\n"; print STDERR "dir: $dir\n";
while(<DAT>) 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}})
{ {
if(/^([^\s\(#][^\(]*)\s\*?([a-z][a-z_0-9]+)\s*\(/) print STDERR "==== working on header $header\n";
{ open(DAT, '<', $dir.'/'.$header.'.h') or die "Cannot open $header.\n";
# Skip preprocessing/comment stuff and official API. while(<DAT>)
unless($1 =~ '^#' or $1 =~ '/\*' or $2 =~ /^mpg123_/) { # Only simple declarations parsed here, more configured manually.
if(/^([^\s\(#][^\(]*)\s\*?([a-z][a-z_0-9]+)\s*\(/)
{ {
push(@symbols, $2) unless grep {$_ eq $2} @leavealone; # Skip preprocessing/comment stuff and official API.
unless($1 =~ '^#' or $1 =~ '/\*' or $2 =~ $apiex)
{
push(@symbols, $2) unless grep {$_ eq $2} (keys %{$i->{conditional}});
}
} }
} }
close(DAT);
} }
close(DAT);
}
print STDERR join("\n", glob("$dir/*.S"))."\n"; print STDERR join("\n", glob("$dir/*.S"))."\n";
foreach my $asm (glob("$dir/*.S")) 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>)
{ {
if(/^\s*\.globl\s+ASM_NAME\((\S+)\)$/) print STDERR "==== working on asm file $asm\n";
open(DAT, '<', $asm) or die "Cannot open $asm.\n";
while(<DAT>)
{ {
print STDERR; if(/^\s*\.globl\s+ASM_NAME\((\S+)\)$/)
push(@symbols, $1) unless grep {$_ eq $1} @symbols; {
print STDERR;
push(@symbols, $1) unless grep {$_ eq $1} @symbols;
}
} }
close(DAT);
} }
close(DAT);
}
print "#ifndef MPG123_INTMAP_H\n"; print $out "#ifndef $i->{guard}\n";
print "#define MPG123_INTMAP_H\n"; print $out "#define $i->{guard}\n";
print "/* Mapping of internal mpg123 symbols to something that is less likely to conflict in case of static linking. */\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) foreach my $sym (@symbols)
{
my $name = $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"; my $name = $i->{prefix}.$sym;
my $signi = substr($name,0,31);
#print STDERR "$name / $signi\n";
if(++$ident{$signi} > 1)
{
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";
}

View File

@@ -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
View 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

View File

@@ -1,16 +1,16 @@
/* /*
audio: audio output interface 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp initially written by Michael Hipp
*/ */
#include <errno.h> #include <errno.h>
#include "mpg123app.h" #include "mpg123app.h"
#include "out123.h"
#include "common.h" #include "common.h"
#include "sysutil.h" #include "sysutil.h"
#include "buffer.h"
#ifdef HAVE_SYS_WAIT_H #ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h> #include <sys/wait.h>
@@ -18,237 +18,6 @@
#include "debug.h" #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 struct enc_desc
{ {
int code; /* MPG123_ENC_SOMETHING */ int code; /* MPG123_ENC_SOMETHING */
@@ -320,6 +89,7 @@ const char* audio_encoding_name(const int encoding, const int longer)
return name; return name;
} }
static void capline(mpg123_handle *mh, long rate) static void capline(mpg123_handle *mh, long rate)
{ {
int enci; int enci;
@@ -347,13 +117,9 @@ void print_capabilities(audio_output_t *ao, mpg123_handle *mh)
size_t num_rates; size_t num_rates;
const int *encs; const int *encs;
size_t num_encs; size_t num_encs;
const char *name = "<buffer>"; char *name;
const char *dev = "<none>"; char *dev;
if(!param.usebuffer) out123_driver_info(ao, &name, &dev);
{
name = ao->module ? ao->module->name : "file/raw/test";
if(ao->device != NULL) dev = ao->device;
}
mpg123_rates(&rates, &num_rates); mpg123_rates(&rates, &num_rates);
mpg123_encodings(&encs, &num_encs); 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); 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. */ mpg123_format_none(mh); /* Start with nothing. */
if(param.force_encoding != NULL) if(param.force_encoding != NULL)
{ {
int i;
if(!param.quiet) fprintf(stderr, "Note: forcing output encoding %s\n", param.force_encoding); if(!param.quiet) fprintf(stderr, "Note: forcing output encoding %s\n", param.force_encoding);
for(i=0;i<KNOWN_ENCS;++i) force_fmt = audio_enc_name2code(param.force_encoding);
if(!strncasecmp(encdesc[i].name, param.force_encoding, encdesc[i].nlen)) if(!force_fmt)
{
force_fmt = encdesc[i].code;
break;
}
if(i==KNOWN_ENCS)
{ {
error1("Failed to find an encoding to match requested \"%s\"!\n", param.force_encoding); error1("Failed to find an encoding to match requested \"%s\"!\n", param.force_encoding);
return; /* No capabilities at all... */ 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; decode_rate = ri < num_rates ? rates[ri] : param.force_rate;
rate = pitch_rate(decode_rate); rate = pitch_rate(decode_rate);
if(param.verbose > 2) fprintf(stderr, "Note: checking support for %liHz/%ich.\n", rate, channels); if(param.verbose > 2) fprintf(stderr, "Note: checking support for %liHz/%ich.\n", rate, channels);
#ifndef NOXFERMEM
if(param.usebuffer) fmts = out123_encodings(ao, channels, rate);
{ /* 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);
}
if(param.verbose > 2) fprintf(stderr, "Note: result 0x%x\n", fmts); if(param.verbose > 2) fprintf(stderr, "Note: result 0x%x\n", fmts);
if(force_fmt) if(force_fmt)
{ /* Filter for forced encoding. */ { /* 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); 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(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 set_pitch(mpg123_handle *fr, audio_output_t *ao, double new_pitch)
{ {
int ret = 1;
double old_pitch = param.pitch; double old_pitch = param.pitch;
long rate; long rate;
int channels, format; int channels, format;
@@ -706,19 +204,13 @@ int set_pitch(mpg123_handle *fr, audio_output_t *ao, double new_pitch)
return 0; return 0;
} }
if(param.usebuffer)
{
error("No runtime pitch change with output buffer, sorry.");
return 0;
}
param.pitch = new_pitch; param.pitch = new_pitch;
if(param.pitch < -0.99) param.pitch = -0.99; if(param.pitch < -0.99) param.pitch = -0.99;
if(channels == 1) smode = MPG123_MONO; if(channels == 1) smode = MPG123_MONO;
if(channels == 2) smode = MPG123_STEREO; if(channels == 2) smode = MPG123_STEREO;
output_pause(ao); out123_stop(ao);
/* Remember: This takes param.pitch into account. */ /* Remember: This takes param.pitch into account. */
audio_capabilities(ao, fr); audio_capabilities(ao, fr);
if(!(mpg123_format_support(fr, rate, format) & smode)) 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!"); error("Reached a hardware limit there with pitch!");
param.pitch = old_pitch; param.pitch = old_pitch;
audio_capabilities(ao, fr); audio_capabilities(ao, fr);
ret = 0;
} }
ao->format = format; return out123_start(ao, pitch_rate(rate), channels, format);
ao->channels = channels;
ao->rate = pitch_rate(rate);
reset_output(ao);
output_unpause(ao);
return ret;
} }

View File

@@ -1,7 +1,9 @@
/* /*
audio: audio output interface 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp initially written by Michael Hipp
*/ */
@@ -16,86 +18,17 @@
#include "compat.h" #include "compat.h"
#include "mpg123.h" #include "mpg123.h"
#include "module.h" #include "out123.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;
};
#define pitch_rate(rate) (param.pitch == 0 ? (rate) : (long) ((param.pitch+1.0)*(rate))) #define pitch_rate(rate) (param.pitch == 0 ? (rate) : (long) ((param.pitch+1.0)*(rate)))
/* ------ Declarations from "audio.c" ------ */ int audio_enc_name2code(const char* name);
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);
const char* audio_encoding_name(const int encoding, const int longer); 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); 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. Twiddle audio output rate to yield speedup/down (pitch) effect.
The actually achieved pitch value is stored in param.pitch. The actually achieved pitch value is stored in param.pitch.

View File

@@ -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 */

View File

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

View File

@@ -1,18 +1,19 @@
/* /*
common: misc stuff... audio flush, status display... 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp initially written by Michael Hipp
*/ */
#include "mpg123app.h" #include "mpg123app.h"
#include "out123.h"
#include <sys/stat.h> #include <sys/stat.h>
#include "common.h" #include "common.h"
#include "debug.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 *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 *smodes[5] = { "stereo", "joint-stereo", "dual-channel", "mono", "invalid" };
static const char *layers[4] = { "Unknown" , "I", "II", "III" }; 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 */ { -1,-1,-1,-1 }, /* Unknown */
}; };
/* concurring to print_rheader... here for control_generic */ /* 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)>"; 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) 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, &timesep);
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; off_t rno, no;
double basevol, realvol; double basevol, realvol;
char *icy; 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 WIN32
#ifndef GENERIC #ifndef GENERIC
/* Only generate new stat line when stderr is ready... don't overfill... */ /* Only generate new stat line when stderr is ready... don't overfill... */
@@ -151,27 +182,30 @@ void print_stat(mpg123_handle *fr, long offset, long buffsize)
} }
#endif #endif
#endif #endif
if( MPG123_OK == mpg123_position(fr, offset, buffsize, &no, &rno, tim, tim+1) if( MPG123_OK == mpg123_position(fr, offset, buffsize, &no, &rno, tim, tim+1)
&& MPG123_OK == mpg123_getvolume(fr, &basevol, &realvol, NULL) ) && MPG123_OK == mpg123_getvolume(fr, &basevol, &realvol, NULL) )
{ {
int ti; int ti;
/* Deal with overly long times. */ /* Deal with overly long times. */
unsigned long times[2][3]; unsigned long times[3][3];
char timesep[2]; char timesep[3];
char sign[2] = {' ', ' '}; char sign[3] = {' ', ' ', ' '};
for(ti=0; ti<2; ++ti) tim[2] = (double)(buffsize/framesize)/rate;
for(ti=0; ti<3; ++ti)
{ {
if(tim[ti] < 0.){ sign[ti] = '-'; tim[ti] = -tim[ti]; } if(tim[ti] < 0.){ sign[ti] = '-'; tim[ti] = -tim[ti]; }
settle_time(tim[ti], times[ti], &timesep[ti]); settle_time(tim[ti], times[ti], &timesep[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, (off_p)no, (off_p)rno,
sign[0], sign[0],
times[0][0], times[0][1], timesep[0], times[0][2], times[0][0], times[0][1], timesep[0], times[0][2],
sign[1], sign[1],
times[1][0], times[1][1], timesep[1], times[1][2], times[1][0], times[1][1], timesep[1], times[1][2],
rva_name[param.rva], roundui(basevol*100), roundui(realvol*100) ); 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? */ /* Check for changed tags here too? */
if( mpg123_meta_check(fr) & MPG123_NEW_ICY && MPG123_OK == mpg123_icy(fr, &icy) ) if( mpg123_meta_check(fr) & MPG123_NEW_ICY && MPG123_OK == mpg123_icy(fr, &icy) )

View File

@@ -13,7 +13,7 @@
void print_header(mpg123_handle *); void print_header(mpg123_handle *);
void print_header_compact(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(); void clear_stat();
/* for control_generic */ /* for control_generic */
extern const char* remote_header_help; extern const char* remote_header_help;

13
src/compat.c Normal file
View 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"

View File

@@ -15,7 +15,13 @@
#define MPG123_COMPAT_H #define MPG123_COMPAT_H
#include "config.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 #ifdef HAVE_STDLIB_H
/* realloc, size_t */ /* realloc, size_t */
@@ -63,6 +69,9 @@
#ifdef HAVE_STRING_H #ifdef HAVE_STRING_H
#include <string.h> #include <string.h>
#endif #endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#ifdef OS2 #ifdef OS2
#include <float.h> #include <float.h>
@@ -133,6 +142,7 @@ typedef long ssize_p;
* @return file descriptor (>=0) or error code. * @return file descriptor (>=0) or error code.
*/ */
int compat_open(const char *filename, int flags); 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. * 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. * @return 0 if the file was successfully closed. A return value of -1 indicates an error.
*/ */
int compat_close(int infd); 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. */ /* 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); int win32_utf8_wide(const char *const mbptr, wchar_t **wptr, size_t *buflen);
#endif #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. */ /* That one comes from Tellie on OS/2, needed in resolver. */
#ifdef __KLIBC__ #ifdef __KLIBC__
typedef int socklen_t; typedef int socklen_t;
@@ -186,4 +203,8 @@ typedef int socklen_t;
#include "true.h" #include "true.h"
#if (!defined(WIN32) || defined (__CYGWIN__)) && defined(HAVE_SIGNAL_H)
void (*catchsignal(int signum, void(*handler)()))();
#endif
#endif #endif

244
src/compat/compat_impl.h Normal file
View 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

View File

@@ -8,6 +8,7 @@
*/ */
#include "mpg123app.h" #include "mpg123app.h"
#include "out123.h"
#include <stdarg.h> #include <stdarg.h>
#include <ctype.h> #include <ctype.h>
#if !defined (WIN32) || defined (__CYGWIN__) #if !defined (WIN32) || defined (__CYGWIN__)
@@ -18,14 +19,12 @@
#include <string.h> #include <string.h>
#include "common.h" #include "common.h"
#include "buffer.h"
#include "genre.h" #include "genre.h"
#include "playlist.h" #include "playlist.h"
#define MODE_STOPPED 0 #define MODE_STOPPED 0
#define MODE_PLAYING 1 #define MODE_PLAYING 1
#define MODE_PAUSED 2 #define MODE_PAUSED 2
extern int buffer_pid;
extern audio_output_t *ao; extern audio_output_t *ao;
#ifdef FIFO #ifdef FIFO
@@ -100,7 +99,7 @@ void generic_sendstat (mpg123_handle *fr)
{ {
off_t current_frame, frames_left; off_t current_frame, frames_left;
double current_seconds, seconds_left; double current_seconds, seconds_left;
if(!mpg123_position(fr, 0, xfermem_get_usedspace(buffermem), &current_frame, &frames_left, &current_seconds, &seconds_left)) if(!mpg123_position(fr, 0, out123_buffered(ao), &current_frame, &frames_left, &current_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); 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) static void generic_load(mpg123_handle *fr, char *arg, int state)
{ {
if(param.usebuffer) out123_drop(ao);
{
buffer_resync();
if(mode == MODE_PAUSED && state != MODE_PAUSED) buffer_start();
}
if(mode != MODE_STOPPED) if(mode != MODE_STOPPED)
{ {
close_track(); close_track();
@@ -359,7 +354,7 @@ int control_generic (mpg123_handle *fr)
{ {
mode = MODE_PAUSED; mode = MODE_PAUSED;
/* Hm, buffer should be stopped already, shouldn't it? */ /* Hm, buffer should be stopped already, shouldn't it? */
if(param.usebuffer) buffer_stop(); if(param.usebuffer) out123_pause(ao);
generic_sendmsg("P 1"); generic_sendmsg("P 1");
} }
else else
@@ -469,11 +464,11 @@ int control_generic (mpg123_handle *fr)
{ {
if (mode == MODE_PLAYING) { if (mode == MODE_PLAYING) {
mode = MODE_PAUSED; mode = MODE_PAUSED;
if(param.usebuffer) buffer_stop(); out123_pause(ao);
generic_sendmsg("P 1"); generic_sendmsg("P 1");
} else { } else {
mode = MODE_PLAYING; mode = MODE_PLAYING;
if(param.usebuffer) buffer_start(); out123_continue(ao);
generic_sendmsg("P 2"); generic_sendmsg("P 2");
} }
} else generic_sendmsg("P 0"); } else generic_sendmsg("P 0");
@@ -483,11 +478,9 @@ int control_generic (mpg123_handle *fr)
/* STOP */ /* STOP */
if (!strcasecmp(comstr, "S") || !strcasecmp(comstr, "STOP")) { if (!strcasecmp(comstr, "S") || !strcasecmp(comstr, "STOP")) {
if (mode != MODE_STOPPED) { if (mode != MODE_STOPPED) {
if(param.usebuffer) /* Do we want to drop here? */
{ out123_drop(ao);
buffer_stop(); out123_stop(ao);
buffer_resync();
}
close_track(); close_track();
mode = MODE_STOPPED; mode = MODE_STOPPED;
generic_sendmsg("P 0"); generic_sendmsg("P 0");
@@ -694,7 +687,7 @@ int control_generic (mpg123_handle *fr)
generic_sendmsg("E Error while seeking: %s", mpg123_strerror(fr)); generic_sendmsg("E Error while seeking: %s", mpg123_strerror(fr));
mpg123_seek(fr, 0, SEEK_SET); mpg123_seek(fr, 0, SEEK_SET);
} }
if(param.usebuffer) buffer_resync(); out123_drop(ao);
newpos = mpg123_tell(fr); newpos = mpg123_tell(fr);
if(newpos <= oldpos) mpg123_meta_free(fr); if(newpos <= oldpos) mpg123_meta_free(fr);
@@ -728,7 +721,7 @@ int control_generic (mpg123_handle *fr)
generic_sendmsg("E Error while seeking"); generic_sendmsg("E Error while seeking");
mpg123_seek_frame(fr, 0, SEEK_SET); mpg123_seek_frame(fr, 0, SEEK_SET);
} }
if(param.usebuffer) buffer_resync(); out123_drop(ao);
if(framenum <= oldpos) mpg123_meta_free(fr); if(framenum <= oldpos) mpg123_meta_free(fr);
generic_sendmsg("J %d", framenum); generic_sendmsg("J %d", framenum);

View File

@@ -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
View 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

View File

@@ -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. I'll sort it out properly sometime.
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 I smell symbol conflicts, anyway. Actually wondering why it
see COPYING and AUTHORS files in distribution or http://mpg123.org worked so far.
initially written by Thomas Orgis, Windows Unicode stuff by JonY.
*/ */
#include "config.h" #include "config.h"
#include "compat.h" #include "intsym.h"
#define NO_CATCHSIGNAL
#ifdef _MSC_VER #include "compat/compat_impl.h"
#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

View File

@@ -1,119 +1,3 @@
/* /* Hack to allow building the same code with and without libtool. */
dither: Generate shaped noise for dithering #include "intsym.h"
#include "dither_impl.h"
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);
}

119
src/libmpg123/dither_impl.h Normal file
View 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);
}

View File

@@ -1,11 +1,13 @@
/* /*
icy: Puny code to pretend for a serious ICY data structure. 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis initially written by Thomas Orgis
*/ */
#include "intsym.h"
#include "icy.h" #include "icy.h"
void init_icy(struct icy_meta *icy) void init_icy(struct icy_meta *icy)

View File

@@ -26,6 +26,8 @@
* Convert from ICY encoding (windows-1252 codepage) to UTF-8 * Convert from ICY encoding (windows-1252 codepage) to UTF-8
*/ */
#include "config.h"
#include "intsym.h"
/* Includes string and stdlib headers... */ /* Includes string and stdlib headers... */
#include "compat.h" #include "compat.h"

View File

@@ -1,11 +1,13 @@
/* /*
index: frame index data structure and functions 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis initially written by Thomas Orgis
*/ */
#include "intsym.h"
#include "index.h" #include "index.h"
#include "debug.h" #include "debug.h"

View File

@@ -1,14 +1,18 @@
#ifndef MPG123_INTMAP_H #ifndef MPG123_INTSYM_H
#define MPG123_INTMAP_H #define MPG123_INTSYM_H
/* Mapping of internal mpg123 symbols to something that is less likely to conflict in case of static linking. */ /* Mapping of internal mpg123 symbols to something that is less likely to conflict in case of static linking. */
#define COS9 INT123_COS9 #define COS9 INT123_COS9
#define tfcos36 INT123_tfcos36 #define tfcos36 INT123_tfcos36
#define pnts INT123_pnts #define pnts INT123_pnts
#define safe_realloc INT123_safe_realloc #define safe_realloc INT123_safe_realloc
#define compat_open INT123_compat_open #define compat_open INT123_compat_open
#define compat_fopen INT123_compat_fopen
#define compat_close INT123_compat_close #define compat_close INT123_compat_close
#define compat_fclose INT123_compat_fclose
#define win32_wide_utf8 INT123_win32_wide_utf8 #define win32_wide_utf8 INT123_win32_wide_utf8
#define win32_utf8_wide INT123_win32_utf8_wide #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 ntom_set_ntom INT123_ntom_set_ntom
#define synth_1to1 INT123_synth_1to1 #define synth_1to1 INT123_synth_1to1
#define synth_1to1_dither INT123_synth_1to1_dither #define synth_1to1_dither INT123_synth_1to1_dither
@@ -29,6 +33,8 @@
#define synth_1to1_arm INT123_synth_1to1_arm #define synth_1to1_arm INT123_synth_1to1_arm
#define synth_1to1_neon INT123_synth_1to1_neon #define synth_1to1_neon INT123_synth_1to1_neon
#define synth_1to1_stereo_neon INT123_synth_1to1_stereo_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 absynth_1to1_i486 INT123_absynth_1to1_i486
#define synth_1to1_mono INT123_synth_1to1_mono #define synth_1to1_mono INT123_synth_1to1_mono
#define synth_1to1_m2s INT123_synth_1to1_m2s #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_stereo_altivec INT123_synth_1to1_real_stereo_altivec
#define synth_1to1_real_neon INT123_synth_1to1_real_neon #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_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_mono INT123_synth_1to1_real_mono
#define synth_1to1_real_m2s INT123_synth_1to1_real_m2s #define synth_1to1_real_m2s INT123_synth_1to1_real_m2s
#define synth_2to1_real INT123_synth_2to1_real #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_stereo_altivec INT123_synth_1to1_s32_stereo_altivec
#define synth_1to1_s32_neon INT123_synth_1to1_s32_neon #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_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_mono INT123_synth_1to1_s32_mono
#define synth_1to1_s32_m2s INT123_synth_1to1_s32_m2s #define synth_1to1_s32_m2s INT123_synth_1to1_s32_m2s
#define synth_2to1_s32 INT123_synth_2to1_s32 #define synth_2to1_s32 INT123_synth_2to1_s32
@@ -120,8 +130,8 @@
#define dct36 INT123_dct36 #define dct36 INT123_dct36
#define dct36_3dnow INT123_dct36_3dnow #define dct36_3dnow INT123_dct36_3dnow
#define dct36_3dnowext INT123_dct36_3dnowext #define dct36_3dnowext INT123_dct36_3dnowext
#define dct36_sse INT123_dct36_sse
#define dct36_x86_64 INT123_dct36_x86_64 #define dct36_x86_64 INT123_dct36_x86_64
#define dct36_sse INT123_dct36_sse
#define dct36_avx INT123_dct36_avx #define dct36_avx INT123_dct36_avx
#define dct36_neon INT123_dct36_neon #define dct36_neon INT123_dct36_neon
#define dct36_neon64 INT123_dct36_neon64 #define dct36_neon64 INT123_dct36_neon64
@@ -198,8 +208,11 @@
#define double_to_long_rounded INT123_double_to_long_rounded #define double_to_long_rounded INT123_double_to_long_rounded
#define scale_rounded INT123_scale_rounded #define scale_rounded INT123_scale_rounded
#define decode_update INT123_decode_update #define decode_update INT123_decode_update
#define decoder_synth_bytes INT123_decoder_synth_bytes
#define samples_to_bytes INT123_samples_to_bytes #define samples_to_bytes INT123_samples_to_bytes
#define bytes_to_samples INT123_bytes_to_samples #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 frame_cpu_opt INT123_frame_cpu_opt
#define set_synth_functions INT123_set_synth_functions #define set_synth_functions INT123_set_synth_functions
#define dectype INT123_dectype #define dectype INT123_dectype
@@ -216,6 +229,10 @@
#define compute_bpf INT123_compute_bpf #define compute_bpf INT123_compute_bpf
#define time_to_frame INT123_time_to_frame #define time_to_frame INT123_time_to_frame
#define get_songlen INT123_get_songlen #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 INT123_open_stream
#define open_stream_handle INT123_open_stream_handle #define open_stream_handle INT123_open_stream_handle
#define open_feed INT123_open_feed #define open_feed INT123_open_feed
@@ -223,20 +240,21 @@
#define feed_forget INT123_feed_forget #define feed_forget INT123_feed_forget
#define feed_set_pos INT123_feed_set_pos #define feed_set_pos INT123_feed_set_pos
#define open_bad INT123_open_bad #define open_bad INT123_open_bad
#define check_neon INT123_check_neon
#define dct64_3dnow INT123_dct64_3dnow #define dct64_3dnow INT123_dct64_3dnow
#define dct64_3dnowext INT123_dct64_3dnowext #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_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_sse INT123_dct64_sse
#define dct64_real_sse INT123_dct64_real_sse #define dct64_real_sse INT123_dct64_real_sse
#define dct64_x86_64 INT123_dct64_x86_64 #define dct64_x86_64 INT123_dct64_x86_64
#define dct64_real_x86_64 INT123_dct64_real_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 do_equalizer_3dnow INT123_do_equalizer_3dnow
#define synth_1to1_3dnow_asm INT123_synth_1to1_3dnow_asm #define synth_1to1_3dnow_asm INT123_synth_1to1_3dnow_asm
#define synth_1to1_arm_asm INT123_synth_1to1_arm_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 INT123_synth_1to1_i586_asm
#define synth_1to1_i586_asm_dither INT123_synth_1to1_i586_asm_dither #define synth_1to1_i586_asm_dither INT123_synth_1to1_i586_asm_dither
#define synth_1to1_MMX INT123_synth_1to1_MMX #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_sse_accurate_asm INT123_synth_1to1_sse_accurate_asm
#define synth_1to1_real_sse_asm INT123_synth_1to1_real_sse_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_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_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_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 #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_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_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_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 costab_mmxsse INT123_costab_mmxsse
#define make_decode_tables_mmx_asm INT123_make_decode_tables_mmx_asm #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 #endif

View 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
View 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
View 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
View 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"

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

View File

@@ -5,15 +5,16 @@
see COPYING and AUTHORS files in distribution or http://mpg123.org see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Nicholas J Humfrey initially written by Nicholas J Humfrey
*/ */
#include "config.h"
#include "out123_intsym.h"
#include "compat.h"
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h>
#include <errno.h> #include <errno.h>
#include <ctype.h> #include <ctype.h>
#include <ltdl.h> #include <ltdl.h>
#include "mpg123app.h" #include "module.h"
#include "debug.h" #include "debug.h"
#ifndef HAVE_LTDL #ifndef HAVE_LTDL
@@ -32,8 +33,8 @@ static const char* modulesearch[] =
,"plugins" ,"plugins"
}; };
static char *get_the_cwd(); /* further down... */ static char *get_the_cwd(int verbose); /* further down... */
static char *get_module_dir() 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. */ /* Either PKGLIBDIR is accessible right away or we search for some possible plugin dirs relative to binary path. */
DIR* dir = NULL; DIR* dir = NULL;
@@ -42,14 +43,17 @@ static char *get_module_dir()
/* Compiled-in default module dir or environment variable MPG123_MODDIR. */ /* Compiled-in default module dir or environment variable MPG123_MODDIR. */
defaultdir = getenv("MPG123_MODDIR"); defaultdir = getenv("MPG123_MODDIR");
if(defaultdir == NULL) if(defaultdir == NULL)
defaultdir=PKGLIBDIR; defaultdir=PKGLIBDIR;
else if(verbose > 1)
fprintf(stderr, "Trying module directory from environment: %s\n", defaultdir);
dir = opendir(defaultdir); dir = opendir(defaultdir);
if(dir != NULL) if(dir != NULL)
{ {
size_t l = strlen(defaultdir); 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); moddir = malloc(l+1);
if(moddir != NULL) if(moddir != NULL)
{ {
@@ -65,19 +69,21 @@ static char *get_module_dir()
{ {
const char *testpath = modulesearch[i]; const char *testpath = modulesearch[i];
size_t l; size_t l;
if(binpath != NULL) l = strlen(binpath) + strlen(testpath) + 1; fprintf(stderr, "TODO: module search relative to binary path\n");
else l = strlen(testpath); /* if(binpath != NULL) l = strlen(binpath) + strlen(testpath) + 1;
else */ l = strlen(testpath);
moddir = malloc(l+1); moddir = malloc(l+1);
if(moddir != NULL) 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); snprintf(moddir, l+1, "%s", testpath);
else /* else
snprintf(moddir, l+1, "%s/%s", binpath, testpath); snprintf(moddir, l+1, "%s/%s", binpath, testpath); */
moddir[l] = 0; 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); dir = opendir(moddir);
if(dir != NULL) 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; return moddir;
} }
/* Open a module in current directory. */ /* 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; lt_dlhandle handle = NULL;
mpg123_module_t *module = NULL; mpg123_module_t *module = NULL;
@@ -106,7 +113,8 @@ mpg123_module_t* open_module_here(const char* type, const char* name)
/* Initialize libltdl */ /* Initialize libltdl */
if(lt_dlinit()) if(lt_dlinit())
{ {
error("Failed to initialise libltdl"); if(verbose > -1)
error("Failed to initialise libltdl");
return NULL; return NULL;
} }
@@ -115,20 +123,22 @@ 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_len = 2 + strlen(type) + 1 + strlen(name) + strlen(MODULE_FILE_SUFFIX) + 1;
module_path = malloc( module_path_len ); module_path = malloc( module_path_len );
if (module_path == NULL) { if (module_path == NULL) {
error1( "Failed to allocate memory for module name: %s", strerror(errno) ); if(verbose > -1)
error1( "Failed to allocate memory for module name: %s", strerror(errno) );
return NULL; return NULL;
} }
snprintf( module_path, module_path_len, "./%s_%s%s", type, name, MODULE_FILE_SUFFIX ); snprintf( module_path, module_path_len, "./%s_%s%s", type, name, MODULE_FILE_SUFFIX );
/* Display the path of the module created */ /* 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 */ /* Open the module */
handle = lt_dlopen( module_path ); handle = lt_dlopen( module_path );
free( module_path ); free( module_path );
if (handle==NULL) { if (handle==NULL) {
error2( "Failed to open module %s: %s", name, lt_dlerror() ); 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"); fprintf(stderr, "Note: This could be because of braindead path in the .la file...\n");
return NULL; return NULL;
} }
@@ -139,7 +149,8 @@ mpg123_module_t* open_module_here(const char* type, const char* name)
strlen( MODULE_SYMBOL_SUFFIX ) + 1; strlen( MODULE_SYMBOL_SUFFIX ) + 1;
module_symbol = malloc(module_symbol_len); module_symbol = malloc(module_symbol_len);
if (module_symbol == NULL) { if (module_symbol == NULL) {
error1( "Failed to allocate memory for module symbol: %s", strerror(errno) ); if(verbose > -1)
error1( "Failed to allocate memory for module symbol: %s", strerror(errno) );
return NULL; return NULL;
} }
snprintf( module_symbol, module_symbol_len, "%s%s%s", MODULE_SYMBOL_PREFIX, type, MODULE_SYMBOL_SUFFIX ); snprintf( module_symbol, module_symbol_len, "%s%s%s", MODULE_SYMBOL_PREFIX, type, MODULE_SYMBOL_SUFFIX );
@@ -168,26 +179,31 @@ mpg123_module_t* open_module_here(const char* type, const char* name)
/* Open a module, including directory search. */ /* 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; mpg123_module_t *module = NULL;
char *workdir = NULL; char *workdir = NULL;
char *moddir = NULL; char *moddir = NULL;
workdir = get_the_cwd(); workdir = get_the_cwd(verbose);
moddir = get_module_dir(); moddir = get_module_dir(verbose);
if(workdir == NULL || moddir == NULL) if(workdir == NULL || moddir == NULL)
{ {
error("Failure getting workdir or moddir! (Perhaps set MPG123_MODDIR?)"); if(verbose > -1)
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"); {
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) free(workdir); if(workdir != NULL) free(workdir);
if(moddir != NULL) free(moddir); if(moddir != NULL) free(moddir);
return NULL; return NULL;
} }
if(chdir(moddir) == 0) module = open_module_here(type, name); if(chdir(moddir) == 0)
else error2("Failed to enter module directory %s: %s", moddir, strerror(errno)); module = open_module_here(type, name, verbose);
else if(verbose > -1)
error2("Failed to enter module directory %s: %s", moddir, strerror(errno));
chdir(workdir); chdir(workdir);
free(moddir); free(moddir);
@@ -195,17 +211,18 @@ mpg123_module_t* open_module(const char* type, const char* name)
return module; return module;
} }
void close_module( mpg123_module_t* module ) void close_module( mpg123_module_t* module, int verbose )
{ {
lt_dlhandle handle = module->handle; lt_dlhandle handle = module->handle;
int err = lt_dlclose( 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 #define PATH_STEP 50
static char *get_the_cwd() static char *get_the_cwd(int verbose)
{ {
size_t bs = PATH_STEP; size_t bs = PATH_STEP;
char *buf = malloc(bs); char *buf = malloc(bs);
@@ -215,7 +232,8 @@ static char *get_the_cwd()
char *buf2; char *buf2;
if(errno != ERANGE) if(errno != ERANGE)
{ {
error1("getcwd returned unexpected error: %s", strerror(errno)); if(verbose > -1)
error1("getcwd returned unexpected error: %s", strerror(errno));
free(buf); free(buf);
return NULL; return NULL;
} }
@@ -228,85 +246,125 @@ static char *get_the_cwd()
return buf; return buf;
} }
void list_modules() int list_modules(const char *type, char ***names, char ***descr, int verbose)
{ {
DIR* dir = NULL; DIR* dir = NULL;
struct dirent *dp = NULL; struct dirent *dp = NULL;
char *moddir = NULL; char *moddir = NULL;
int count = 0;
moddir = get_module_dir(); *names = NULL;
*descr = NULL;
moddir = get_module_dir(verbose);
if(moddir == NULL) if(moddir == NULL)
{ {
error("Failure getting module directory! (Perhaps set MPG123_MODDIR?)"); if(verbose > -1)
exit(-1); /* TODO: change this to return a value instead of exit()! */ error("Failure getting module directory! (Perhaps set MPG123_MODDIR?)");
return -1;
} }
/* Open the module directory */ /* Open the module directory */
dir = opendir(moddir); dir = moddir != NULL ? opendir(moddir) : NULL;
if (dir==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); free(moddir);
exit(-1); return -1;
} }
if(chdir(moddir) != 0) if(chdir(moddir) != 0)
{ {
error2("Failed to enter module directory (%s): %s\n", PKGLIBDIR, strerror(errno)); if(verbose > -1)
closedir( dir ); error2("Failed to enter module directory (%s): %s\n"
, PKGLIBDIR, strerror(errno));
closedir(dir);
free(moddir); free(moddir);
exit(-1); return -1;
} }
/* Display the program title */
/* print_title(stderr); */
/* 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)
{
char *module_name = NULL;
char *module_type = NULL;
char *uscore_pos = NULL;
mpg123_module_t *module = NULL;
/* Extract the module type */
module_type = strdup( dp->d_name );
uscore_pos = strchr( module_type, '_' );
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';
/* Open the module */
module = open_module_here(module_type, module_name);
if (module) {
printf("%-15s%s %s\n", module->name, module_type, module->description );
/* Close the module again */
close_module( module );
}
free( module_name );
free( module_type );
}
}
}
closedir( dir );
free(moddir); free(moddir);
exit(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 */
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) )
{
free(module_type);
continue;
}
*uscore_pos = '\0';
/* Extract the short name of the module */
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 */
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, verbose);
}
free(module_name);
free(module_type);
}
closedir(dir);
return count;
} }

View File

@@ -1,20 +1,20 @@
/* /*
module: module loading and listing interface 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Nicholas J. Humphfrey
*/ */
#ifndef _MPG123_MODULE_H_ #ifndef _MPG123_MODULE_H_
#define _MPG123_MODULE_H_ #define _MPG123_MODULE_H_
/* Pulled in by mpg123app.h! */
#ifdef HAVE_LTDL #ifdef HAVE_LTDL
#include <ltdl.h> #include <ltdl.h>
#endif #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 */ /* The full structure is delared in audio.h */
struct audio_output_struct; struct audio_output_struct;
@@ -41,8 +41,8 @@ typedef struct mpg123_module_struct {
/* ------ Declarations from "module.c" ------ */ /* ------ Declarations from "module.c" ------ */
mpg123_module_t* open_module( const char* type, const char* name ); mpg123_module_t* open_module(const char* type, const char* name, int verbose);
void close_module( mpg123_module_t* module ); void close_module(mpg123_module_t* module, int verbose);
void list_modules(); int list_modules(const char *type, char ***names, char ***descr, int verbose);
#endif #endif

View 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

View File

@@ -19,7 +19,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/param.h> #include <sys/param.h>
#include "mpg123app.h" #include "out123_int.h"
#include "debug.h" #include "debug.h"
/* use AUDIO_BSIZE to set the msec for audio buffering in Ultimedia library /* use AUDIO_BSIZE to set the msec for audio buffering in Ultimedia library

View File

@@ -30,7 +30,7 @@
/**************************************************************************/ /**************************************************************************/
#include "mpg123app.h" #include "out123_int.h"
#include <fcntl.h> #include <fcntl.h>

View File

@@ -7,7 +7,7 @@
initially written by Clemens Ladisch <clemens@ladisch.de> initially written by Clemens Ladisch <clemens@ladisch.de>
*/ */
#include "mpg123app.h" #include "out123_int.h"
#include "audio.h" #include "audio.h"
#include "module.h" #include "module.h"
#include <errno.h> #include <errno.h>
@@ -16,6 +16,7 @@
#define ALSA_PCM_NEW_HW_PARAMS_API #define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API #define ALSA_PCM_NEW_SW_PARAMS_API
#include <alloca.h> /* GCC complains about missing declaration of alloca. */
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "debug.h" #include "debug.h"

View File

@@ -8,7 +8,7 @@
*/ */
#include "mpg123app.h" #include "out123_int.h"
#include <artsc.h> #include <artsc.h>
#include "debug.h" #include "debug.h"

View File

@@ -9,7 +9,7 @@
*/ */
#include "mpg123app.h" #include "out123_int.h"
#include <CoreServices/CoreServices.h> #include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h> #include <AudioUnit/AudioUnit.h>

View File

@@ -5,7 +5,7 @@
see COPYING and AUTHORS files in distribution or http://mpg123.org see COPYING and AUTHORS files in distribution or http://mpg123.org
*/ */
#include "mpg123app.h" #include "out123_int.h"
#include "debug.h" #include "debug.h"
static int open_dummy(audio_output_t *ao) static int open_dummy(audio_output_t *ao)

View File

@@ -8,7 +8,7 @@
/* First the common header, including config.h /* First the common header, including config.h
...this is important for stuff like _FILE_OFFSET_BITS */ ...this is important for stuff like _FILE_OFFSET_BITS */
#include "mpg123app.h" #include "out123_int.h"
#include <esd.h> #include <esd.h>
#include <errno.h> #include <errno.h>

View File

@@ -6,7 +6,7 @@
initially written by Michael Hipp initially written by Michael Hipp
*/ */
#include "mpg123app.h" #include "out123_int.h"
#include <fcntl.h> #include <fcntl.h>
#include <sys/audio.h> #include <sys/audio.h>
#include "debug.h" #include "debug.h"

View File

@@ -12,7 +12,7 @@
#include <jack/ringbuffer.h> #include <jack/ringbuffer.h>
#include <sys/errno.h> #include <sys/errno.h>
#include "mpg123app.h" #include "out123_int.h"
#include "debug.h" #include "debug.h"
#define MAX_CHANNELS (2) #define MAX_CHANNELS (2)

View File

@@ -8,7 +8,7 @@
/* derived from LINUX, VOXWARE and SUN for MiNT Audio Device by Petr Stehlik */ /* derived from LINUX, VOXWARE and SUN for MiNT Audio Device by Petr Stehlik */
#include <fcntl.h> #include <fcntl.h>
#include "mpg123app.h" #include "out123_int.h"
#include <ioctl.h> #include <ioctl.h>
#include <audios.h> #include <audios.h>
#include "debug.h" #include "debug.h"

View File

@@ -6,7 +6,7 @@
initially written by Martin Denn initially written by Martin Denn
*/ */
#include "mpg123app.h" #include "out123_int.h"
#include <fcntl.h> #include <fcntl.h>
#include <audio/audiolib.h> #include <audio/audiolib.h>
#include <audio/soundlib.h> #include <audio/soundlib.h>

View File

@@ -7,7 +7,7 @@
*/ */
#include "mpg123app.h" #include "out123_int.h"
#ifdef OPENAL_SUBDIR_OPENAL #ifdef OPENAL_SUBDIR_OPENAL
#include <OpenAL/al.h> #include <OpenAL/al.h>

View File

@@ -17,7 +17,7 @@
/* I guess this #ifndef could be removed now... */ /* I guess this #ifndef could be removed now... */
#ifndef MPG123_INCLUDED #ifndef MPG123_INCLUDED
#include "mpg123app.h" #include "out123_int.h"
#endif #endif
#include "debug.h" #include "debug.h"

View File

@@ -9,7 +9,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <fcntl.h> #include <fcntl.h>
#include "mpg123app.h" #include "out123_int.h"
#ifdef HAVE_LINUX_SOUNDCARD_H #ifdef HAVE_LINUX_SOUNDCARD_H
#include <linux/soundcard.h> #include <linux/soundcard.h>

View File

@@ -11,7 +11,7 @@
#include <portaudio.h> #include <portaudio.h>
#include "audio.h" #include "audio.h"
#include "mpg123app.h" #include "out123_int.h"
#ifdef WIN32 #ifdef WIN32
#include <windows.h> #include <windows.h>

View File

@@ -14,7 +14,7 @@
#include <pulse/error.h> #include <pulse/error.h>
#include "config.h" #include "config.h"
#include "mpg123app.h" #include "out123_int.h"
#include "audio.h" #include "audio.h"
#include "module.h" #include "module.h"
#include "debug.h" #include "debug.h"

View File

@@ -7,7 +7,7 @@
written by Mike Gorchak <mike.gorchak.qnx@gmail.com> written by Mike Gorchak <mike.gorchak.qnx@gmail.com>
*/ */
#include "mpg123app.h" #include "out123_int.h"
#include "audio.h" #include "audio.h"
#include "module.h" #include "module.h"
#include <errno.h> #include <errno.h>

View File

@@ -11,7 +11,7 @@
#include <SDL.h> #include <SDL.h>
#include "audio.h" #include "audio.h"
#include "mpg123app.h" #include "out123_int.h"
#ifdef WIN32 #ifdef WIN32
#include <windows.h> #include <windows.h>
#endif #endif

View File

@@ -10,7 +10,7 @@
#include <dmedia/audio.h> #include <dmedia/audio.h>
#include "mpg123app.h" #include "out123_int.h"
#include "errno.h" #include "errno.h"
#include "debug.h" #include "debug.h"

View File

@@ -17,7 +17,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "mpg123app.h" #include "out123_int.h"
#include <sndio.h> #include <sndio.h>

View File

@@ -6,7 +6,7 @@
initially written by Michael Hipp initially written by Michael Hipp
*/ */
#include "mpg123app.h" #include "out123_int.h"
#ifdef HAVE_SYS_IOCTL_H #ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h> #include <sys/ioctl.h>

View File

@@ -7,7 +7,7 @@
initially written by Jarno Lehtinen <lehtinen@sci.fi> initially written by Jarno Lehtinen <lehtinen@sci.fi>
*/ */
#include "mpg123app.h" #include "out123_int.h"
#include "audio.h" #include "audio.h"
#include "module.h" #include "module.h"
#include <errno.h> #include <errno.h>

View File

@@ -9,7 +9,7 @@
Closing buffer playback fixed by David Wohlferd <limegreensocks (*) yahoo dod com> Closing buffer playback fixed by David Wohlferd <limegreensocks (*) yahoo dod com>
*/ */
#include "mpg123app.h" #include "out123_int.h"
#include <windows.h> #include <windows.h>
#include "debug.h" #include "debug.h"

View File

@@ -9,7 +9,7 @@
#define COBJMACROS 1 #define COBJMACROS 1
#define _WIN32_WINNT 0x601 #define _WIN32_WINNT 0x601
#include <initguid.h> #include <initguid.h>
#include "mpg123app.h" #include "out123_int.h"
#include <audioclient.h> #include <audioclient.h>
#include <mmdeviceapi.h> #include <mmdeviceapi.h>
#include <avrt.h> #include <avrt.h>

469
src/libout123/out123.h.in Normal file
View 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
View 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

View 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
View 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
View File

@@ -0,0 +1,33 @@
/*
wav.c: write wav/au/cdr files (and headerless raw)
copyright ?-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially extracted of out123_int.h, formerly audio.h, by Thomas Orgis
*/
#ifndef _MPG123_WAV_H_
#define _MPG123_WAV_H_
/* Could get away without any header, as only pointers declared. */
#include "out123.h"
/* Interfaces from wav.c, variants of file writing, to be combined into
fake modules by the main library code. */
int au_open(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

View File

@@ -1,12 +1,12 @@
/* /*
wavhead.h: wav file header, to be included twice for integer and float wavs 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Samuel Audet initially written by Samuel Audet
*/ */
struct struct RIFF_STRUCT_NAME
{ {
byte riffheader[4]; byte riffheader[4];
byte WAVElen[4]; /* should this include riffheader or not? */ byte WAVElen[4]; /* should this include riffheader or not? */
@@ -42,7 +42,7 @@ struct
/* from here you insert your PCM data */ /* from here you insert your PCM data */
} data; } data;
} WAVE; } WAVE;
} RIFF_NAME = } const RIFF_NAME =
{ {
{ 'R','I','F','F' } , { 'R','I','F','F' } ,
{ sizeof(RIFF_NAME.WAVE),0,0,0 } , { sizeof(RIFF_NAME.WAVE),0,0,0 } ,

View File

@@ -1,7 +1,7 @@
/* /*
xfermem: unidirectional fast pipe 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Oliver Fromme initially written by Oliver Fromme
old timestamp: Sun Apr 6 02:26:26 MET DST 1997 old timestamp: Sun Apr 6 02:26:26 MET DST 1997
@@ -9,10 +9,10 @@
See xfermem.h for documentation/description. See xfermem.h for documentation/description.
*/ */
#include "config.h"
#ifndef NOXFERMEM #include "out123_intsym.h"
#include "compat.h"
#include "mpg123app.h" #include "xfermem.h"
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <sys/uio.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); exit (1);
} }
(*xf)->freeindex = (*xf)->readindex = 0; (*xf)->freeindex = (*xf)->readindex = 0;
(*xf)->wakeme[0] = (*xf)->wakeme[1] = FALSE; (*xf)->data = ((char *) *xf) + sizeof(txfermem) + msize;
(*xf)->data = ((byte *) *xf) + sizeof(txfermem) + msize; (*xf)->metadata = ((char *) *xf) + sizeof(txfermem);
(*xf)->metadata = ((byte *) *xf) + sizeof(txfermem);
(*xf)->size = bufsize; (*xf)->size = bufsize;
(*xf)->metasize = msize + skipbuf; (*xf)->metasize = msize + skipbuf;
(*xf)->justwait = 0;
} }
void xfermem_done (txfermem *xf) void xfermem_done (txfermem *xf)
@@ -92,7 +90,10 @@ void xfermem_done (txfermem *xf)
if(!xf) if(!xf)
return; return;
#ifdef HAVE_MMAP #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 #else
if (shmdt((void *) xf) == -1) { if (shmdt((void *) xf) == -1) {
perror ("shmdt()"); perror ("shmdt()");
@@ -105,12 +106,14 @@ void xfermem_init_writer (txfermem *xf)
{ {
if(xf) if(xf)
close (xf->fd[XF_READER]); close (xf->fd[XF_READER]);
debug1("xfermem writer fd=%i", xf->fd[XF_WRITER]);
} }
void xfermem_init_reader (txfermem *xf) void xfermem_init_reader (txfermem *xf)
{ {
if(xf) if(xf)
close (xf->fd[XF_WRITER]); close (xf->fd[XF_WRITER]);
debug1("xfermem reader fd=%i", xf->fd[XF_READER]);
} }
size_t xfermem_get_freespace (txfermem *xf) size_t xfermem_get_freespace (txfermem *xf)
@@ -145,10 +148,10 @@ size_t xfermem_get_usedspace (txfermem *xf)
return (xf->size - (readindex - freeindex)); 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; fd_set selfds;
byte cmd; int ret;
for (;;) { for (;;) {
struct timeval selto = {0, 0}; struct timeval selto = {0, 0};
@@ -156,10 +159,11 @@ int xfermem_getcmd (int fd, int block)
FD_ZERO (&selfds); FD_ZERO (&selfds);
FD_SET (fd, &selfds); FD_SET (fd, &selfds);
#ifdef HPUX #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 #else
switch (select(FD_SETSIZE, &selfds, NULL, NULL, block ? NULL : &selto)) { switch (select(FD_SETSIZE, &selfds, NULL, NULL, block ? NULL : &selto))
#endif #endif
{
case 0: case 0:
if (!block) if (!block)
return (0); return (0);
@@ -170,17 +174,16 @@ int xfermem_getcmd (int fd, int block)
return (-2); return (-2);
case 1: case 1:
if (FD_ISSET(fd, &selfds)) if (FD_ISSET(fd, &selfds))
switch (read(fd, &cmd, 1)) { switch((ret=read(fd, cmds, count)))
{
case 0: /* EOF */ case 0: /* EOF */
return (-1); return (-1);
case -1: case -1:
if (errno == EINTR) if (errno == EINTR)
continue; continue;
return (-3); return (-3);
case 1: default:
return (cmd); return ret;
default: /* ?!? */
return (-4);
} }
else /* ?!? */ else /* ?!? */
return (-5); 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) int xfermem_putcmd (int fd, byte cmd)
{ {
for (;;) { for (;;) {
switch (write(fd, &cmd, 1)) { switch (write(fd, &cmd, 1)) {
case 1: case 1:
debug2("xfermem_putcmd(%i, %i) = 1", fd, cmd);
return (1); return (1);
case -1: case -1:
if (errno != EINTR) if (errno != EINTR)
{
debug3("xfermem_putcmd(%i, %i) = -1 (%s)"
, fd, cmd, strerror(errno));
return (-1); 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; int result;
xf->wakeme[readwrite] = TRUE; xfermem_putcmd(myfd, XF_CMD_PING);
if (xf->wakeme[1 - readwrite])
xfermem_putcmd (myfd, XF_CMD_WAKEUP);
result = xfermem_getcmd(myfd, TRUE); result = xfermem_getcmd(myfd, TRUE);
xf->wakeme[readwrite] = FALSE; /* Only a pong to my ping is the expected good answer.
return ((result <= 0) ? -1 : result); 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. */ /* Return: 0 on success, -1 on communication error, > 0 for
int xfermem_sigblock(int readwrite, txfermem *xf, int pid, int signal) 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]; if(buffer == NULL || bytes < 1) return 0;
int result;
xf->wakeme[readwrite] = TRUE; /* You weren't so braindead not allocating enough space at all, right? */
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? */
while (xfermem_get_freespace(xf) < bytes) while (xfermem_get_freespace(xf) < bytes)
{ {
int cmd = xfermem_block(XF_WRITER, xf); int cmd = xfermem_writer_block(xf);
if (cmd == XF_CMD_TERMINATE || cmd < 0) if(cmd) /* Non-successful wait. */
{ return cmd;
error("failed to wait for free space");
return TRUE; /* Failure. */
}
} }
/* Now we have enough space. copy the memory, possibly with the wrap. */ /* Now we have enough space. copy the memory, possibly with the wrap. */
if(xf->size - xf->freeindex >= bytes) if(xf->size - xf->freeindex >= bytes)
@@ -258,64 +293,13 @@ int xfermem_write(txfermem *xf, byte *buffer, size_t bytes)
{ /* two blocks */ { /* two blocks */
size_t endblock = xf->size - xf->freeindex; size_t endblock = xf->size - xf->freeindex;
memcpy(xf->data+xf->freeindex, buffer, endblock); 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. */ /* Advance the free space pointer, including the wrap. */
xf->freeindex = (xf->freeindex + bytes) % xf->size; xf->freeindex = (xf->freeindex + bytes) % xf->size;
/* Wake up the buffer process if necessary. */ /* Always notify the buffer process. */
debug("write waking"); debug("write waking");
if(xf->wakeme[XF_READER]) return xfermem_putcmd(xf->fd[XF_WRITER], XF_CMD_DATA) < 0
return xfermem_putcmd(xf->fd[XF_WRITER], XF_CMD_WAKEUP_INFO) < 0 ? TRUE : FALSE; ? -1
: 0;
return FALSE;
} }
#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
View 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

View File

@@ -2,4 +2,4 @@
# wrapper script to set MPG123_MODDIR automatically so that one can run mpg123 # wrapper script to set MPG123_MODDIR automatically so that one can run mpg123
# from the source directory, even if modules are enabled # from the source directory, even if modules are enabled
src=$(dirname $0) src=$(dirname $0)
MPG123_MODDIR="$src/output/.libs" exec "$src/mpg123" "$@" MPG123_MODDIR="$src/libout123/modules/.libs" exec "$src/mpg123" "$@"

View File

@@ -9,6 +9,7 @@
#define ME "main" #define ME "main"
#include "mpg123app.h" #include "mpg123app.h"
#include "mpg123.h" #include "mpg123.h"
#include "out123.h"
#include "local.h" #include "local.h"
#ifdef HAVE_SYS_WAIT_H #ifdef HAVE_SYS_WAIT_H
@@ -34,7 +35,6 @@
#include "common.h" #include "common.h"
#include "sysutil.h" #include "sysutil.h"
#include "getlopt.h" #include "getlopt.h"
#include "buffer.h"
#include "term.h" #include "term.h"
#include "playlist.h" #include "playlist.h"
#include "httpget.h" #include "httpget.h"
@@ -56,7 +56,6 @@ struct parameter param = {
FALSE , /* shuffle */ FALSE , /* shuffle */
FALSE , /* remote */ FALSE , /* remote */
FALSE , /* remote to stderr */ FALSE , /* remote to stderr */
DECODE_AUDIO , /* write samples to audio device */
FALSE , /* silent operation */ FALSE , /* silent operation */
FALSE , /* xterm title on/off */ FALSE , /* xterm title on/off */
0 , /* second level buffer size */ 0 , /* second level buffer size */
@@ -77,7 +76,6 @@ struct parameter param = {
#ifdef HAVE_WINDOWS_H #ifdef HAVE_WINDOWS_H
0, /* win32 process priority */ 0, /* win32 process priority */
#endif #endif
NULL, /* wav,cdr,au Filename */
0, /* default is to play all titles in playlist */ 0, /* default is to play all titles in playlist */
NULL, /* no playlist per default */ NULL, /* no playlist per default */
0 /* condensed id3 per default */ 0 /* condensed id3 per default */
@@ -110,7 +108,7 @@ struct parameter param = {
,0 /* force_utf8 */ ,0 /* force_utf8 */
,INDEX_SIZE ,INDEX_SIZE
,NULL /* force_encoding */ ,NULL /* force_encoding */
,1. /* preload */ ,0.2 /* preload */
,-1 /* preframes */ ,-1 /* preframes */
,-1 /* gain */ ,-1 /* gain */
,NULL /* stream dump file */ ,NULL /* stream dump file */
@@ -121,20 +119,16 @@ mpg123_handle *mh = NULL;
off_t framenum; off_t framenum;
off_t frames_left; off_t frames_left;
audio_output_t *ao = NULL; audio_output_t *ao = NULL;
txfermem *buffermem = NULL;
char *prgName = NULL; char *prgName = NULL;
/* ThOr: pointers are not TRUE or FALSE */ /* ThOr: pointers are not TRUE or FALSE */
char *equalfile = NULL; char *equalfile = NULL;
struct httpdata htd; struct httpdata htd;
int fresh = TRUE; 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. */ 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; size_t bufferblock = 0;
static int intflag = FALSE; int intflag = FALSE;
static int skip_tracks = 0; static int skip_tracks = 0;
int OutputDescriptor; int OutputDescriptor;
@@ -182,6 +176,34 @@ void prev_track(void)
next_track(); 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 /* Directory jumping based on comparing the directory part of the playlist
URLs. */ URLs. */
int cmp_dir(const char* patha, const char* pathb) int cmp_dir(const char* patha, const char* pathb)
@@ -227,11 +249,14 @@ void safe_exit(int code)
char *dummy, *dammy; char *dummy, *dammy;
dump_close(); dump_close();
controlled_drain();
#ifdef HAVE_TERMIOS #ifdef HAVE_TERMIOS
if(param.term_ctrl) if(param.term_ctrl)
term_restore(); term_restore();
#endif #endif
if(have_output) exit_output(ao, intflag); if(intflag)
out123_drop(ao);
out123_del(ao);
if(mh != NULL) mpg123_delete(mh); if(mh != NULL) mpg123_delete(mh);
@@ -251,7 +276,22 @@ void safe_exit(int code)
exit(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 ) static void set_output_module( char *arg )
{ {
unsigned int i; unsigned int i;
@@ -320,67 +360,44 @@ static void set_quiet (char *arg)
static void set_out_wav(char *arg) static void set_out_wav(char *arg)
{ {
param.outmode = DECODE_WAV; param.output_module = "wav";
param.filename = arg; param.output_device = arg;
} }
void set_out_cdr(char *arg) void set_out_cdr(char *arg)
{ {
param.outmode = DECODE_CDR; param.output_module = "cdr";
param.filename = arg; param.output_device = arg;
} }
void set_out_au(char *arg) void set_out_au(char *arg)
{ {
param.outmode = DECODE_AU; param.output_module = "au";
param.filename = arg; 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) static void set_out_file(char *arg)
{ {
param.outmode=DECODE_FILE; param.output_module = "raw";
#ifdef WIN32 param.output_device = arg;
#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);
}
} }
static void set_out_stdout(char *arg) static void set_out_stdout(char *arg)
{ {
param.outmode=DECODE_FILE; param.output_module = "raw";
param.remote_err=TRUE; param.output_device = NULL;
aux_out = stderr;
OutputDescriptor=STDOUT_FILENO;
#ifdef WIN32
_setmode(STDOUT_FILENO, _O_BINARY);
#endif
} }
static void set_out_stdout1(char *arg) static void set_out_stdout1(char *arg)
{ {
param.outmode=DECODE_AUDIOFILE; param.output_module = "raw";
param.remote_err=TRUE; param.output_device = NULL;
aux_out = stderr;
OutputDescriptor=STDOUT_FILENO;
#ifdef WIN32
_setmode(STDOUT_FILENO, _O_BINARY);
#endif
} }
#if !defined (HAVE_SCHED_SETSCHEDULER) && !defined (HAVE_WINDOWS_H) #if !defined (HAVE_SCHED_SETSCHEDULER) && !defined (HAVE_WINDOWS_H)
@@ -407,6 +424,38 @@ static void set_appflag(char *arg)
{ {
param.appflags |= appflag; 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) /* static void unset_appflag(char *arg)
{ {
param.appflags &= ~appflag; param.appflags &= ~appflag;
@@ -427,9 +476,9 @@ topt opts[] = {
{'k', "skip", GLO_ARG | GLO_LONG, 0, &param.start_frame, 0}, {'k', "skip", GLO_ARG | GLO_LONG, 0, &param.start_frame, 0},
{'2', "2to1", GLO_INT, 0, &param.down_sample, 1}, {'2', "2to1", GLO_INT, 0, &param.down_sample, 1},
{'4', "4to1", GLO_INT, 0, &param.down_sample, 2}, {'4', "4to1", GLO_INT, 0, &param.down_sample, 2},
{'t', "test", GLO_INT, 0, &param.outmode, DECODE_TEST}, {'t', "test", GLO_INT, set_out_test, NULL, 0},
{'s', "stdout", GLO_INT, set_out_stdout, &param.outmode, DECODE_FILE}, {'s', "stdout", GLO_INT, set_out_stdout, NULL, 0},
{'S', "STDOUT", GLO_INT, set_out_stdout1, &param.outmode,DECODE_AUDIOFILE}, {'S', "STDOUT", GLO_INT, set_out_stdout1, NULL, 0},
{'O', "outfile", GLO_ARG | GLO_CHAR, set_out_file, NULL, 0}, {'O', "outfile", GLO_ARG | GLO_CHAR, set_out_file, NULL, 0},
{'c', "check", GLO_INT, 0, &param.checkrange, TRUE}, {'c', "check", GLO_INT, 0, &param.checkrange, TRUE},
{'v', "verbose", 0, set_verbose, 0, 0}, {'v', "verbose", 0, set_verbose, 0, 0},
@@ -454,7 +503,7 @@ topt opts[] = {
{0, "speaker", 0, set_output_s, 0,0}, {0, "speaker", 0, set_output_s, 0,0},
{0, "lineout", 0, set_output_l, 0,0}, {0, "lineout", 0, set_output_l, 0,0},
{'o', "output", GLO_ARG | GLO_CHAR, set_output, 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, &param.output_device, 0}, {'a', "audiodevice", GLO_ARG | GLO_CHAR, 0, &param.output_device, 0},
{'f', "scale", GLO_ARG | GLO_LONG, 0, &param.outscale, 0}, {'f', "scale", GLO_ARG | GLO_LONG, 0, &param.outscale, 0},
{'n', "frames", GLO_ARG | GLO_LONG, 0, &param.frame_number, 0}, {'n', "frames", GLO_ARG | GLO_LONG, 0, &param.frame_number, 0},
@@ -546,54 +595,6 @@ topt opts[] = {
{0, 0, 0, 0, 0, 0} {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) static int open_track_fd (void)
{ {
/* Let reader handle invalid filept */ /* Let reader handle invalid filept */
@@ -717,8 +718,10 @@ int play_frame(void)
{ {
fresh = FALSE; fresh = FALSE;
} }
/* Normal flushing of data, includes buffer decoding. */ /* Interrupt here doesn't necessarily interrupt out123_play().
if(flush_output(ao, audio, bytes) < (int)bytes && !intflag) 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!"); error("Deep trouble! Cannot flush to my output anymore!");
safe_exit(133); 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); if(param.verbose > 2) fprintf(stderr, "\nNote: New output format %liHz %ich, format %i\n", rate, channels, format);
new_header = 1; new_header = 1;
reset_audio(rate, channels, format); check_fatal_output(out123_start(ao, format, channels, rate));
} }
} }
if(new_header && !param.quiet) if(new_header && !param.quiet)
@@ -762,24 +765,6 @@ int play_frame(void)
return 1; 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. */ /* Return TRUE if we should continue (second interrupt happens quickly), skipping tracks, or FALSE if we should die. */
#if !defined(WIN32) && !defined(GENERIC) #if !defined(WIN32) && !defined(GENERIC)
int skip_or_die(struct timeval *start_time) 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. /* Init audio as early as possible.
If there is the buffer process to be spawned, it shouldn't carry the mpg123_handle with it. */ 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. */ 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. */ /* 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. */ /* Prepare stream dumping, possibly replacing mpg123 reader. */
if(dump_open(mh) != 0) safe_exit(78); 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); load_equalizer(mh);
#ifdef HAVE_SETPRIORITY #ifdef HAVE_SETPRIORITY
@@ -1069,6 +1044,38 @@ int main(int sys_argc, char ** sys_argv)
win32_set_priority( param.realtime ? 3 : param.w32_priority); win32_set_priority( param.realtime ? 3 : param.w32_priority);
#endif #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(!param.remote) prepare_playlist(argc, argv);
#if !defined(WIN32) && !defined(GENERIC) #if !defined(WIN32) && !defined(GENERIC)
@@ -1103,15 +1110,14 @@ int main(int sys_argc, char ** sys_argv)
} }
if(param.delay > 0) if(param.delay > 0)
{ {
controlled_drain();
/* One should enable terminal control during that sleeping phase! */ /* 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); if(param.verbose > 2) fprintf(stderr, "Note: pausing %i seconds before next track.\n", param.delay);
output_pause(ao);
#ifdef WIN32 #ifdef WIN32
Sleep(param.delay*1000); Sleep(param.delay*1000);
#else #else
sleep(param.delay); sleep(param.delay);
#endif #endif
output_unpause(ao);
} }
if(!APPFLAG(MPG123APP_CONTINUE)) frames_left = param.frame_number; 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) if(!fresh && param.verbose)
{ {
#ifndef NOXFERMEM if(param.verbose > 1 || !(framenum & 0x7)) print_stat(mh,0,ao);
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
} }
#ifdef HAVE_TERMIOS #ifdef HAVE_TERMIOS
if(!param.term_ctrl) continue; if(!param.term_ctrl) continue;
@@ -1245,8 +1246,9 @@ int main(int sys_argc, char ** sys_argv)
#endif #endif
} }
if(!param.smooth && param.usebuffer) buffer_drain(); if(!param.smooth && !intflag)
if(param.verbose) print_stat(mh,0,xfermem_get_usedspace(buffermem)); controlled_drain();
if(param.verbose) print_stat(mh,0,ao);
if(!param.quiet) if(!param.quiet)
{ {
@@ -1269,21 +1271,13 @@ int main(int sys_argc, char ** sys_argv)
intflag = FALSE; intflag = FALSE;
#ifndef NOXFERMEM if(!param.smooth)
if(!param.smooth && param.usebuffer) buffer_resync(); out123_drop(ao);
#endif
} }
if(end_of_files) break; if(end_of_files) break;
} /* end of loop over input files */ } /* end of loop over input files */
/* Ensure we played everything. */
if(param.smooth && param.usebuffer)
{
buffer_drain();
buffer_resync();
}
if(APPFLAG(MPG123APP_CONTINUE)) if(APPFLAG(MPG123APP_CONTINUE))
{ {
continue_msg("CONTINUE"); continue_msg("CONTINUE");

View File

@@ -1,7 +1,7 @@
/* /*
mpg123: main code of the program (not of the decoder...) 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp initially written by Michael Hipp
@@ -17,7 +17,6 @@
#include "compat.h" #include "compat.h"
/* import DLL symbols on windows */ /* import DLL symbols on windows */
#include "xfermem.h"
#include "httpget.h" #include "httpget.h"
#if WIN32 #if WIN32
#include "win32_support.h" #include "win32_support.h"
@@ -52,13 +51,12 @@ struct parameter
int shuffle; /* shuffle/random play */ int shuffle; /* shuffle/random play */
int remote; /* remote operation */ int remote; /* remote operation */
int remote_err; /* remote operation to stderr */ int remote_err; /* remote operation to stderr */
int outmode; /* where to out the decoded sampels */
int quiet; /* shut up! */ int quiet; /* shut up! */
int xterm_title; /* Change xterm title to song names? */ int xterm_title; /* Change xterm title to song names? */
long usebuffer; /* second level buffer size */ long usebuffer; /* second level buffer size */
int verbose; /* verbose level */ int verbose; /* verbose level */
char* output_module; /* audio output module to use */ const char* output_module; /* audio output module to use */
char* output_device; /* audio output device to use */ const char* output_device; /* audio output device to use */
int output_flags; /* legacy output destination for AIX/HP/Sun */ int output_flags; /* legacy output destination for AIX/HP/Sun */
#ifdef HAVE_TERMIOS #ifdef HAVE_TERMIOS
int term_ctrl; int term_ctrl;
@@ -73,7 +71,6 @@ struct parameter
#ifdef HAVE_WINDOWS_H #ifdef HAVE_WINDOWS_H
int w32_priority; int w32_priority;
#endif #endif
char *filename;
long listentry; /* possibility to choose playback of one entry in playlist (0: off, > 0 : select, < 0; just show list*/ long listentry; /* possibility to choose playback of one entry in playlist (0: off, > 0 : select, < 0; just show list*/
char* listname; /* name of playlist */ char* listname; /* name of playlist */
int long_id3; int long_id3;
@@ -127,14 +124,6 @@ extern char *equalfile;
extern off_t framenum; extern off_t framenum;
extern struct httpdata htd; 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; extern int OutputDescriptor;
#ifdef VARMODESUPPORT #ifdef VARMODESUPPORT
@@ -148,15 +137,6 @@ extern int play_frame(void);
extern int control_generic(mpg123_handle *fr); 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; extern struct parameter param;
/* avoid the SIGINT in terminal control */ /* avoid the SIGINT in terminal control */

View File

@@ -2,4 +2,4 @@
# wrapper script to set MPG123_MODDIR automatically so that one can run mpg123 # wrapper script to set MPG123_MODDIR automatically so that one can run mpg123
# from the source directory, even if modules are enabled # from the source directory, even if modules are enabled
src=$(dirname $0) src=$(dirname $0)
MPG123_MODDIR="$src/output/.libs" exec "$src/out123" "$@" MPG123_MODDIR="$src/libout123/modules/.libs" exec "$src/out123" "$@"

View File

@@ -1,7 +1,7 @@
/* /*
out123: simple program to stream data to an audio output device 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis (extracted from mpg123.c) initially written by Thomas Orgis (extracted from mpg123.c)
@@ -26,6 +26,7 @@
#define ME "main" #define ME "main"
#include "mpg123app.h" #include "mpg123app.h"
#include "out123.h"
#ifdef HAVE_SYS_WAIT_H #ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h> #include <sys/wait.h>
@@ -49,7 +50,6 @@
#include "sysutil.h" #include "sysutil.h"
#include "getlopt.h" #include "getlopt.h"
#include "buffer.h"
#include "debug.h" #include "debug.h"
@@ -72,7 +72,6 @@ struct parameter param = {
FALSE , /* shuffle */ FALSE , /* shuffle */
FALSE , /* remote */ FALSE , /* remote */
FALSE , /* remote to stderr */ FALSE , /* remote to stderr */
DECODE_AUDIO , /* write samples to audio device */
FALSE , /* silent operation */ FALSE , /* silent operation */
FALSE , /* xterm title on/off */ FALSE , /* xterm title on/off */
0 , /* second level buffer size */ 0 , /* second level buffer size */
@@ -93,7 +92,6 @@ struct parameter param = {
#ifdef HAVE_WINDOWS_H #ifdef HAVE_WINDOWS_H
0, /* win32 process priority */ 0, /* win32 process priority */
#endif #endif
NULL, /* wav,cdr,au Filename */
0, /* default is to play all titles in playlist */ 0, /* default is to play all titles in playlist */
NULL, /* no playlist per default */ NULL, /* no playlist per default */
0 /* condensed id3 per default */ 0 /* condensed id3 per default */
@@ -126,7 +124,7 @@ struct parameter param = {
,0 /* force_utf8 */ ,0 /* force_utf8 */
,INDEX_SIZE ,INDEX_SIZE
,"s16" /* force_encoding */ ,"s16" /* force_encoding */
,1. /* preload */ ,0.2 /* preload */
,-1 /* preframes */ ,-1 /* preframes */
,-1 /* gain */ ,-1 /* gain */
,NULL /* stream dump file */ ,NULL /* stream dump file */
@@ -134,18 +132,13 @@ struct parameter param = {
}; };
audio_output_t *ao = NULL; audio_output_t *ao = NULL;
txfermem *buffermem = NULL;
char *prgName = NULL; char *prgName = NULL;
/* ThOr: pointers are not TRUE or FALSE */ /* ThOr: pointers are not TRUE or FALSE */
char *equalfile = NULL; char *equalfile = NULL;
int fresh = TRUE; 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; size_t bufferblock = 4096;
static int intflag = FALSE;
int OutputDescriptor; int OutputDescriptor;
char *fullprogname = NULL; /* Copy of argv[0]. */ char *fullprogname = NULL; /* Copy of argv[0]. */
@@ -160,8 +153,7 @@ void safe_exit(int code)
{ {
char *dummy, *dammy; char *dummy, *dammy;
if(have_output) exit_output(ao, intflag); out123_del(ao);
#ifdef WANT_WIN32_UNICODE #ifdef WANT_WIN32_UNICODE
win32_cmdline_free(argc, argv); /* This handles the premature argv == NULL, too. */ win32_cmdline_free(argc, argv); /* This handles the premature argv == NULL, too. */
#endif #endif
@@ -172,7 +164,22 @@ void safe_exit(int code)
exit(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 ) static void set_output_module( char *arg )
{ {
unsigned int i; unsigned int i;
@@ -241,65 +248,44 @@ static void set_quiet (char *arg)
static void set_out_wav(char *arg) static void set_out_wav(char *arg)
{ {
param.outmode = DECODE_WAV; param.output_module = "wav";
param.filename = arg; param.output_device = arg;
} }
void set_out_cdr(char *arg) void set_out_cdr(char *arg)
{ {
param.outmode = DECODE_CDR; param.output_module = "cdr";
param.filename = arg; param.output_device = arg;
} }
void set_out_au(char *arg) void set_out_au(char *arg)
{ {
param.outmode = DECODE_AU; param.output_module = "au";
param.filename = arg; 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) static void set_out_file(char *arg)
{ {
param.outmode=DECODE_FILE; param.output_module = "raw";
#ifdef WIN32 param.output_device = arg;
#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);
}
} }
static void set_out_stdout(char *arg) static void set_out_stdout(char *arg)
{ {
param.outmode=DECODE_FILE; param.output_module = "raw";
param.remote_err=TRUE; param.output_device = NULL;
OutputDescriptor=STDOUT_FILENO;
#ifdef WIN32
_setmode(STDOUT_FILENO, _O_BINARY);
#endif
} }
static void set_out_stdout1(char *arg) static void set_out_stdout1(char *arg)
{ {
param.outmode=DECODE_AUDIOFILE; param.output_module = "raw";
param.remote_err=TRUE; param.output_device = NULL;
OutputDescriptor=STDOUT_FILENO;
#ifdef WIN32
_setmode(STDOUT_FILENO, _O_BINARY);
#endif
} }
#if !defined (HAVE_SCHED_SETSCHEDULER) && !defined (HAVE_WINDOWS_H) #if !defined (HAVE_SCHED_SETSCHEDULER) && !defined (HAVE_WINDOWS_H)
@@ -322,6 +308,32 @@ static void unset_frameflag(char *arg)
param.flags &= ~frameflag; 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 int appflag;*/ /* still ugly, but works */
/*static void set_appflag(char *arg) /*static void set_appflag(char *arg)
{ {
@@ -341,9 +353,9 @@ passed.
* GLO_NUM no longer exists. * GLO_NUM no longer exists.
*/ */
topt opts[] = { topt opts[] = {
{'t', "test", GLO_INT, 0, &param.outmode, DECODE_TEST}, {'t', "test", GLO_INT, set_out_test, NULL, 0},
{'s', "stdout", GLO_INT, set_out_stdout, &param.outmode, DECODE_FILE}, {'s', "stdout", GLO_INT, set_out_stdout, NULL, 0},
{'S', "STDOUT", GLO_INT, set_out_stdout1, &param.outmode,DECODE_AUDIOFILE}, {'S', "STDOUT", GLO_INT, set_out_stdout1, NULL, 0},
{'O', "outfile", GLO_ARG | GLO_CHAR, set_out_file, NULL, 0}, {'O', "outfile", GLO_ARG | GLO_CHAR, set_out_file, NULL, 0},
{'v', "verbose", 0, set_verbose, 0, 0}, {'v', "verbose", 0, set_verbose, 0, 0},
{'q', "quiet", 0, set_quiet, 0, 0}, {'q', "quiet", 0, set_quiet, 0, 0},
@@ -354,7 +366,7 @@ topt opts[] = {
{0, "speaker", 0, set_output_s, 0,0}, {0, "speaker", 0, set_output_s, 0,0},
{0, "lineout", 0, set_output_l, 0,0}, {0, "lineout", 0, set_output_l, 0,0},
{'o', "output", GLO_ARG | GLO_CHAR, set_output, 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, &param.output_device, 0}, {'a', "audiodevice", GLO_ARG | GLO_CHAR, 0, &param.output_device, 0},
#ifndef NOXFERMEM #ifndef NOXFERMEM
{'b', "buffer", GLO_ARG | GLO_LONG, 0, &param.usebuffer, 0}, {'b', "buffer", GLO_ARG | GLO_LONG, 0, &param.usebuffer, 0},
@@ -382,54 +394,6 @@ topt opts[] = {
{0, 0, 0, 0, 0, 0} {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 */ /* return 1 on success, 0 on failure */
int play_frame(void) int play_frame(void)
{ {
@@ -439,9 +403,13 @@ int play_frame(void)
if(got_samples) if(got_samples)
{ {
size_t got_bytes = pcmframe * 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); safe_exit(133);
} }
return 1; return 1;
@@ -449,24 +417,17 @@ int play_frame(void)
else return 0; 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. */ /* Hack: A hidden function in audio.c just for me. */
int audio_enc_name2code(const char* name); 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 main(int sys_argc, char ** sys_argv)
{ {
int result; int result;
@@ -523,6 +484,27 @@ int main(int sys_argc, char ** sys_argv)
usage(1); 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 #ifdef HAVE_SETPRIORITY
if(param.aggressive) { /* tst */ if(param.aggressive) { /* tst */
int mypid = getpid(); int mypid = getpid();
@@ -564,46 +546,26 @@ int main(int sys_argc, char ** sys_argv)
bufferblock = pcmblock*pcmframe; bufferblock = pcmblock*pcmframe;
audio = (unsigned char*) malloc(bufferblock); audio = (unsigned char*) malloc(bufferblock);
check_fatal_output(out123_set_buffer(ao, param.usebuffer*1024));
/* This needs bufferblock set! */ /* This needs bufferblock set! */
if(init_output(&ao) < 0) check_fatal_output(out123_open( ao
{ , param.output_module, param.output_device ));
error("Failed to initialize output, goodbye.");
return 99; /* It's safe here... nothing nasty happened yet. */
}
have_output = TRUE;
fprintf(stderr, "TODO: Check audio caps, add option to display 'em.\n"); fprintf(stderr, "TODO: Check audio caps, add option to display 'em.\n");
/* audio_capabilities(ao, mh); */ /* 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; input = stdin;
while(play_frame()) while(play_frame() && !intflag)
{ {
/* be happy */ /* be happy */
} }
/* Ensure we played everything. */ if(intflag) /* Make it quick! */
if(param.usebuffer)
{ {
buffer_drain(); if(!param.quiet)
buffer_resync(); fprintf(stderr, "Interrupted. Dropping the ball.\n");
out123_drop(ao);
} }
safe_exit(0); /* That closes output and restores terminal, too. */ safe_exit(0); /* That closes output and restores terminal, too. */

View File

@@ -12,27 +12,6 @@
#include "debug.h" #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 #if 0
/* removed the strndup for better portability */ /* removed the strndup for better portability */
/* /*

View File

@@ -1,7 +1,7 @@
/* /*
sysutil: generic utilities to interact with the OS (signals, paths) 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp (dissected/renamed by Thomas Orgis) initially written by Michael Hipp (dissected/renamed by Thomas Orgis)
*/ */
@@ -11,7 +11,6 @@
#include "mpg123app.h" #include "mpg123app.h"
void (*catchsignal(int signum, void(*handler)()))();
int split_dir_file(const char *path, char **dname, char **fname); int split_dir_file(const char *path, char **dname, char **fname);
/* Length of directory part in given path. */ /* Length of directory part in given path. */
size_t dir_length(const char *path); size_t dir_length(const char *path);

View File

@@ -1,7 +1,7 @@
/* /*
term: terminal control 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 see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp initially written by Michael Hipp
*/ */
@@ -13,14 +13,12 @@
#include <termios.h> #include <termios.h>
#include <ctype.h> #include <ctype.h>
#include "buffer.h"
#include "term.h" #include "term.h"
#include "common.h" #include "common.h"
#include "playlist.h" #include "playlist.h"
#include "metaprint.h" #include "metaprint.h"
#include "debug.h" #include "debug.h"
extern int buffer_pid;
extern audio_output_t *ao; extern audio_output_t *ao;
static int term_enable = 0; static int term_enable = 0;
@@ -178,34 +176,23 @@ void pause_uncycle(void)
off_t term_control(mpg123_handle *fr, audio_output_t *ao) off_t term_control(mpg123_handle *fr, audio_output_t *ao)
{ {
offset = 0; 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(!term_enable) return 0;
if(paused) if(paused)
{ {
/* pause_cycle counts the remaining frames _after_ this one, thus <0, not ==0 . */ /* pause_cycle counts the remaining frames _after_ this one, thus <0, not ==0 . */
if(--pause_cycle < 0) if(--pause_cycle < 0)
{
pause_recycle(fr); 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 do
{ {
off_t old_offset = offset;
term_handle_input(fr, ao, stopped|seeking); term_handle_input(fr, ao, stopped|seeking);
if((offset < 0) && (-offset > framenum)) offset = - framenum; if((offset < 0) && (-offset > framenum)) offset = - framenum;
if(param.verbose && offset != 0) if(param.verbose && offset != old_offset)
print_stat(fr,offset,0); print_stat(fr,offset,ao);
} while (stopped); } while (stopped);
/* Make the seeking experience with buffer less annoying. /* 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); debug1("seeked to %li", (long)offset);
else error1("seek failed: %s!", mpg123_strerror(fr)); else error1("seek failed: %s!", mpg123_strerror(fr));
/* Buffer resync already happened on un-stop? */ /* Buffer resync already happened on un-stop? */
/* if(param.usebuffer) buffer_resync();*/ /* if(param.usebuffer) audio_drop(ao);*/
} }
return 0; return 0;
} }
/* Stop playback while seeking if buffer is involved. */ /* 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) if(param.usebuffer && !stopped)
{ {
stopped = TRUE; 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); 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) static void term_handle_key(mpg123_handle *fr, audio_output_t *ao, char val)
{ {
debug1("term_handle_key: %c", val);
switch(tolower(val)) switch(tolower(val))
{ {
case MPG123_BACK_KEY: case MPG123_BACK_KEY:
if(!param.usebuffer) ao->flush(ao); out123_drop(ao);
else buffer_resync();
if(paused) pause_cycle=(int)(LOOP_CYCLES/mpg123_tpf(fr)); if(paused) pause_cycle=(int)(LOOP_CYCLES/mpg123_tpf(fr));
if(mpg123_seek_frame(fr, 0, SEEK_SET) < 0) 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; framenum=0;
break; break;
case MPG123_NEXT_KEY: case MPG123_NEXT_KEY:
if(!param.usebuffer) ao->flush(ao); out123_drop(ao);
else buffer_resync(); /* was: plain_buffer_resync */
next_track(); next_track();
break; break;
case MPG123_NEXT_DIR_KEY: case MPG123_NEXT_DIR_KEY:
if(!param.usebuffer) ao->flush(ao); out123_drop(ao);
else buffer_resync(); /* was: plain_buffer_resync */
next_dir(); next_dir();
break; break;
case MPG123_QUIT_KEY: case MPG123_QUIT_KEY:
@@ -292,75 +289,72 @@ static void term_handle_key(mpg123_handle *fr, audio_output_t *ao, char val)
if(stopped) if(stopped)
{ {
stopped = 0; stopped = 0;
if(param.usebuffer) out123_drop(ao);
{
buffer_resync();
buffer_start();
}
} }
set_intflag(); set_intflag();
offset = 0; offset = 0;
break; break;
case MPG123_PAUSE_KEY: case MPG123_PAUSE_KEY:
paused=1-paused; paused=1-paused;
if(paused) { if(paused)
{
/* Not really sure if that is what is wanted /* Not really sure if that is what is wanted
This jumps in audio output, but has direct reaction to pausing loop. */ 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); pause_recycle(fr);
} }
else
out123_param(ao, OUT123_PRELOAD, 0, param.preload);
if(stopped) if(stopped)
{ {
stopped=0; stopped=0;
if(param.usebuffer) buffer_start(); out123_continue(ao);
} }
fprintf(stderr, "%s", (paused) ? MPG123_PAUSED_STRING : MPG123_EMPTY_STRING); fprintf(stderr, "%s", (paused) ? MPG123_PAUSED_STRING : MPG123_EMPTY_STRING);
break; break;
case MPG123_STOP_KEY: case MPG123_STOP_KEY:
case ' ': case ' ':
/* when seeking while stopped and then resuming, I want to prevent the chirp from the past */ /* TODO: Verify/ensure that there is no "chirp from the past" when
if(!param.usebuffer) ao->flush(ao); seeking while stopped. */
stopped=1-stopped; stopped=1-stopped;
if(paused) { if(paused) {
paused=0; paused=0;
offset -= pause_cycle; offset -= pause_cycle;
} }
if(param.usebuffer) if(stopped)
out123_pause(ao);
else
{ {
if(stopped) buffer_stop(); if(offset) /* If position changed, old is outdated. */
else out123_drop(ao);
{ out123_continue(ao);
/* When we stopped buffer for seeking, we must resync. */
if(offset) buffer_resync();
buffer_start();
}
} }
fprintf(stderr, "%s", (stopped) ? MPG123_STOPPED_STRING : MPG123_EMPTY_STRING); fprintf(stderr, "%s", (stopped) ? MPG123_STOPPED_STRING : MPG123_EMPTY_STRING);
break; break;
case MPG123_FINE_REWIND_KEY: case MPG123_FINE_REWIND_KEY:
if(param.usebuffer) seekmode(); seekmode(fr, ao);
offset--; offset--;
break; break;
case MPG123_FINE_FORWARD_KEY: case MPG123_FINE_FORWARD_KEY:
seekmode(); seekmode(fr, ao);
offset++; offset++;
break; break;
case MPG123_REWIND_KEY: case MPG123_REWIND_KEY:
seekmode(); seekmode(fr, ao);
offset-=10; offset-=10;
break; break;
case MPG123_FORWARD_KEY: case MPG123_FORWARD_KEY:
seekmode(); seekmode(fr, ao);
offset+=10; offset+=10;
break; break;
case MPG123_FAST_REWIND_KEY: case MPG123_FAST_REWIND_KEY:
seekmode(); seekmode(fr, ao);
offset-=50; offset-=50;
break; break;
case MPG123_FAST_FORWARD_KEY: case MPG123_FAST_FORWARD_KEY:
seekmode(); seekmode(fr, ao);
offset+=50; offset+=50;
break; break;
case MPG123_VOL_UP_KEY: 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.); mpg123_volume_change(fr, 0.);
break; break;
case MPG123_PREV_KEY: case MPG123_PREV_KEY:
if(!param.usebuffer) ao->flush(ao); out123_drop(ao);
else buffer_resync(); /* was: plain_buffer_resync */
prev_track(); prev_track();
break; break;
case MPG123_PREV_DIR_KEY: case MPG123_PREV_DIR_KEY:
if(!param.usebuffer) ao->flush(ao); out123_drop(ao);
else buffer_resync(); /* was: plain_buffer_resync */
prev_dir(); prev_dir();
break; break;
case MPG123_PLAYLIST_KEY: 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"); fprintf(stderr, "\n");
break; break;
case MPG123_MPEG_KEY: 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"); fprintf(stderr, "\n");
print_header(fr); print_header(fr);
fprintf(stderr, "\n"); 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 = val == '0' ? 10 : val - '0';
--num; /* from 0 to 9 */ --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); 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; break;
case MPG123_BOOKMARK_KEY: case MPG123_BOOKMARK_KEY:

View File

@@ -3,6 +3,10 @@
#include "dither.h" #include "dither.h"
#include "debug.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" }; const char *typenames[] = { "white", "tpdf", "highpass_tpdf" };
enum mpg123_noise_type types[] = { mpg123_white_noise, mpg123_tpdf_noise, mpg123_highpass_tpdf_noise }; enum mpg123_noise_type types[] = { mpg123_white_noise, mpg123_tpdf_noise, mpg123_highpass_tpdf_noise };

476
src/wav.c
View File

@@ -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();
}

View 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