Built SDL2_image and _mixer static

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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