diff options
author | Anton Khirnov <anton@khirnov.net> | 2013-04-21 11:01:39 +0200 |
---|---|---|
committer | Anton Khirnov <anton@khirnov.net> | 2013-11-04 10:52:50 +0100 |
commit | 0ab207aae7becf128a4324a58a97f961f2f015eb (patch) | |
tree | 667341db6eb0d0e011e2ec895aafcbc161f59cd9 | |
parent | 550ace8556074a1450df6649a0dbb13ca0542999 (diff) |
Drop all decode plugins except for ffmpeg.
38 files changed, 10 insertions, 9105 deletions
diff --git a/Makefile.am b/Makefile.am index 332b51d9..8ba4ab4e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -95,12 +95,6 @@ mpd_headers = \ src/gcc.h \ src/decoder_list.h \ src/decoder_print.h \ - src/decoder/flac_compat.h \ - src/decoder/flac_metadata.h \ - src/decoder/flac_pcm.h \ - src/decoder/_flac_common.h \ - src/decoder/_ogg_common.h \ - src/decoder/pcm_decoder_plugin.h \ src/input_init.h \ src/input_plugin.h \ src/input_registry.h \ @@ -491,128 +485,18 @@ endif # decoder plugins libdecoder_plugins_a_SOURCES = \ - src/decoder/pcm_decoder_plugin.c \ - src/decoder/dsdiff_decoder_plugin.c \ - src/decoder/dsdiff_decoder_plugin.h \ - src/decoder/dsf_decoder_plugin.c \ - src/decoder/dsf_decoder_plugin.h \ - src/decoder/dsdlib.c \ - src/decoder/dsdlib.h \ src/decoder_buffer.c \ src/decoder_plugin.c \ src/decoder_list.c libdecoder_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \ - $(VORBIS_CFLAGS) $(TREMOR_CFLAGS) \ - $(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \ - $(SNDFILE_CFLAGS) \ - $(AUDIOFILE_CFLAGS) \ - $(LIBMIKMOD_CFLAGS) \ - $(GME_CFLAGS) \ - $(SIDPLAY_CFLAGS) \ - $(FLUIDSYNTH_CFLAGS) \ - $(WILDMIDI_CFLAGS) \ - $(WAVPACK_CFLAGS) \ - $(MAD_CFLAGS) \ - $(MPG123_CFLAGS) \ - $(FFMPEG_CFLAGS) \ - $(MPCDEC_CFLAGS) \ - $(FAAD_CFLAGS) + $(FFMPEG_CFLAGS) DECODER_LIBS = \ libdecoder_plugins.a \ - $(VORBIS_LIBS) $(TREMOR_LIBS) \ - $(FLAC_LIBS) \ - $(SNDFILE_LIBS) \ - $(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \ - $(GME_LIBS) \ - $(SIDPLAY_LIBS) \ - $(FLUIDSYNTH_LIBS) \ - $(WILDMIDI_LIBS) \ - $(WAVPACK_LIBS) \ - $(MAD_LIBS) \ - $(MPG123_LIBS) \ - $(MP4FF_LIBS) \ - $(FFMPEG_LIBS) \ - $(MPCDEC_LIBS) \ - $(FAAD_LIBS) + $(FFMPEG_LIBS) DECODER_SRC = -if HAVE_MAD -libdecoder_plugins_a_SOURCES += src/decoder/mad_decoder_plugin.c -endif - -if HAVE_MPG123 -libdecoder_plugins_a_SOURCES += src/decoder/mpg123_decoder_plugin.c -endif - -if HAVE_MPCDEC -libdecoder_plugins_a_SOURCES += src/decoder/mpcdec_decoder_plugin.c -endif - -if HAVE_WAVPACK -libdecoder_plugins_a_SOURCES += src/decoder/wavpack_decoder_plugin.c -endif - -if HAVE_FAAD -libdecoder_plugins_a_SOURCES += src/decoder/faad_decoder_plugin.c -endif - -if HAVE_MP4 -libdecoder_plugins_a_SOURCES += src/decoder/mp4ff_decoder_plugin.c -endif - -if HAVE_OGG_COMMON -libdecoder_plugins_a_SOURCES += src/decoder/_ogg_common.c -endif - -if HAVE_FLAC_COMMON -libdecoder_plugins_a_SOURCES += \ - src/decoder/flac_metadata.c \ - src/decoder/flac_pcm.c \ - src/decoder/_flac_common.c -endif - -if ENABLE_VORBIS_DECODER -libdecoder_plugins_a_SOURCES += \ - src/decoder/vorbis_comments.c \ - src/decoder/vorbis_comments.h \ - src/decoder/vorbis_decoder_plugin.c -endif - -if HAVE_FLAC -libdecoder_plugins_a_SOURCES += src/decoder/flac_decoder_plugin.c -endif - -if HAVE_AUDIOFILE -libdecoder_plugins_a_SOURCES += src/decoder/audiofile_decoder_plugin.c -endif - -if ENABLE_MIKMOD_DECODER -libdecoder_plugins_a_SOURCES += src/decoder/mikmod_decoder_plugin.c -endif - -if HAVE_MODPLUG -libmodplug_decoder_plugin_a_SOURCES = src/decoder/modplug_decoder_plugin.c -libmodplug_decoder_plugin_a_CFLAGS = $(src_mpd_CFLAGS) $(MODPLUG_CFLAGS) -libmodplug_decoder_plugin_a_CPPFLAGS = $(src_mpd_CPPFLAGS) -noinst_LIBRARIES += libmodplug_decoder_plugin.a -DECODER_LIBS += libmodplug_decoder_plugin.a $(MODPLUG_LIBS) -endif - -if ENABLE_SIDPLAY -libdecoder_plugins_a_SOURCES += src/decoder/sidplay_decoder_plugin.cxx -DECODER_SRC += src/dummy.cxx -endif - -if ENABLE_FLUIDSYNTH -libdecoder_plugins_a_SOURCES += src/decoder/fluidsynth_decoder_plugin.c -endif - -if ENABLE_WILDMIDI -libdecoder_plugins_a_SOURCES += src/decoder/wildmidi_decoder_plugin.c -endif - if HAVE_FFMPEG libdecoder_plugins_a_SOURCES += \ src/decoder/ffmpeg_metadata.c \ @@ -620,14 +504,6 @@ libdecoder_plugins_a_SOURCES += \ src/decoder/ffmpeg_decoder_plugin.c endif -if ENABLE_SNDFILE -libdecoder_plugins_a_SOURCES += src/decoder/sndfile_decoder_plugin.c -endif - -if HAVE_GME -libdecoder_plugins_a_SOURCES += src/decoder/gme_decoder_plugin.c -endif - # encoder plugins if ENABLE_ENCODER @@ -637,7 +513,6 @@ noinst_LIBRARIES += libencoder_plugins.a libencoder_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \ $(LAME_CFLAGS) \ $(TWOLAME_CFLAGS) \ - $(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \ $(VORBISENC_CFLAGS) ENCODER_LIBS = \ @@ -1072,12 +947,6 @@ test_dump_playlist_SOURCES = test/dump_playlist.c \ src/timer.c \ src/fd_util.c -if HAVE_FLAC -test_dump_playlist_SOURCES += \ - src/replay_gain_info.c \ - src/decoder/flac_metadata.c -endif - test_run_decoder_LDADD = \ $(DECODER_LIBS) \ libpcm.a \ diff --git a/configure.ac b/configure.ac index 0b6c0fc4..37009e76 100644 --- a/configure.ac +++ b/configure.ac @@ -161,11 +161,6 @@ AC_ARG_ENABLE(ao, [enable support for libao]),, enable_ao=auto) -AC_ARG_ENABLE(audiofile, - AS_HELP_STRING([--enable-audiofile], - [enable audiofile support (WAV and others)]),, - enable_audiofile=auto) - AC_ARG_ENABLE(bzip2, AS_HELP_STRING([--enable-bzip2], [enable bzip2 archive support (default: disabled)]),, @@ -210,21 +205,6 @@ AC_ARG_ENABLE(fifo, [disable support for writing audio to a FIFO (default: enable)]),, enable_fifo=yes) -AC_ARG_ENABLE(flac, - AS_HELP_STRING([--enable-flac], - [enable FLAC decoder]),, - enable_flac=auto) - -AC_ARG_ENABLE(fluidsynth, - AS_HELP_STRING([--enable-fluidsynth], - [enable MIDI support via fluidsynth (default: disable)]),, - enable_fluidsynth=no) - -AC_ARG_ENABLE(gme, - AS_HELP_STRING([--enable-gme], - [enable Blargg's game music emulator plugin]),, - enable_gme=auto) - AC_ARG_ENABLE(httpd-output, AS_HELP_STRING([--enable-httpd-output], [enables the HTTP server output]),, @@ -286,36 +266,11 @@ AC_ARG_ENABLE(lsr, [enable libsamplerate support]),, enable_lsr=auto) -AC_ARG_ENABLE(mad, - AS_HELP_STRING([--enable-mad], - [enable libmad mp3 decoder plugin]),, - enable_mad=auto) - -AC_ARG_ENABLE(mikmod, - AS_HELP_STRING([--enable-mikmod], - [enable the mikmod decoder (default: disable)]),, - enable_mikmod=no) - AC_ARG_ENABLE(mms, AS_HELP_STRING([--enable-mms], [enable the MMS protocol with libmms]),, [enable_mms=auto]) -AC_ARG_ENABLE(modplug, - AS_HELP_STRING([--enable-modplug], - [enable modplug decoder plugin]),, - enable_modplug=auto) - -AC_ARG_ENABLE(mpc, - AS_HELP_STRING([--disable-mpc], - [disable musepack (MPC) support (default: enable)]),, - enable_mpc=yes) - -AC_ARG_ENABLE(mpg123, - AS_HELP_STRING([--enable-mpg123], - [enable libmpg123 decoder plugin]),, - enable_mpg123=auto) - AC_ARG_ENABLE(mvp, AS_HELP_STRING([--enable-mvp], [enable support for Hauppauge Media MVP (default: disable)]),, @@ -346,22 +301,11 @@ AC_ARG_ENABLE(recorder-output, [enables the recorder file output plugin (default: disable)]),, [enable_recorder_output=auto]) -AC_ARG_ENABLE(sidplay, - AS_HELP_STRING([--enable-sidplay], - [enable C64 SID support via libsidplay2]),, - enable_sidplay=auto) - - AC_ARG_ENABLE(shout, AS_HELP_STRING([--enable-shout], [enables the shoutcast streaming output]),, [enable_shout=auto]) -AC_ARG_ENABLE(sndfile, - AS_HELP_STRING([--enable-sndfile], - [enable sndfile support]),, - enable_sndfile=auto) - AC_ARG_ENABLE(solaris_output, AS_HELP_STRING([--enable-solaris-output], [enables the Solaris /dev/audio output]),, @@ -387,11 +331,6 @@ AC_ARG_ENABLE(test, [build the test programs (default: disabled)]),, enable_test=no) -AC_ARG_WITH(tremor, - AS_HELP_STRING([--with-tremor=PFX], - [use Tremor (vorbisidec) integer Ogg Vorbis decoder (with optional prefix)]),, - with_tremor=no) - AC_ARG_ENABLE(twolame-encoder, AS_HELP_STRING([--enable-twolame-encoder], [enable the TwoLAME mp2 encoder]),, @@ -402,11 +341,6 @@ AC_ARG_ENABLE(un, [disable support for clients connecting via unix domain sockets (default: enable)]),, [enable_un=yes]) -AC_ARG_ENABLE(vorbis, - AS_HELP_STRING([--enable-vorbis], - [enable Ogg Vorbis decoder]),, - enable_vorbis=auto) - AC_ARG_ENABLE(vorbis-encoder, AS_HELP_STRING([--enable-vorbis-encoder], [enable the Ogg Vorbis encoder]),, @@ -417,21 +351,11 @@ AC_ARG_ENABLE(wave-encoder, [enable the PCM wave encoder]),, enable_wave_encoder=yes) -AC_ARG_ENABLE(wavpack, - AS_HELP_STRING([--enable-wavpack], - [enable WavPack support]),, - enable_wavpack=auto) - AC_ARG_ENABLE(werror, AS_HELP_STRING([--enable-werror], [treat warnings as errors (default: disabled)]),, enable_werror=no) -AC_ARG_ENABLE(wildmidi, - AS_HELP_STRING([--enable-wildmidi], - [enable MIDI support via wildmidi (default: disable)]),, - enable_wildmidi=no) - AC_ARG_WITH(zeroconf, AS_HELP_STRING([--with-zeroconf=@<:@auto|avahi|bonjour|no@:>@], [enable zeroconf backend (default=auto)]),, @@ -806,20 +730,6 @@ dnl --------------------------------------------------------------------------- dnl Decoder Plugins dnl --------------------------------------------------------------------------- -dnl -------------------------------- audiofile -------------------------------- -MPD_AUTO_PKG(audiofile, AUDIOFILE, [audiofile >= 0.1.7], - [audiofile decoder plugin], [libaudiofile not found]) -AM_CONDITIONAL(HAVE_AUDIOFILE, test x$enable_audiofile = xyes) -if test x$enable_audiofile = xyes; then - AC_DEFINE(HAVE_AUDIOFILE, 1, [Define for audiofile support]) -fi - -dnl ----------------------------------- FAAD ---------------------------------- -AM_PATH_FAAD() - -AM_CONDITIONAL(HAVE_FAAD, test x$enable_aac = xyes) -AM_CONDITIONAL(HAVE_MP4, test x$enable_mp4 = xyes) - dnl ---------------------------------- ffmpeg --------------------------------- MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat >= 52.31 libavcodec >= 52.20 libavutil >= 49.15], [ffmpeg decoder library], [libavformat+libavcodec+libavutil not found]) @@ -830,281 +740,6 @@ fi AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes) -dnl ----------------------------------- FLAC ---------------------------------- - -MPD_AUTO_PKG(flac, FLAC, [flac >= 1.1], - [FLAC decoder], [libFLAC not found]) - -if test x$enable_flac = xyes; then - AC_DEFINE(HAVE_FLAC, 1, [Define for FLAC support]) -fi - -AM_CONDITIONAL(HAVE_FLAC, test x$enable_flac = xyes) - -enable_flac_encoder=$enable_flac - -dnl -------------------------------- FluidSynth ------------------------------- -if test x$enable_fluidsynth = xyes; then - PKG_CHECK_MODULES(FLUIDSYNTH, [fluidsynth], - AC_DEFINE(ENABLE_FLUIDSYNTH, 1, [Define for fluidsynth support]), - enable_fluidsynth=no) -fi - -AM_CONDITIONAL(ENABLE_FLUIDSYNTH, test x$enable_fluidsynth = xyes) - -dnl ---------------------------------- libgme --------------------------------- -MPD_AUTO_PKG(gme, GME, [libgme], - [gme decoder plugin], [libgme not found]) -AM_CONDITIONAL(HAVE_GME, test x$enable_gme = xyes) -if test x$enable_gme = xyes; then - AC_DEFINE(HAVE_GME, 1, [Define for gme support]) -fi - -dnl ---------------------------------- libmad --------------------------------- -MPD_AUTO_PKG_LIB(mad, MAD, [mad], - mad, mad_stream_init, [-lmad], [], - [libmad MP3 decoder plugin], [libmad not found]) -if test x$enable_mad = xyes; then - AC_DEFINE(HAVE_MAD, 1, [Define to use libmad]) -fi -AM_CONDITIONAL(HAVE_MAD, test x$enable_mad = xyes) - -enable_shout2="$enable_shout" -MPD_AUTO_PKG(shout, SHOUT, [shout], - [shout output plugin], [libshout not found]) -if test x$enable_shout = xyes && test x$enable_shout2 = xauto; then - enable_shout=auto -fi - -dnl -------------------------------- libmpg123 -------------------------------- -MPD_AUTO_PKG(mpg123, MPG123, [libmpg123], - [libmpg123 decoder plugin], [libmpg123 not found]) -if test x$enable_mpg123 = xyes; then - AC_DEFINE(HAVE_MPG123, 1, [Define to use libmpg123]) -fi -AM_CONDITIONAL(HAVE_MPG123, test x$enable_mpg123 = xyes) - -dnl -------------------------------- libmikmod -------------------------------- -if test x$enable_mikmod = xyes; then - AC_PATH_PROG(LIBMIKMOD_CONFIG, libmikmod-config) - if test x$LIBMIKMOD_CONFIG != x ; then - AC_SUBST(LIBMIKMOD_CFLAGS, `$LIBMIKMOD_CONFIG --cflags`) - AC_SUBST(LIBMIKMOD_LIBS, `$LIBMIKMOD_CONFIG --libs`) - AC_DEFINE(ENABLE_MIKMOD_DECODER, 1, [Define for mikmod support]) - else - enable_mikmod=no - fi -fi - -AM_CONDITIONAL(ENABLE_MIKMOD_DECODER, test x$enable_mikmod = xyes) - -dnl -------------------------------- libmodplug ------------------------------- -found_modplug=$HAVE_CXX -MPD_AUTO_PRE(modplug, [modplug decoder plugin], [No C++ compiler found]) - -MPD_AUTO_PKG(modplug, MODPLUG, [libmodplug], - [modplug decoder plugin], [libmodplug not found]) - -if test x$enable_modplug = xyes; then - AC_DEFINE(HAVE_MODPLUG, 1, [Define for modplug support]) -fi -AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes) - -dnl -------------------------------- libsndfile ------------------------------- -dnl See above test, which may disable this. -MPD_AUTO_PKG(sndfile, SNDFILE, [sndfile], - [libsndfile decoder plugin], [libsndfile not found]) - -if test x$enable_sndfile = xyes; then - AC_DEFINE(ENABLE_SNDFILE, 1, [Define to enable the sndfile decoder plugin]) -fi -AM_CONDITIONAL(ENABLE_SNDFILE, test x$enable_sndfile = xyes) - -dnl --------------------------------- musepack -------------------------------- -if test x$enable_mpc = xyes; then - oldcflags=$CFLAGS - oldlibs=$LIBS - oldcppflags=$CPPFLAGS - AC_CHECK_LIB(mpcdec,main, - MPCDEC_LIBS="$MPCDEC_LIBS -lmpcdec", - enable_mpc=no) - CFLAGS=$oldcflags - LIBS=$oldlibs - CPPFLAGS=$oldcppflags - - if test x$enable_mpc = xyes; then - AC_CHECK_HEADER([mpc/mpcdec.h], - [AC_DEFINE(HAVE_MPCDEC,1, - [Define to use libmpcdec for MPC decoding])], - [AC_CHECK_HEADER(mpcdec/mpcdec.h, - [AC_DEFINE(MPC_IS_OLD_API, 1, - [Define if an old pre-SV8 libmpcdec is used])] - )] - ) - else - AC_MSG_WARN([mpcdec lib needed for MPC support -- disabling MPC support]) - fi -fi - -AC_SUBST(MPCDEC_LIBS) -AC_SUBST(MPCDEC_CFLAGS) -AM_CONDITIONAL(HAVE_MPCDEC, test x$enable_mpc = xyes) - -dnl -------------------------------- Ogg Tremor ------------------------------- -if test x$with_tremor = xyes || test x$with_tremor = xno; then - enable_tremor="$with_tremor" -else - tremor_prefix="$with_tremor" - enable_tremor=yes -fi - -if test x$enable_tremor = xyes; then - if test "x$tremor_libraries" != "x" ; then - TREMOR_LIBS="-L$tremor_libraries" - elif test "x$tremor_prefix" != "x" ; then - TREMOR_LIBS="-L$tremor_prefix/lib" - fi - TREMOR_LIBS="$TREMOR_LIBS -lvorbisidec" - if test "x$tremor_includes" != "x" ; then - TREMOR_CFLAGS="-I$tremor_includes" - elif test "x$tremor_prefix" != "x" ; then - TREMOR_CFLAGS="-I$tremor_prefix/include" - fi - ac_save_CFLAGS="$CFLAGS" - ac_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $TREMOR_CFLAGS" - LIBS="$LIBS $TREMOR_LIBS" - AC_CHECK_LIB(vorbisidec,ov_read,,enable_tremor=no; - AC_MSG_WARN([vorbisidec lib needed for ogg support with tremor -- disabling ogg support])) - CFLAGS="$ac_save_CFLAGS" - LIBS="$ac_save_LIBS" -fi - -if test x$enable_tremor = xyes; then - AC_DEFINE(HAVE_TREMOR,1, - [Define to use tremor (libvorbisidec) for ogg support]) - AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]) -else - TREMOR_CFLAGS= - TREMOR_LIBS= -fi - -AC_SUBST(TREMOR_CFLAGS) -AC_SUBST(TREMOR_LIBS) - -dnl -------------------------------- Ogg Vorbis ------------------------------- - -if test x$enable_tremor = xyes; then - if test x$enable_vorbis = xyes; then - AC_MSG_WARN(["OggTremor detected, could not enable Vorbis."]) - fi - enable_vorbis=no -fi - -MPD_AUTO_PKG(vorbis, VORBIS, [vorbis vorbisfile ogg], - [Ogg Vorbis decoder], [libvorbis not found]) -if test x$enable_vorbis = xyes; then - AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]) -fi - -AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enable_tremor = xyes) - -dnl --------------------------------- sidplay --------------------------------- -found_sidplay=$HAVE_CXX -MPD_AUTO_PRE(sidplay, [sidplay decoder plugin], [No C++ compiler found]) - -if test x$enable_sidplay != xno; then - # we're not using pkg-config here - # because libsidplay2's .pc file requires libtool - AC_CHECK_LIB([sidplay2],[main],[found_sidplay=yes],[found_sidplay=no],[]) - - MPD_AUTO_PRE(sidplay, [sidplay decoder plugin], - [libsidplay2 not found]) -fi - -if test x$enable_sidplay != xno; then - AC_CHECK_LIB([resid-builder], [main], - [found_sidplay=yes], [found_sidplay=no]) - - if test x$found_sidplay = xyes; then - AC_CHECK_LIB([sidutils],[main],[],[found_sidplay=no],[]) - fi - - MPD_AUTO_RESULT(sidplay, [sidplay decoder plugin], - [libresid-builder or libsidutils not found]) -fi - -if test x$enable_sidplay = xyes; then - AC_SUBST(SIDPLAY_LIBS,"-lsidplay2 -lresid-builder -lsidutils") - AC_SUBST(SIDPLAY_CFLAGS,) - - AC_DEFINE(ENABLE_SIDPLAY, 1, [Define for libsidplay2 support]) -fi - -AM_CONDITIONAL(ENABLE_SIDPLAY, test x$enable_sidplay = xyes) - -dnl --------------------------------- wavpack --------------------------------- -MPD_AUTO_PKG(wavpack, WAVPACK, [wavpack], - [WavPack decoder plugin], [libwavpack not found]) -AM_CONDITIONAL(HAVE_WAVPACK, test x$enable_wavpack = xyes) -if test x$enable_wavpack = xyes; then - AC_DEFINE([HAVE_WAVPACK], 1, [Define to enable WavPack support]) -fi - -dnl --------------------------------- WildMidi -------------------------------- -if test x$enable_wildmidi = xyes; then - oldcflags=$CFLAGS - oldlibs=$LIBS - oldcppflags=$CPPFLAGS - - AC_CHECK_LIB(WildMidi, WildMidi_Init,, - AC_MSG_ERROR([libwildmidi not found])) - - AC_CHECK_LIB(WildMidi, WildMidi_SampledSeek, - [AC_DEFINE(HAVE_WILDMIDI_SAMPLED_SEEK, 1, - [Defined if WildMidi_SampledSeek() is available (libwildmidi <= 0.2.2)])]) - - CFLAGS=$oldcflags - LIBS=$oldlibs - CPPFLAGS=$oldcppflags - - AC_SUBST(WILDMIDI_LIBS,-lWildMidi) - AC_SUBST(WILDMIDI_CFLAGS,) - - AC_DEFINE(ENABLE_WILDMIDI, 1, [Define for wildmidi support]) -fi - -AM_CONDITIONAL(ENABLE_WILDMIDI, test x$enable_wildmidi = xyes) - -dnl ------------------------ Post Decoder Plugins Tests ----------------------- - -if - test x$enable_aac = xno && - test x$enable_audiofile = xno && - test x$enable_ffmpeg = xno && - test x$enable_flac = xno && - test x$enable_fluidsynth = xno && - test x$enable_mad = xno && - test x$enable_mikmod = xno; then - test x$enable_modplug = xno && - test x$enable_mp4 = xno && - test x$enable_mpc = xno && - test x$enable_mpg123 = xno && - test x$enable_sidplay = xno && - test x$enable_tremor = xno && - test x$enable_vorbis = xno && - test x$enable_wavpack = xno && - test x$enable_wildmidi = xno && - - AC_MSG_ERROR([No input plugins supported!]) -fi - -AM_CONDITIONAL(HAVE_OGG_COMMON, - test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes) - -AM_CONDITIONAL(HAVE_FLAC_COMMON, - test x$enable_flac = xyes) - dnl --------------------------------------------------------------------------- dnl Encoders for Streaming Audio Output Plugins dnl --------------------------------------------------------------------------- @@ -1291,6 +926,14 @@ if test x$enable_jack = xyes; then LIBS=$old_LIBS fi +enable_shout2="$enable_shout" +MPD_AUTO_PKG(shout, SHOUT, [shout], + [shout output plugin], [libshout not found]) +if test x$enable_shout = xyes && test x$enable_shout2 = xauto; then + enable_shout=auto +fi + + AM_CONDITIONAL(HAVE_JACK, test x$enable_jack = xyes) dnl ---------------------------------- libao ---------------------------------- @@ -1539,26 +1182,7 @@ results(tcp, "TCP") results(un,[UNIX Domain Sockets]) printf '\nFile format support:\n\t' -results(aac, [AAC]) -results(sidplay, [C64 SID]) results(ffmpeg, [FFMPEG]) -results(flac, [FLAC]) -results(fluidsynth, [FluidSynth]) -results(gme, [GME]) -results(sndfile, [libsndfile]) -printf '\n\t' -results(mikmod, [MikMod]) -results(modplug, [MODPLUG]) -results(mad, [MAD]) -results(mpg123, [MPG123]) -results(mp4, [MP4]) -results(mpc, [Musepack]) -printf '\n\t' -results(tremor, [OggTremor]) -results(vorbis, [OggVorbis]) -results(audiofile, [WAVE]) -results(wavpack, [WavPack]) -results(wildmidi, [WildMidi]) printf '\nOther features:\n\t' results(lsr, [libsamplerate]) diff --git a/src/decoder/_flac_common.c b/src/decoder/_flac_common.c deleted file mode 100644 index bab3995f..00000000 --- a/src/decoder/_flac_common.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* - * Common data structures and functions used by FLAC and OggFLAC - */ - -#include "config.h" -#include "_flac_common.h" -#include "flac_metadata.h" -#include "flac_pcm.h" -#include "audio_check.h" - -#include <glib.h> - -#include <assert.h> - -void -flac_data_init(struct flac_data *data, struct decoder * decoder, - struct input_stream *input_stream) -{ - pcm_buffer_init(&data->buffer); - - data->unsupported = false; - data->initialized = false; - data->total_frames = 0; - data->first_frame = 0; - data->next_frame = 0; - - data->position = 0; - data->decoder = decoder; - data->input_stream = input_stream; - data->tag = NULL; -} - -void -flac_data_deinit(struct flac_data *data) -{ - pcm_buffer_deinit(&data->buffer); - - if (data->tag != NULL) - tag_free(data->tag); -} - -static enum sample_format -flac_sample_format(unsigned bits_per_sample) -{ - switch (bits_per_sample) { - case 8: - return SAMPLE_FORMAT_S8; - - case 16: - return SAMPLE_FORMAT_S16; - - case 24: - return SAMPLE_FORMAT_S24_P32; - - case 32: - return SAMPLE_FORMAT_S32; - - default: - return SAMPLE_FORMAT_UNDEFINED; - } -} - -static void -flac_got_stream_info(struct flac_data *data, - const FLAC__StreamMetadata_StreamInfo *stream_info) -{ - if (data->initialized || data->unsupported) - return; - - GError *error = NULL; - if (!audio_format_init_checked(&data->audio_format, - stream_info->sample_rate, - flac_sample_format(stream_info->bits_per_sample), - stream_info->channels, &error)) { - g_warning("%s", error->message); - g_error_free(error); - data->unsupported = true; - return; - } - - data->frame_size = audio_format_frame_size(&data->audio_format); - - if (data->total_frames == 0) - data->total_frames = stream_info->total_samples; - - data->initialized = true; -} - -void flac_metadata_common_cb(const FLAC__StreamMetadata * block, - struct flac_data *data) -{ - if (data->unsupported) - return; - - struct replay_gain_info rgi; - char *mixramp_start; - char *mixramp_end; - float replay_gain_db = 0; - - switch (block->type) { - case FLAC__METADATA_TYPE_STREAMINFO: - flac_got_stream_info(data, &block->data.stream_info); - break; - - case FLAC__METADATA_TYPE_VORBIS_COMMENT: - if (flac_parse_replay_gain(&rgi, block)) - replay_gain_db = decoder_replay_gain(data->decoder, &rgi); - if (flac_parse_mixramp(&mixramp_start, &mixramp_end, block)) { - g_debug("setting mixramp_tags"); - decoder_mixramp(data->decoder, replay_gain_db, - mixramp_start, mixramp_end); - } - - if (data->tag != NULL) - flac_vorbis_comments_to_tag(data->tag, NULL, - &block->data.vorbis_comment); - - default: - break; - } -} - -void flac_error_common_cb(const FLAC__StreamDecoderErrorStatus status, - struct flac_data *data) -{ - if (decoder_get_command(data->decoder) == DECODE_COMMAND_STOP) - return; - - g_warning("%s", FLAC__StreamDecoderErrorStatusString[status]); -} - -/** - * This function attempts to call decoder_initialized() in case there - * was no STREAMINFO block. This is allowed for nonseekable streams, - * where the server sends us only a part of the file, without - * providing the STREAMINFO block from the beginning of the file - * (e.g. when seeking with SqueezeBox Server). - */ -static bool -flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header) -{ - if (data->unsupported) - return false; - - GError *error = NULL; - if (!audio_format_init_checked(&data->audio_format, - header->sample_rate, - flac_sample_format(header->bits_per_sample), - header->channels, &error)) { - g_warning("%s", error->message); - g_error_free(error); - data->unsupported = true; - return false; - } - - data->frame_size = audio_format_frame_size(&data->audio_format); - - decoder_initialized(data->decoder, &data->audio_format, - data->input_stream->seekable, - (float)data->total_frames / - (float)data->audio_format.sample_rate); - - data->initialized = true; - - return true; -} - -FLAC__StreamDecoderWriteStatus -flac_common_write(struct flac_data *data, const FLAC__Frame * frame, - const FLAC__int32 *const buf[], - FLAC__uint64 nbytes) -{ - enum decoder_command cmd; - void *buffer; - unsigned bit_rate; - - if (!data->initialized && !flac_got_first_frame(data, &frame->header)) - return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; - - size_t buffer_size = frame->header.blocksize * data->frame_size; - buffer = pcm_buffer_get(&data->buffer, buffer_size); - - flac_convert(buffer, frame->header.channels, - data->audio_format.format, buf, - 0, frame->header.blocksize); - - if (nbytes > 0) - bit_rate = nbytes * 8 * frame->header.sample_rate / - (1000 * frame->header.blocksize); - else - bit_rate = 0; - - cmd = decoder_data(data->decoder, data->input_stream, - buffer, buffer_size, - bit_rate); - data->next_frame += frame->header.blocksize; - switch (cmd) { - case DECODE_COMMAND_NONE: - case DECODE_COMMAND_START: - break; - - case DECODE_COMMAND_STOP: - return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; - - case DECODE_COMMAND_SEEK: - return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; - } - - return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; -} diff --git a/src/decoder/_flac_common.h b/src/decoder/_flac_common.h deleted file mode 100644 index 0d90ba65..00000000 --- a/src/decoder/_flac_common.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* - * Common data structures and functions used by FLAC and OggFLAC - */ - -#ifndef MPD_FLAC_COMMON_H -#define MPD_FLAC_COMMON_H - -#include "decoder_api.h" -#include "pcm_buffer.h" - -#include <glib.h> - -#include <FLAC/stream_decoder.h> -#include <FLAC/metadata.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "flac" - -struct flac_data { - struct pcm_buffer buffer; - - /** - * The size of one frame in the output buffer. - */ - unsigned frame_size; - - /** - * Has decoder_initialized() been called yet? - */ - bool initialized; - - /** - * Does the FLAC file contain an unsupported audio format? - */ - bool unsupported; - - /** - * The validated audio format of the FLAC file. This - * attribute is defined if "initialized" is true. - */ - struct audio_format audio_format; - - /** - * The total number of frames in this song. The decoder - * plugin may initialize this attribute to override the value - * provided by libFLAC (e.g. for sub songs from a CUE sheet). - */ - FLAC__uint64 total_frames; - - /** - * The number of the first frame in this song. This is only - * non-zero if playing sub songs from a CUE sheet. - */ - FLAC__uint64 first_frame; - - /** - * The number of the next frame which is going to be decoded. - */ - FLAC__uint64 next_frame; - - FLAC__uint64 position; - struct decoder *decoder; - struct input_stream *input_stream; - struct tag *tag; -}; - -/* initializes a given FlacData struct */ -void -flac_data_init(struct flac_data *data, struct decoder * decoder, - struct input_stream *input_stream); - -void -flac_data_deinit(struct flac_data *data); - -void flac_metadata_common_cb(const FLAC__StreamMetadata * block, - struct flac_data *data); - -void flac_error_common_cb(FLAC__StreamDecoderErrorStatus status, - struct flac_data *data); - -FLAC__StreamDecoderWriteStatus -flac_common_write(struct flac_data *data, const FLAC__Frame * frame, - const FLAC__int32 *const buf[], - FLAC__uint64 nbytes); - -#endif /* _FLAC_COMMON_H */ diff --git a/src/decoder/_ogg_common.c b/src/decoder/_ogg_common.c deleted file mode 100644 index bedd3de6..00000000 --- a/src/decoder/_ogg_common.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* - * Common functions used for Ogg data streams (Ogg-Vorbis and OggFLAC) - */ - -#include "config.h" -#include "_ogg_common.h" - -ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream) -{ - /* oggflac detection based on code in ogg123 and this post - * http://lists.xiph.org/pipermail/flac/2004-December/000393.html - * ogg123 trunk still doesn't have this patch as of June 2005 */ - unsigned char buf[41]; - size_t r; - - r = decoder_read(NULL, inStream, buf, sizeof(buf)); - if (r >= 32 && memcmp(buf, "OggS", 4) == 0 && ( - (memcmp(buf+29, "FLAC", 4) == 0 - && memcmp(buf+37, "fLaC", 4) == 0) - || (memcmp(buf+28, "FLAC", 4) == 0) - || (memcmp(buf+28, "fLaC", 4) == 0))) { - return FLAC; - } - return VORBIS; -} diff --git a/src/decoder/_ogg_common.h b/src/decoder/_ogg_common.h deleted file mode 100644 index 85e4ebba..00000000 --- a/src/decoder/_ogg_common.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* - * Common functions used for Ogg data streams (Ogg-Vorbis and OggFLAC) - */ - -#ifndef MPD_OGG_COMMON_H -#define MPD_OGG_COMMON_H - -#include "decoder_api.h" - -typedef enum _ogg_stream_type { VORBIS, FLAC } ogg_stream_type; - -ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream); - -#endif /* _OGG_COMMON_H */ diff --git a/src/decoder/audiofile_decoder_plugin.c b/src/decoder/audiofile_decoder_plugin.c deleted file mode 100644 index b344795e..00000000 --- a/src/decoder/audiofile_decoder_plugin.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "decoder_api.h" -#include "audio_check.h" -#include "tag_handler.h" - -#include <audiofile.h> -#include <af_vfs.h> -#include <assert.h> -#include <glib.h> -#include <stdio.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "audiofile" - -/* pick 1020 since its devisible for 8,16,24, and 32-bit audio */ -#define CHUNK_SIZE 1020 - -static int audiofile_get_duration(const char *file) -{ - int total_time; - AFfilehandle af_fp = afOpenFile(file, "r", NULL); - if (af_fp == AF_NULL_FILEHANDLE) { - return -1; - } - total_time = (int) - ((double)afGetFrameCount(af_fp, AF_DEFAULT_TRACK) - / afGetRate(af_fp, AF_DEFAULT_TRACK)); - afCloseFile(af_fp); - return total_time; -} - -static ssize_t -audiofile_file_read(AFvirtualfile *vfile, void *data, size_t length) -{ - struct input_stream *is = (struct input_stream *) vfile->closure; - GError *error = NULL; - size_t nbytes; - - nbytes = input_stream_lock_read(is, data, length, &error); - if (nbytes == 0 && error != NULL) { - g_warning("%s", error->message); - g_error_free(error); - return -1; - } - - return nbytes; -} - -static AFfileoffset -audiofile_file_length(AFvirtualfile *vfile) -{ - struct input_stream *is = (struct input_stream *) vfile->closure; - return is->size; -} - -static AFfileoffset -audiofile_file_tell(AFvirtualfile *vfile) -{ - struct input_stream *is = (struct input_stream *) vfile->closure; - return is->offset; -} - -static void -audiofile_file_destroy(AFvirtualfile *vfile) -{ - assert(vfile->closure != NULL); - - vfile->closure = NULL; -} - -static AFfileoffset -audiofile_file_seek(AFvirtualfile *vfile, AFfileoffset offset, int is_relative) -{ - struct input_stream *is = (struct input_stream *) vfile->closure; - int whence = (is_relative ? SEEK_CUR : SEEK_SET); - if (input_stream_lock_seek(is, offset, whence, NULL)) { - return is->offset; - } else { - return -1; - } -} - -static AFvirtualfile * -setup_virtual_fops(struct input_stream *stream) -{ - AFvirtualfile *vf = g_malloc(sizeof(AFvirtualfile)); - vf->closure = stream; - vf->write = NULL; - vf->read = audiofile_file_read; - vf->length = audiofile_file_length; - vf->destroy = audiofile_file_destroy; - vf->seek = audiofile_file_seek; - vf->tell = audiofile_file_tell; - return vf; -} - -static enum sample_format -audiofile_bits_to_sample_format(int bits) -{ - switch (bits) { - case 8: - return SAMPLE_FORMAT_S8; - - case 16: - return SAMPLE_FORMAT_S16; - - case 24: - return SAMPLE_FORMAT_S24_P32; - - case 32: - return SAMPLE_FORMAT_S32; - } - - return SAMPLE_FORMAT_UNDEFINED; -} - -static enum sample_format -audiofile_setup_sample_format(AFfilehandle af_fp) -{ - int fs, bits; - - afGetSampleFormat(af_fp, AF_DEFAULT_TRACK, &fs, &bits); - if (!audio_valid_sample_format(audiofile_bits_to_sample_format(bits))) { - g_debug("input file has %d bit samples, converting to 16", - bits); - bits = 16; - } - - afSetVirtualSampleFormat(af_fp, AF_DEFAULT_TRACK, - AF_SAMPFMT_TWOSCOMP, bits); - afGetVirtualSampleFormat(af_fp, AF_DEFAULT_TRACK, &fs, &bits); - - return audiofile_bits_to_sample_format(bits); -} - -static void -audiofile_stream_decode(struct decoder *decoder, struct input_stream *is) -{ - GError *error = NULL; - AFvirtualfile *vf; - int fs, frame_count; - AFfilehandle af_fp; - struct audio_format audio_format; - float total_time; - uint16_t bit_rate; - int ret; - char chunk[CHUNK_SIZE]; - enum decoder_command cmd; - - if (!is->seekable) { - g_warning("not seekable"); - return; - } - - vf = setup_virtual_fops(is); - - af_fp = afOpenVirtualFile(vf, "r", NULL); - if (af_fp == AF_NULL_FILEHANDLE) { - g_warning("failed to input stream\n"); - return; - } - - if (!audio_format_init_checked(&audio_format, - afGetRate(af_fp, AF_DEFAULT_TRACK), - audiofile_setup_sample_format(af_fp), - afGetVirtualChannels(af_fp, AF_DEFAULT_TRACK), - &error)) { - g_warning("%s", error->message); - g_error_free(error); - afCloseFile(af_fp); - return; - } - - frame_count = afGetFrameCount(af_fp, AF_DEFAULT_TRACK); - - total_time = ((float)frame_count / (float)audio_format.sample_rate); - - bit_rate = (uint16_t)(is->size * 8.0 / total_time / 1000.0 + 0.5); - - fs = (int)afGetVirtualFrameSize(af_fp, AF_DEFAULT_TRACK, 1); - - decoder_initialized(decoder, &audio_format, true, total_time); - - do { - ret = afReadFrames(af_fp, AF_DEFAULT_TRACK, chunk, - CHUNK_SIZE / fs); - if (ret <= 0) - break; - - cmd = decoder_data(decoder, NULL, - chunk, ret * fs, - bit_rate); - - if (cmd == DECODE_COMMAND_SEEK) { - AFframecount frame = decoder_seek_where(decoder) * - audio_format.sample_rate; - afSeekFrame(af_fp, AF_DEFAULT_TRACK, frame); - - decoder_command_finished(decoder); - cmd = DECODE_COMMAND_NONE; - } - } while (cmd == DECODE_COMMAND_NONE); - - afCloseFile(af_fp); -} - -static bool -audiofile_scan_file(const char *file, - const struct tag_handler *handler, void *handler_ctx) -{ - int total_time = audiofile_get_duration(file); - - if (total_time < 0) { - g_debug("Failed to get total song time from: %s\n", - file); - return false; - } - - tag_handler_invoke_duration(handler, handler_ctx, total_time); - return true; -} - -static const char *const audiofile_suffixes[] = { - "wav", "au", "aiff", "aif", NULL -}; - -static const char *const audiofile_mime_types[] = { - "audio/x-wav", - "audio/x-aiff", - NULL -}; - -const struct decoder_plugin audiofile_decoder_plugin = { - .name = "audiofile", - .stream_decode = audiofile_stream_decode, - .scan_file = audiofile_scan_file, - .suffixes = audiofile_suffixes, - .mime_types = audiofile_mime_types, -}; diff --git a/src/decoder/dsdiff_decoder_plugin.c b/src/decoder/dsdiff_decoder_plugin.c deleted file mode 100644 index 84471fb3..00000000 --- a/src/decoder/dsdiff_decoder_plugin.c +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* \file - * - * This plugin decodes DSDIFF data (SACD) embedded in DFF files. - * The DFF code was modeled after the specification found here: - * http://www.sonicstudio.com/pdf/dsd/DSDIFF_1.5_Spec.pdf - * - * All functions common to both DSD decoders have been moved to dsdlib - */ - -#include "config.h" -#include "dsdiff_decoder_plugin.h" -#include "decoder_api.h" -#include "audio_check.h" -#include "util/bit_reverse.h" -#include "tag_handler.h" -#include "dsdlib.h" -#include "tag_handler.h" - -#include <unistd.h> -#include <stdio.h> /* for SEEK_SET, SEEK_CUR */ - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "dsdiff" - -struct dsdiff_header { - struct dsdlib_id id; - uint32_t size_high, size_low; - struct dsdlib_id format; -}; - -struct dsdiff_chunk_header { - struct dsdlib_id id; - uint32_t size_high, size_low; -}; - -struct dsdiff_metadata { - unsigned sample_rate, channels; - bool bitreverse; - uint64_t chunk_size; -}; - -static bool lsbitfirst; - -static bool -dsdiff_init(const struct config_param *param) -{ - lsbitfirst = config_get_block_bool(param, "lsbitfirst", false); - return true; -} - -/** - * Read the "size" attribute from the specified header, converting it - * to the host byte order if needed. - */ -G_GNUC_CONST -static uint64_t -dsdiff_chunk_size(const struct dsdiff_chunk_header *header) -{ - return (((uint64_t)GUINT32_FROM_BE(header->size_high)) << 32) | - ((uint64_t)GUINT32_FROM_BE(header->size_low)); -} - -static bool -dsdiff_read_id(struct decoder *decoder, struct input_stream *is, - struct dsdlib_id *id) -{ - return dsdlib_read(decoder, is, id, sizeof(*id)); -} - -static bool -dsdiff_read_chunk_header(struct decoder *decoder, struct input_stream *is, - struct dsdiff_chunk_header *header) -{ - return dsdlib_read(decoder, is, header, sizeof(*header)); -} - -static bool -dsdiff_read_payload(struct decoder *decoder, struct input_stream *is, - const struct dsdiff_chunk_header *header, - void *data, size_t length) -{ - uint64_t size = dsdiff_chunk_size(header); - if (size != (uint64_t)length) - return false; - - size_t nbytes = decoder_read(decoder, is, data, length); - return nbytes == length; -} - -/** - * Read and parse a "SND" chunk inside "PROP". - */ -static bool -dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is, - struct dsdiff_metadata *metadata, - goffset end_offset) -{ - struct dsdiff_chunk_header header; - while ((goffset)(is->offset + sizeof(header)) <= end_offset) { - if (!dsdiff_read_chunk_header(decoder, is, &header)) - return false; - - goffset chunk_end_offset = - is->offset + dsdiff_chunk_size(&header); - if (chunk_end_offset > end_offset) - return false; - - if (dsdlib_id_equals(&header.id, "FS ")) { - uint32_t sample_rate; - if (!dsdiff_read_payload(decoder, is, &header, - &sample_rate, - sizeof(sample_rate))) - return false; - - metadata->sample_rate = GUINT32_FROM_BE(sample_rate); - } else if (dsdlib_id_equals(&header.id, "CHNL")) { - uint16_t channels; - if (dsdiff_chunk_size(&header) < sizeof(channels) || - !dsdlib_read(decoder, is, - &channels, sizeof(channels)) || - !dsdlib_skip_to(decoder, is, chunk_end_offset)) - return false; - - metadata->channels = GUINT16_FROM_BE(channels); - } else if (dsdlib_id_equals(&header.id, "CMPR")) { - struct dsdlib_id type; - if (dsdiff_chunk_size(&header) < sizeof(type) || - !dsdlib_read(decoder, is, - &type, sizeof(type)) || - !dsdlib_skip_to(decoder, is, chunk_end_offset)) - return false; - - if (!dsdlib_id_equals(&type, "DSD ")) - /* only uncompressed DSD audio data - is implemented */ - return false; - } else { - /* ignore unknown chunk */ - - if (!dsdlib_skip_to(decoder, is, chunk_end_offset)) - return false; - } - } - - return is->offset == end_offset; -} - -/** - * Read and parse a "PROP" chunk. - */ -static bool -dsdiff_read_prop(struct decoder *decoder, struct input_stream *is, - struct dsdiff_metadata *metadata, - const struct dsdiff_chunk_header *prop_header) -{ - uint64_t prop_size = dsdiff_chunk_size(prop_header); - goffset end_offset = is->offset + prop_size; - - struct dsdlib_id prop_id; - if (prop_size < sizeof(prop_id) || - !dsdiff_read_id(decoder, is, &prop_id)) - return false; - - if (dsdlib_id_equals(&prop_id, "SND ")) - return dsdiff_read_prop_snd(decoder, is, metadata, end_offset); - else - /* ignore unknown PROP chunk */ - return dsdlib_skip_to(decoder, is, end_offset); -} - -/** - * Read and parse all metadata chunks at the beginning. Stop when the - * first "DSD" chunk is seen, and return its header in the - * "chunk_header" parameter. - */ -static bool -dsdiff_read_metadata(struct decoder *decoder, struct input_stream *is, - struct dsdiff_metadata *metadata, - struct dsdiff_chunk_header *chunk_header) -{ - struct dsdiff_header header; - if (!dsdlib_read(decoder, is, &header, sizeof(header)) || - !dsdlib_id_equals(&header.id, "FRM8") || - !dsdlib_id_equals(&header.format, "DSD ")) - return false; - - while (true) { - if (!dsdiff_read_chunk_header(decoder, is, - chunk_header)) - return false; - - if (dsdlib_id_equals(&chunk_header->id, "PROP")) { - if (!dsdiff_read_prop(decoder, is, metadata, - chunk_header)) - return false; - } else if (dsdlib_id_equals(&chunk_header->id, "DSD ")) { - uint64_t chunk_size; - chunk_size = dsdiff_chunk_size(chunk_header); - metadata->chunk_size = chunk_size; - return true; - } else { - /* ignore unknown chunk */ - uint64_t chunk_size; - chunk_size = dsdiff_chunk_size(chunk_header); - goffset chunk_end_offset = is->offset + chunk_size; - - if (!dsdlib_skip_to(decoder, is, chunk_end_offset)) - return false; - } - } -} - -static void -bit_reverse_buffer(uint8_t *p, uint8_t *end) -{ - for (; p < end; ++p) - *p = bit_reverse(*p); -} - -/** - * Decode one "DSD" chunk. - */ -static bool -dsdiff_decode_chunk(struct decoder *decoder, struct input_stream *is, - unsigned channels, - uint64_t chunk_size) -{ - uint8_t buffer[8192]; - - const size_t sample_size = sizeof(buffer[0]); - const size_t frame_size = channels * sample_size; - const unsigned buffer_frames = sizeof(buffer) / frame_size; - const unsigned buffer_samples = buffer_frames * frame_size; - const size_t buffer_size = buffer_samples * sample_size; - - while (chunk_size > 0) { - /* see how much aligned data from the remaining chunk - fits into the local buffer */ - unsigned now_frames = buffer_frames; - size_t now_size = buffer_size; - if (chunk_size < (uint64_t)now_size) { - now_frames = (unsigned)chunk_size / frame_size; - now_size = now_frames * frame_size; - } - - size_t nbytes = decoder_read(decoder, is, buffer, now_size); - if (nbytes != now_size) - return false; - - chunk_size -= nbytes; - - if (lsbitfirst) - bit_reverse_buffer(buffer, buffer + nbytes); - - enum decoder_command cmd = - decoder_data(decoder, is, buffer, nbytes, 0); - switch (cmd) { - case DECODE_COMMAND_NONE: - break; - - case DECODE_COMMAND_START: - case DECODE_COMMAND_STOP: - return false; - - case DECODE_COMMAND_SEEK: - - /* Not implemented yet */ - decoder_seek_error(decoder); - break; - } - } - return dsdlib_skip(decoder, is, chunk_size); -} - -static void -dsdiff_stream_decode(struct decoder *decoder, struct input_stream *is) -{ - struct dsdiff_metadata metadata = { - .sample_rate = 0, - .channels = 0, - }; - - struct dsdiff_chunk_header chunk_header; - /* check if it is is a proper DFF file */ - if (!dsdiff_read_metadata(decoder, is, &metadata, &chunk_header)) - return; - - GError *error = NULL; - struct audio_format audio_format; - if (!audio_format_init_checked(&audio_format, metadata.sample_rate / 8, - SAMPLE_FORMAT_DSD, - metadata.channels, &error)) { - g_warning("%s", error->message); - g_error_free(error); - return; - } - - /* calculate song time from DSD chunk size and sample frequency */ - uint64_t chunk_size = metadata.chunk_size; - float songtime = ((chunk_size / metadata.channels) * 8) / - (float) metadata.sample_rate; - - /* success: file was recognized */ - decoder_initialized(decoder, &audio_format, false, songtime); - - /* every iteration of the following loop decodes one "DSD" - chunk from a DFF file */ - - while (true) { - chunk_size = dsdiff_chunk_size(&chunk_header); - - if (dsdlib_id_equals(&chunk_header.id, "DSD ")) { - if (!dsdiff_decode_chunk(decoder, is, - metadata.channels, - chunk_size)) - break; - } else { - /* ignore other chunks */ - if (!dsdlib_skip(decoder, is, chunk_size)) - break; - } - - /* read next chunk header; the first one was read by - dsdiff_read_metadata() */ - if (!dsdiff_read_chunk_header(decoder, - is, &chunk_header)) - break; - } -} - -static bool -dsdiff_scan_stream(struct input_stream *is, - G_GNUC_UNUSED const struct tag_handler *handler, - G_GNUC_UNUSED void *handler_ctx) -{ - struct dsdiff_metadata metadata = { - .sample_rate = 0, - .channels = 0, - }; - - struct dsdiff_chunk_header chunk_header; - /* First check for DFF metadata */ - if (!dsdiff_read_metadata(NULL, is, &metadata, &chunk_header)) - return false; - - struct audio_format audio_format; - if (!audio_format_init_checked(&audio_format, metadata.sample_rate / 8, - SAMPLE_FORMAT_DSD, - metadata.channels, NULL)) - /* refuse to parse files which we cannot play anyway */ - return false; - - /* calculate song time and add as tag */ - unsigned songtime = ((metadata.chunk_size / metadata.channels) * 8) / - metadata.sample_rate; - tag_handler_invoke_duration(handler, handler_ctx, songtime); - - return true; -} - -static const char *const dsdiff_suffixes[] = { - "dff", - NULL -}; - -static const char *const dsdiff_mime_types[] = { - "application/x-dff", - NULL -}; - -const struct decoder_plugin dsdiff_decoder_plugin = { - .name = "dsdiff", - .init = dsdiff_init, - .stream_decode = dsdiff_stream_decode, - .scan_stream = dsdiff_scan_stream, - .suffixes = dsdiff_suffixes, - .mime_types = dsdiff_mime_types, -}; diff --git a/src/decoder/dsdiff_decoder_plugin.h b/src/decoder/dsdiff_decoder_plugin.h deleted file mode 100644 index 452f9050..00000000 --- a/src/decoder/dsdiff_decoder_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_DECODER_DSDIFF_H -#define MPD_DECODER_DSDIFF_H - -extern const struct decoder_plugin dsdiff_decoder_plugin; - -#endif diff --git a/src/decoder/dsdlib.c b/src/decoder/dsdlib.c deleted file mode 100644 index 3df9497c..00000000 --- a/src/decoder/dsdlib.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* \file - * - * This file contains functions used by the DSF and DSDIFF decoders. - * - */ - -#include "config.h" -#include "dsf_decoder_plugin.h" -#include "decoder_api.h" -#include "util/bit_reverse.h" -#include "dsdlib.h" -#include "dsdiff_decoder_plugin.h" - -#include <unistd.h> -#include <stdio.h> /* for SEEK_SET, SEEK_CUR */ - -bool -dsdlib_id_equals(const struct dsdlib_id *id, const char *s) -{ - assert(id != NULL); - assert(s != NULL); - assert(strlen(s) == sizeof(id->value)); - - return memcmp(id->value, s, sizeof(id->value)) == 0; -} - -bool -dsdlib_read(struct decoder *decoder, struct input_stream *is, - void *data, size_t length) -{ - size_t nbytes = decoder_read(decoder, is, data, length); - return nbytes == length; -} - -/** - * Skip the #input_stream to the specified offset. - */ -bool -dsdlib_skip_to(struct decoder *decoder, struct input_stream *is, - goffset offset) -{ - if (is->seekable) - return input_stream_seek(is, offset, SEEK_SET, NULL); - - if (is->offset > offset) - return false; - - char buffer[8192]; - while (is->offset < offset) { - size_t length = sizeof(buffer); - if (offset - is->offset < (goffset)length) - length = offset - is->offset; - - size_t nbytes = decoder_read(decoder, is, buffer, length); - if (nbytes == 0) - return false; - } - - assert(is->offset == offset); - return true; -} - -/** - * Skip some bytes from the #input_stream. - */ -bool -dsdlib_skip(struct decoder *decoder, struct input_stream *is, - goffset delta) -{ - assert(delta >= 0); - - if (delta == 0) - return true; - - if (is->seekable) - return input_stream_seek(is, delta, SEEK_CUR, NULL); - - char buffer[8192]; - while (delta > 0) { - size_t length = sizeof(buffer); - if ((goffset)length > delta) - length = delta; - - size_t nbytes = decoder_read(decoder, is, buffer, length); - if (nbytes == 0) - return false; - - delta -= nbytes; - } - - return true; -} - diff --git a/src/decoder/dsdlib.h b/src/decoder/dsdlib.h deleted file mode 100644 index d9675f5f..00000000 --- a/src/decoder/dsdlib.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_DECODER_DSDLIB_H -#define MPD_DECODER_DSDLIB_H - -struct dsdlib_id { - char value[4]; -}; - -bool -dsdlib_id_equals(const struct dsdlib_id *id, const char *s); - -bool -dsdlib_read(struct decoder *decoder, struct input_stream *is, - void *data, size_t length); - -bool -dsdlib_skip_to(struct decoder *decoder, struct input_stream *is, - goffset offset); - -bool -dsdlib_skip(struct decoder *decoder, struct input_stream *is, - goffset delta); - -#endif diff --git a/src/decoder/dsf_decoder_plugin.c b/src/decoder/dsf_decoder_plugin.c deleted file mode 100644 index c0107eb3..00000000 --- a/src/decoder/dsf_decoder_plugin.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (C) 2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* \file - * - * This plugin decodes DSDIFF data (SACD) embedded in DSF files. - * - * The DSF code was created using the specification found here: - * http://dsd-guide.com/sonys-dsf-file-format-spec - * - * All functions common to both DSD decoders have been moved to dsdlib - */ - -#include "config.h" -#include "dsf_decoder_plugin.h" -#include "decoder_api.h" -#include "audio_check.h" -#include "util/bit_reverse.h" -#include "dsdlib.h" -#include "tag_handler.h" - -#include <unistd.h> -#include <stdio.h> /* for SEEK_SET, SEEK_CUR */ - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "dsf" - -struct dsf_metadata { - unsigned sample_rate, channels; - bool bitreverse; - uint64_t chunk_size; -}; - -struct dsf_header { - /** DSF header id: "DSD " */ - struct dsdlib_id id; - /** DSD chunk size, including id = 28 */ - uint32_t size_low, size_high; - /** total file size */ - uint32_t fsize_low, fsize_high; - /** pointer to id3v2 metadata, should be at the end of the file */ - uint32_t pmeta_low, pmeta_high; -}; -/** DSF file fmt chunk */ -struct dsf_fmt_chunk { - - /** id: "fmt " */ - struct dsdlib_id id; - /** fmt chunk size, including id, normally 52 */ - uint32_t size_low, size_high; - /** version of this format = 1 */ - uint32_t version; - /** 0: DSD raw */ - uint32_t formatid; - /** channel type, 1 = mono, 2 = stereo, 3 = 3 channels, etc */ - uint32_t channeltype; - /** Channel number, 1 = mono, 2 = stereo, ... 6 = 6 channels */ - uint32_t channelnum; - /** sample frequency: 2822400, 5644800 */ - uint32_t sample_freq; - /** bits per sample 1 or 8 */ - uint32_t bitssample; - /** Sample count per channel in bytes */ - uint32_t scnt_low, scnt_high; - /** block size per channel = 4096 */ - uint32_t block_size; - /** reserved, should be all zero */ - uint32_t reserved; -}; - -struct dsf_data_chunk { - struct dsdlib_id id; - /** "data" chunk size, includes header (id+size) */ - uint32_t size_low, size_high; -}; - -/** - * Read and parse all needed metadata chunks for DSF files. - */ -static bool -dsf_read_metadata(struct decoder *decoder, struct input_stream *is, - struct dsf_metadata *metadata) -{ - uint64_t chunk_size; - struct dsf_header dsf_header; - if (!dsdlib_read(decoder, is, &dsf_header, sizeof(dsf_header)) || - !dsdlib_id_equals(&dsf_header.id, "DSD ")) - return false; - - chunk_size = (((uint64_t)GUINT32_FROM_LE(dsf_header.size_high)) << 32) | - ((uint64_t)GUINT32_FROM_LE(dsf_header.size_low)); - - if (sizeof(dsf_header) != chunk_size) - return false; - - /* read the 'fmt ' chunk of the DSF file */ - struct dsf_fmt_chunk dsf_fmt_chunk; - if (!dsdlib_read(decoder, is, &dsf_fmt_chunk, sizeof(dsf_fmt_chunk)) || - !dsdlib_id_equals(&dsf_fmt_chunk.id, "fmt ")) - return false; - - uint64_t fmt_chunk_size; - fmt_chunk_size = (((uint64_t)GUINT32_FROM_LE(dsf_fmt_chunk.size_high)) << 32) | - ((uint64_t)GUINT32_FROM_LE(dsf_fmt_chunk.size_low)); - - if (fmt_chunk_size != sizeof(dsf_fmt_chunk)) - return false; - - uint32_t samplefreq = (uint32_t)GUINT32_FROM_LE(dsf_fmt_chunk.sample_freq); - - /* for now, only support version 1 of the standard, DSD raw stereo - files with a sample freq of 2822400 Hz */ - - if (dsf_fmt_chunk.version != 1 || dsf_fmt_chunk.formatid != 0 - || dsf_fmt_chunk.channeltype != 2 - || dsf_fmt_chunk.channelnum != 2 - || samplefreq != 2822400) - return false; - - uint32_t chblksize = (uint32_t)GUINT32_FROM_LE(dsf_fmt_chunk.block_size); - /* according to the spec block size should always be 4096 */ - if (chblksize != 4096) - return false; - - /* read the 'data' chunk of the DSF file */ - struct dsf_data_chunk data_chunk; - if (!dsdlib_read(decoder, is, &data_chunk, sizeof(data_chunk)) || - !dsdlib_id_equals(&data_chunk.id, "data")) - return false; - - /* data size of DSF files are padded to multiple of 4096, - we use the actual data size as chunk size */ - - uint64_t data_size; - data_size = (((uint64_t)GUINT32_FROM_LE(data_chunk.size_high)) << 32) | - ((uint64_t)GUINT32_FROM_LE(data_chunk.size_low)); - data_size -= sizeof(data_chunk); - - metadata->chunk_size = data_size; - metadata->channels = (unsigned) dsf_fmt_chunk.channelnum; - metadata->sample_rate = samplefreq; - - /* check bits per sample format, determine if bitreverse is needed */ - metadata->bitreverse = dsf_fmt_chunk.bitssample == 1; - return true; -} - -static void -bit_reverse_buffer(uint8_t *p, uint8_t *end) -{ - for (; p < end; ++p) - *p = bit_reverse(*p); -} - -/** - * DSF data is build up of alternating 4096 blocks of DSD samples for left and - * right. Convert the buffer holding 1 block of 4096 DSD left samples and 1 - * block of 4096 DSD right samples to 8k of samples in normal PCM left/right - * order. - */ -static void -dsf_to_pcm_order(uint8_t *dest, uint8_t *scratch, size_t nrbytes) -{ - for (unsigned i = 0, j = 0; i < (unsigned)nrbytes; i += 2) { - scratch[i] = *(dest+j); - j++; - } - - for (unsigned i = 1, j = 0; i < (unsigned) nrbytes; i += 2) { - scratch[i] = *(dest+4096+j); - j++; - } - - for (unsigned i = 0; i < (unsigned)nrbytes; i++) { - *dest = scratch[i]; - dest++; - } -} - -/** - * Decode one complete DSF 'data' chunk i.e. a complete song - */ -static bool -dsf_decode_chunk(struct decoder *decoder, struct input_stream *is, - unsigned channels, - uint64_t chunk_size, - bool bitreverse) -{ - uint8_t buffer[8192]; - - /* scratch buffer for DSF samples to convert to the needed - normal left/right regime of samples */ - uint8_t dsf_scratch_buffer[8192]; - - const size_t sample_size = sizeof(buffer[0]); - const size_t frame_size = channels * sample_size; - const unsigned buffer_frames = sizeof(buffer) / frame_size; - const unsigned buffer_samples = buffer_frames * frame_size; - const size_t buffer_size = buffer_samples * sample_size; - - while (chunk_size > 0) { - /* see how much aligned data from the remaining chunk - fits into the local buffer */ - unsigned now_frames = buffer_frames; - size_t now_size = buffer_size; - if (chunk_size < (uint64_t)now_size) { - now_frames = (unsigned)chunk_size / frame_size; - now_size = now_frames * frame_size; - } - - size_t nbytes = decoder_read(decoder, is, buffer, now_size); - if (nbytes != now_size) - return false; - - chunk_size -= nbytes; - - if (bitreverse) - bit_reverse_buffer(buffer, buffer + nbytes); - - dsf_to_pcm_order(buffer, dsf_scratch_buffer, nbytes); - - enum decoder_command cmd = - decoder_data(decoder, is, buffer, nbytes, 0); - switch (cmd) { - case DECODE_COMMAND_NONE: - break; - - case DECODE_COMMAND_START: - case DECODE_COMMAND_STOP: - return false; - - case DECODE_COMMAND_SEEK: - - /* not implemented yet */ - decoder_seek_error(decoder); - break; - } - } - return dsdlib_skip(decoder, is, chunk_size); -} - -static void -dsf_stream_decode(struct decoder *decoder, struct input_stream *is) -{ - struct dsf_metadata metadata = { - .sample_rate = 0, - .channels = 0, - }; - - /* check if it is a proper DSF file */ - if (!dsf_read_metadata(decoder, is, &metadata)) - return; - - GError *error = NULL; - struct audio_format audio_format; - if (!audio_format_init_checked(&audio_format, metadata.sample_rate / 8, - SAMPLE_FORMAT_DSD, - metadata.channels, &error)) { - g_warning("%s", error->message); - g_error_free(error); - return; - } - /* Calculate song time from DSD chunk size and sample frequency */ - uint64_t chunk_size = metadata.chunk_size; - float songtime = ((chunk_size / metadata.channels) * 8) / - (float) metadata.sample_rate; - - /* success: file was recognized */ - decoder_initialized(decoder, &audio_format, false, songtime); - - if (!dsf_decode_chunk(decoder, is, metadata.channels, - metadata.chunk_size, - metadata.bitreverse)) - return; -} - -static bool -dsf_scan_stream(struct input_stream *is, - G_GNUC_UNUSED const struct tag_handler *handler, - G_GNUC_UNUSED void *handler_ctx) -{ - struct dsf_metadata metadata = { - .sample_rate = 0, - .channels = 0, - }; - - /* check DSF metadata */ - if (!dsf_read_metadata(NULL, is, &metadata)) - return false; - - struct audio_format audio_format; - if (!audio_format_init_checked(&audio_format, metadata.sample_rate / 8, - SAMPLE_FORMAT_DSD, - metadata.channels, NULL)) - /* refuse to parse files which we cannot play anyway */ - return false; - - /* calculate song time and add as tag */ - unsigned songtime = ((metadata.chunk_size / metadata.channels) * 8) / - metadata.sample_rate; - tag_handler_invoke_duration(handler, handler_ctx, songtime); - - return true; -} - -static const char *const dsf_suffixes[] = { - "dsf", - NULL -}; - -static const char *const dsf_mime_types[] = { - "application/x-dsf", - NULL -}; - -const struct decoder_plugin dsf_decoder_plugin = { - .name = "dsf", - .stream_decode = dsf_stream_decode, - .scan_stream = dsf_scan_stream, - .suffixes = dsf_suffixes, - .mime_types = dsf_mime_types, -}; diff --git a/src/decoder/dsf_decoder_plugin.h b/src/decoder/dsf_decoder_plugin.h deleted file mode 100644 index 401d3fed..00000000 --- a/src/decoder/dsf_decoder_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_DECODER_DSF_H -#define MPD_DECODER_DSF_H - -extern const struct decoder_plugin dsf_decoder_plugin; - -#endif diff --git a/src/decoder/faad_decoder_plugin.c b/src/decoder/faad_decoder_plugin.c deleted file mode 100644 index 911f033b..00000000 --- a/src/decoder/faad_decoder_plugin.c +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "decoder_api.h" -#include "decoder_buffer.h" -#include "audio_check.h" -#include "tag_handler.h" - -#define AAC_MAX_CHANNELS 6 - -#include <assert.h> -#include <unistd.h> -#include <faad.h> -#include <glib.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "faad" - -static const unsigned adts_sample_rates[] = - { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, - 16000, 12000, 11025, 8000, 7350, 0, 0, 0 -}; - -/** - * The GLib quark used for errors reported by this plugin. - */ -static inline GQuark -faad_decoder_quark(void) -{ - return g_quark_from_static_string("faad"); -} - -/** - * Check whether the buffer head is an AAC frame, and return the frame - * length. Returns 0 if it is not a frame. - */ -static size_t -adts_check_frame(const unsigned char *data) -{ - /* check syncword */ - if (!((data[0] == 0xFF) && ((data[1] & 0xF6) == 0xF0))) - return 0; - - return (((unsigned int)data[3] & 0x3) << 11) | - (((unsigned int)data[4]) << 3) | - (data[5] >> 5); -} - -/** - * Find the next AAC frame in the buffer. Returns 0 if no frame is - * found or if not enough data is available. - */ -static size_t -adts_find_frame(struct decoder_buffer *buffer) -{ - const unsigned char *data, *p; - size_t length, frame_length; - bool ret; - - while (true) { - data = decoder_buffer_read(buffer, &length); - if (data == NULL || length < 8) { - /* not enough data yet */ - ret = decoder_buffer_fill(buffer); - if (!ret) - /* failed */ - return 0; - - continue; - } - - /* find the 0xff marker */ - p = memchr(data, 0xff, length); - if (p == NULL) { - /* no marker - discard the buffer */ - decoder_buffer_consume(buffer, length); - continue; - } - - if (p > data) { - /* discard data before 0xff */ - decoder_buffer_consume(buffer, p - data); - continue; - } - - /* is it a frame? */ - frame_length = adts_check_frame(data); - if (frame_length == 0) { - /* it's just some random 0xff byte; discard it - and continue searching */ - decoder_buffer_consume(buffer, 1); - continue; - } - - if (length < frame_length) { - /* available buffer size is smaller than the - frame will be - attempt to read more - data */ - ret = decoder_buffer_fill(buffer); - if (!ret) { - /* not enough data; discard this frame - to prevent a possible buffer - overflow */ - data = decoder_buffer_read(buffer, &length); - if (data != NULL) - decoder_buffer_consume(buffer, length); - } - - continue; - } - - /* found a full frame! */ - return frame_length; - } -} - -static float -adts_song_duration(struct decoder_buffer *buffer) -{ - unsigned int frames, frame_length; - unsigned sample_rate = 0; - float frames_per_second; - - /* Read all frames to ensure correct time and bitrate */ - for (frames = 0;; frames++) { - frame_length = adts_find_frame(buffer); - if (frame_length == 0) - break; - - - if (frames == 0) { - const unsigned char *data; - size_t buffer_length; - - data = decoder_buffer_read(buffer, &buffer_length); - assert(data != NULL); - assert(frame_length <= buffer_length); - - sample_rate = adts_sample_rates[(data[2] & 0x3c) >> 2]; - } - - decoder_buffer_consume(buffer, frame_length); - } - - frames_per_second = (float)sample_rate / 1024.0; - if (frames_per_second <= 0) - return -1; - - return (float)frames / frames_per_second; -} - -static float -faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is) -{ - size_t fileread; - size_t tagsize; - const unsigned char *data; - size_t length; - bool success; - - fileread = is->size >= 0 ? is->size : 0; - - decoder_buffer_fill(buffer); - data = decoder_buffer_read(buffer, &length); - if (data == NULL) - return -1; - - tagsize = 0; - if (length >= 10 && !memcmp(data, "ID3", 3)) { - /* skip the ID3 tag */ - - tagsize = (data[6] << 21) | (data[7] << 14) | - (data[8] << 7) | (data[9] << 0); - - tagsize += 10; - - success = decoder_buffer_skip(buffer, tagsize) && - decoder_buffer_fill(buffer); - if (!success) - return -1; - - data = decoder_buffer_read(buffer, &length); - if (data == NULL) - return -1; - } - - if (is->seekable && length >= 2 && - data[0] == 0xFF && ((data[1] & 0xF6) == 0xF0)) { - /* obtain the duration from the ADTS header */ - float song_length = adts_song_duration(buffer); - - input_stream_lock_seek(is, tagsize, SEEK_SET, NULL); - - data = decoder_buffer_read(buffer, &length); - if (data != NULL) - decoder_buffer_consume(buffer, length); - decoder_buffer_fill(buffer); - - return song_length; - } else if (length >= 5 && memcmp(data, "ADIF", 4) == 0) { - /* obtain the duration from the ADIF header */ - unsigned bit_rate; - size_t skip_size = (data[4] & 0x80) ? 9 : 0; - - if (8 + skip_size > length) - /* not enough data yet; skip parsing this - header */ - return -1; - - bit_rate = ((data[4 + skip_size] & 0x0F) << 19) | - (data[5 + skip_size] << 11) | - (data[6 + skip_size] << 3) | - (data[7 + skip_size] & 0xE0); - - if (fileread != 0 && bit_rate != 0) - return fileread * 8.0 / bit_rate; - else - return fileread; - } else - return -1; -} - -/** - * Wrapper for faacDecInit() which works around some API - * inconsistencies in libfaad. - */ -static bool -faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer, - struct audio_format *audio_format, GError **error_r) -{ - union { - /* deconst hack for libfaad */ - const void *in; - void *out; - } u; - size_t length; - int32_t nbytes; - uint32_t sample_rate; - uint8_t channels; -#ifdef HAVE_FAAD_LONG - /* neaacdec.h declares all arguments as "unsigned long", but - internally expects uint32_t pointers. To avoid gcc - warnings, use this workaround. */ - unsigned long *sample_rate_p = (unsigned long *)(void *)&sample_rate; -#else - uint32_t *sample_rate_p = &sample_rate; -#endif - - u.in = decoder_buffer_read(buffer, &length); - if (u.in == NULL) { - g_set_error(error_r, faad_decoder_quark(), 0, - "Empty file"); - return false; - } - - nbytes = faacDecInit(decoder, u.out, -#ifdef HAVE_FAAD_BUFLEN_FUNCS - length, -#endif - sample_rate_p, &channels); - if (nbytes < 0) { - g_set_error(error_r, faad_decoder_quark(), 0, - "Not an AAC stream"); - return false; - } - - decoder_buffer_consume(buffer, nbytes); - - return audio_format_init_checked(audio_format, sample_rate, - SAMPLE_FORMAT_S16, channels, error_r); -} - -/** - * Wrapper for faacDecDecode() which works around some API - * inconsistencies in libfaad. - */ -static const void * -faad_decoder_decode(faacDecHandle decoder, struct decoder_buffer *buffer, - faacDecFrameInfo *frame_info) -{ - union { - /* deconst hack for libfaad */ - const void *in; - void *out; - } u; - size_t length; - void *result; - - u.in = decoder_buffer_read(buffer, &length); - if (u.in == NULL) - return NULL; - - result = faacDecDecode(decoder, frame_info, - u.out -#ifdef HAVE_FAAD_BUFLEN_FUNCS - , length -#endif - ); - - return result; -} - -/** - * Get a song file's total playing time in seconds, as a float. - * Returns 0 if the duration is unknown, and a negative value if the - * file is invalid. - */ -static float -faad_get_file_time_float(struct input_stream *is) -{ - struct decoder_buffer *buffer; - float length; - faacDecHandle decoder; - faacDecConfigurationPtr config; - - buffer = decoder_buffer_new(NULL, is, - FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS); - length = faad_song_duration(buffer, is); - - if (length < 0) { - bool ret; - struct audio_format audio_format; - - decoder = faacDecOpen(); - - config = faacDecGetCurrentConfiguration(decoder); - config->outputFormat = FAAD_FMT_16BIT; - faacDecSetConfiguration(decoder, config); - - decoder_buffer_fill(buffer); - - ret = faad_decoder_init(decoder, buffer, &audio_format, NULL); - if (ret) - length = 0; - - faacDecClose(decoder); - } - - decoder_buffer_free(buffer); - - return length; -} - -/** - * Get a song file's total playing time in seconds, as an int. - * Returns 0 if the duration is unknown, and a negative value if the - * file is invalid. - */ -static int -faad_get_file_time(struct input_stream *is) -{ - int file_time = -1; - float length; - - if ((length = faad_get_file_time_float(is)) >= 0) - file_time = length + 0.5; - - return file_time; -} - -static void -faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is) -{ - GError *error = NULL; - float total_time = 0; - faacDecHandle decoder; - struct audio_format audio_format; - faacDecConfigurationPtr config; - bool ret; - uint16_t bit_rate = 0; - struct decoder_buffer *buffer; - enum decoder_command cmd; - - buffer = decoder_buffer_new(mpd_decoder, is, - FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS); - total_time = faad_song_duration(buffer, is); - - /* create the libfaad decoder */ - - decoder = faacDecOpen(); - - config = faacDecGetCurrentConfiguration(decoder); - config->outputFormat = FAAD_FMT_16BIT; -#ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX - config->downMatrix = 1; -#endif -#ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR - config->dontUpSampleImplicitSBR = 0; -#endif - faacDecSetConfiguration(decoder, config); - - while (!decoder_buffer_is_full(buffer) && - !input_stream_lock_eof(is) && - decoder_get_command(mpd_decoder) == DECODE_COMMAND_NONE) { - adts_find_frame(buffer); - decoder_buffer_fill(buffer); - } - - /* initialize it */ - - ret = faad_decoder_init(decoder, buffer, &audio_format, &error); - if (!ret) { - g_warning("%s", error->message); - g_error_free(error); - faacDecClose(decoder); - return; - } - - /* initialize the MPD core */ - - decoder_initialized(mpd_decoder, &audio_format, false, total_time); - - /* the decoder loop */ - - do { - size_t frame_size; - const void *decoded; - faacDecFrameInfo frame_info; - - /* find the next frame */ - - frame_size = adts_find_frame(buffer); - if (frame_size == 0) - /* end of file */ - break; - - /* decode it */ - - decoded = faad_decoder_decode(decoder, buffer, &frame_info); - - if (frame_info.error > 0) { - g_warning("error decoding AAC stream: %s\n", - faacDecGetErrorMessage(frame_info.error)); - break; - } - - if (frame_info.channels != audio_format.channels) { - g_warning("channel count changed from %u to %u", - audio_format.channels, frame_info.channels); - break; - } - -#ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE - if (frame_info.samplerate != audio_format.sample_rate) { - g_warning("sample rate changed from %u to %lu", - audio_format.sample_rate, - (unsigned long)frame_info.samplerate); - break; - } -#endif - - decoder_buffer_consume(buffer, frame_info.bytesconsumed); - - /* update bit rate and position */ - - if (frame_info.samples > 0) { - bit_rate = frame_info.bytesconsumed * 8.0 * - frame_info.channels * audio_format.sample_rate / - frame_info.samples / 1000 + 0.5; - } - - /* send PCM samples to MPD */ - - cmd = decoder_data(mpd_decoder, is, decoded, - (size_t)frame_info.samples * 2, - bit_rate); - } while (cmd != DECODE_COMMAND_STOP); - - /* cleanup */ - - faacDecClose(decoder); -} - -static bool -faad_scan_stream(struct input_stream *is, - const struct tag_handler *handler, void *handler_ctx) -{ - int file_time = faad_get_file_time(is); - - if (file_time < 0) - return false; - - tag_handler_invoke_duration(handler, handler_ctx, file_time); - return true; -} - -static const char *const faad_suffixes[] = { "aac", NULL }; -static const char *const faad_mime_types[] = { - "audio/aac", "audio/aacp", NULL -}; - -const struct decoder_plugin faad_decoder_plugin = { - .name = "faad", - .stream_decode = faad_stream_decode, - .scan_stream = faad_scan_stream, - .suffixes = faad_suffixes, - .mime_types = faad_mime_types, -}; diff --git a/src/decoder/flac_compat.h b/src/decoder/flac_compat.h deleted file mode 100644 index 9a30acc2..00000000 --- a/src/decoder/flac_compat.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* - * Common data structures and functions used by FLAC and OggFLAC - */ - -#ifndef MPD_FLAC_COMPAT_H -#define MPD_FLAC_COMPAT_H - -#include <FLAC/export.h> -#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7 -# include <FLAC/seekable_stream_decoder.h> - -/* starting with libFLAC 1.1.3, the SeekableStreamDecoder has been - merged into the StreamDecoder. The following macros try to emulate - the new API for libFLAC 1.1.2 by mapping MPD's StreamDecoder calls - to the old SeekableStreamDecoder API. */ - -#define FLAC__StreamDecoder FLAC__SeekableStreamDecoder -#define FLAC__stream_decoder_new FLAC__seekable_stream_decoder_new -#define FLAC__stream_decoder_get_decode_position FLAC__seekable_stream_decoder_get_decode_position -#define FLAC__stream_decoder_get_state FLAC__seekable_stream_decoder_get_state -#define FLAC__stream_decoder_process_single FLAC__seekable_stream_decoder_process_single -#define FLAC__stream_decoder_process_until_end_of_metadata FLAC__seekable_stream_decoder_process_until_end_of_metadata -#define FLAC__stream_decoder_seek_absolute FLAC__seekable_stream_decoder_seek_absolute -#define FLAC__stream_decoder_finish FLAC__seekable_stream_decoder_finish -#define FLAC__stream_decoder_delete FLAC__seekable_stream_decoder_delete - -#define FLAC__STREAM_DECODER_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM - -typedef unsigned flac_read_status_size_t; - -#define FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus -#define FLAC__STREAM_DECODER_READ_STATUS_CONTINUE FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK -#define FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK -#define FLAC__STREAM_DECODER_READ_STATUS_ABORT FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR - -#define FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus -#define FLAC__STREAM_DECODER_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK -#define FLAC__STREAM_DECODER_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR -#define FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR - -#define FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus -#define FLAC__STREAM_DECODER_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK -#define FLAC__STREAM_DECODER_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR -#define FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR - -#define FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus -#define FLAC__STREAM_DECODER_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK -#define FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR -#define FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR - -typedef enum { - FLAC__STREAM_DECODER_INIT_STATUS_OK, - FLAC__STREAM_DECODER_INIT_STATUS_ERROR, -} FLAC__StreamDecoderInitStatus; - -static inline FLAC__StreamDecoderInitStatus -FLAC__stream_decoder_init_stream(FLAC__SeekableStreamDecoder *decoder, - FLAC__SeekableStreamDecoderReadCallback read_cb, - FLAC__SeekableStreamDecoderSeekCallback seek_cb, - FLAC__SeekableStreamDecoderTellCallback tell_cb, - FLAC__SeekableStreamDecoderLengthCallback length_cb, - FLAC__SeekableStreamDecoderEofCallback eof_cb, - FLAC__SeekableStreamDecoderWriteCallback write_cb, - FLAC__SeekableStreamDecoderMetadataCallback metadata_cb, - FLAC__SeekableStreamDecoderErrorCallback error_cb, - void *data) -{ - return FLAC__seekable_stream_decoder_set_read_callback(decoder, read_cb) && - FLAC__seekable_stream_decoder_set_seek_callback(decoder, seek_cb) && - FLAC__seekable_stream_decoder_set_tell_callback(decoder, tell_cb) && - FLAC__seekable_stream_decoder_set_length_callback(decoder, length_cb) && - FLAC__seekable_stream_decoder_set_eof_callback(decoder, eof_cb) && - FLAC__seekable_stream_decoder_set_write_callback(decoder, write_cb) && - FLAC__seekable_stream_decoder_set_metadata_callback(decoder, metadata_cb) && - FLAC__seekable_stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT) && - FLAC__seekable_stream_decoder_set_error_callback(decoder, error_cb) && - FLAC__seekable_stream_decoder_set_client_data(decoder, data) && - FLAC__seekable_stream_decoder_init(decoder) == FLAC__SEEKABLE_STREAM_DECODER_OK - ? FLAC__STREAM_DECODER_INIT_STATUS_OK - : FLAC__STREAM_DECODER_INIT_STATUS_ERROR; -} - -#else /* FLAC_API_VERSION_CURRENT > 7 */ - -# include <FLAC/stream_decoder.h> - -# define flac_init(a,b,c,d,e,f,g,h,i,j) \ - (FLAC__stream_decoder_init_stream(a,b,c,d,e,f,g,h,i,j) \ - == FLAC__STREAM_DECODER_INIT_STATUS_OK) - -typedef size_t flac_read_status_size_t; - -#endif /* FLAC_API_VERSION_CURRENT >= 7 */ - -#endif /* _FLAC_COMMON_H */ diff --git a/src/decoder/flac_decoder_plugin.c b/src/decoder/flac_decoder_plugin.c deleted file mode 100644 index fb0b3502..00000000 --- a/src/decoder/flac_decoder_plugin.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" /* must be first for large file support */ -#include "_flac_common.h" -#include "flac_compat.h" -#include "flac_metadata.h" - -#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7 -#include "_ogg_common.h" -#endif - -#include <glib.h> - -#include <assert.h> -#include <unistd.h> - -#include <sys/stat.h> -#include <sys/types.h> - -/* this code was based on flac123, from flac-tools */ - -static FLAC__StreamDecoderReadStatus -flac_read_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd, - FLAC__byte buf[], flac_read_status_size_t *bytes, - void *fdata) -{ - struct flac_data *data = fdata; - size_t r; - - r = decoder_read(data->decoder, data->input_stream, - (void *)buf, *bytes); - *bytes = r; - - if (r == 0) { - if (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE || - input_stream_lock_eof(data->input_stream)) - return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; - else - return FLAC__STREAM_DECODER_READ_STATUS_ABORT; - } - - return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; -} - -static FLAC__StreamDecoderSeekStatus -flac_seek_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd, - FLAC__uint64 offset, void *fdata) -{ - struct flac_data *data = (struct flac_data *) fdata; - - if (!data->input_stream->seekable) - return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; - - if (!input_stream_lock_seek(data->input_stream, offset, SEEK_SET, - NULL)) - return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; - - return FLAC__STREAM_DECODER_SEEK_STATUS_OK; -} - -static FLAC__StreamDecoderTellStatus -flac_tell_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd, - FLAC__uint64 * offset, void *fdata) -{ - struct flac_data *data = (struct flac_data *) fdata; - - if (!data->input_stream->seekable) - return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; - - *offset = (long)(data->input_stream->offset); - - return FLAC__STREAM_DECODER_TELL_STATUS_OK; -} - -static FLAC__StreamDecoderLengthStatus -flac_length_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd, - FLAC__uint64 * length, void *fdata) -{ - struct flac_data *data = (struct flac_data *) fdata; - - if (data->input_stream->size < 0) - return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; - - *length = (size_t) (data->input_stream->size); - - return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; -} - -static FLAC__bool -flac_eof_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd, void *fdata) -{ - struct flac_data *data = (struct flac_data *) fdata; - - return (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE && - decoder_get_command(data->decoder) != DECODE_COMMAND_SEEK) || - input_stream_lock_eof(data->input_stream); -} - -static void -flac_error_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd, - FLAC__StreamDecoderErrorStatus status, void *fdata) -{ - flac_error_common_cb(status, (struct flac_data *) fdata); -} - -#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7 -static void flacPrintErroredState(FLAC__SeekableStreamDecoderState state) -{ - switch (state) { - case FLAC__SEEKABLE_STREAM_DECODER_OK: - case FLAC__SEEKABLE_STREAM_DECODER_SEEKING: - case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM: - return; - - case FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR: - case FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR: - case FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR: - case FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR: - case FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED: - case FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK: - case FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED: - break; - } - - g_warning("%s\n", FLAC__SeekableStreamDecoderStateString[state]); -} -#else /* FLAC_API_VERSION_CURRENT >= 7 */ -static void flacPrintErroredState(FLAC__StreamDecoderState state) -{ - switch (state) { - case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: - case FLAC__STREAM_DECODER_READ_METADATA: - case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: - case FLAC__STREAM_DECODER_READ_FRAME: - case FLAC__STREAM_DECODER_END_OF_STREAM: - return; - - case FLAC__STREAM_DECODER_OGG_ERROR: - case FLAC__STREAM_DECODER_SEEK_ERROR: - case FLAC__STREAM_DECODER_ABORTED: - case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR: - case FLAC__STREAM_DECODER_UNINITIALIZED: - break; - } - - g_warning("%s\n", FLAC__StreamDecoderStateString[state]); -} -#endif /* FLAC_API_VERSION_CURRENT >= 7 */ - -static void flacMetadata(G_GNUC_UNUSED const FLAC__StreamDecoder * dec, - const FLAC__StreamMetadata * block, void *vdata) -{ - flac_metadata_common_cb(block, (struct flac_data *) vdata); -} - -static FLAC__StreamDecoderWriteStatus -flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame, - const FLAC__int32 *const buf[], void *vdata) -{ - struct flac_data *data = (struct flac_data *) vdata; - FLAC__uint64 nbytes = 0; - - if (FLAC__stream_decoder_get_decode_position(dec, &nbytes)) { - if (data->position > 0 && nbytes > data->position) { - nbytes -= data->position; - data->position += nbytes; - } else { - data->position = nbytes; - nbytes = 0; - } - } else - nbytes = 0; - - return flac_common_write(data, frame, buf, nbytes); -} - -static bool -flac_scan_file(const char *file, - const struct tag_handler *handler, void *handler_ctx) -{ - return flac_scan_file2(file, NULL, handler, handler_ctx); -} - -/** - * Some glue code around FLAC__stream_decoder_new(). - */ -static FLAC__StreamDecoder * -flac_decoder_new(void) -{ - FLAC__StreamDecoder *sd = FLAC__stream_decoder_new(); - if (sd == NULL) { - g_warning("FLAC__stream_decoder_new() failed"); - return NULL; - } - -#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7 - if(!FLAC__stream_decoder_set_metadata_respond(sd, FLAC__METADATA_TYPE_VORBIS_COMMENT)) - g_debug("FLAC__stream_decoder_set_metadata_respond() has failed"); -#endif - - return sd; -} - -static bool -flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd, - FLAC__uint64 duration) -{ - data->total_frames = duration; - - if (!FLAC__stream_decoder_process_until_end_of_metadata(sd)) { - g_warning("problem reading metadata"); - return false; - } - - if (data->initialized) { - /* done */ - decoder_initialized(data->decoder, &data->audio_format, - data->input_stream->seekable, - (float)data->total_frames / - (float)data->audio_format.sample_rate); - return true; - } - - if (data->input_stream->seekable) - /* allow the workaround below only for nonseekable - streams*/ - return false; - - /* no stream_info packet found; try to initialize the decoder - from the first frame header */ - FLAC__stream_decoder_process_single(sd); - return data->initialized; -} - -static void -flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec, - FLAC__uint64 t_start, FLAC__uint64 t_end) -{ - struct decoder *decoder = data->decoder; - enum decoder_command cmd; - - data->first_frame = t_start; - - while (true) { - if (data->tag != NULL && !tag_is_empty(data->tag)) { - cmd = decoder_tag(data->decoder, data->input_stream, - data->tag); - tag_free(data->tag); - data->tag = tag_new(); - } else - cmd = decoder_get_command(decoder); - - if (cmd == DECODE_COMMAND_SEEK) { - FLAC__uint64 seek_sample = t_start + - decoder_seek_where(decoder) * - data->audio_format.sample_rate; - if (seek_sample >= t_start && - (t_end == 0 || seek_sample <= t_end) && - FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) { - data->next_frame = seek_sample; - data->position = 0; - decoder_command_finished(decoder); - } else - decoder_seek_error(decoder); - } else if (cmd == DECODE_COMMAND_STOP || - FLAC__stream_decoder_get_state(flac_dec) == FLAC__STREAM_DECODER_END_OF_STREAM) - break; - - if (t_end != 0 && data->next_frame >= t_end) - /* end of this sub track */ - break; - - if (!FLAC__stream_decoder_process_single(flac_dec) && - decoder_get_command(decoder) == DECODE_COMMAND_NONE) { - /* a failure that was not triggered by a - decoder command */ - flacPrintErroredState(FLAC__stream_decoder_get_state(flac_dec)); - break; - } - } -} - -static FLAC__StreamDecoderInitStatus -stream_init_oggflac(FLAC__StreamDecoder *flac_dec, struct flac_data *data) -{ -#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7 - return FLAC__stream_decoder_init_ogg_stream(flac_dec, - flac_read_cb, - flac_seek_cb, - flac_tell_cb, - flac_length_cb, - flac_eof_cb, - flac_write_cb, - flacMetadata, - flac_error_cb, - data); -#else - (void)flac_dec; - (void)data; - - return FLAC__STREAM_DECODER_INIT_STATUS_ERROR; -#endif -} - -static FLAC__StreamDecoderInitStatus -stream_init_flac(FLAC__StreamDecoder *flac_dec, struct flac_data *data) -{ - return FLAC__stream_decoder_init_stream(flac_dec, - flac_read_cb, flac_seek_cb, - flac_tell_cb, flac_length_cb, - flac_eof_cb, flac_write_cb, - flacMetadata, - flac_error_cb, - data); -} - -static FLAC__StreamDecoderInitStatus -stream_init(FLAC__StreamDecoder *flac_dec, struct flac_data *data, bool is_ogg) -{ - return is_ogg - ? stream_init_oggflac(flac_dec, data) - : stream_init_flac(flac_dec, data); -} - -static void -flac_decode_internal(struct decoder * decoder, - struct input_stream *input_stream, - bool is_ogg) -{ - FLAC__StreamDecoder *flac_dec; - struct flac_data data; - - flac_dec = flac_decoder_new(); - if (flac_dec == NULL) - return; - - flac_data_init(&data, decoder, input_stream); - data.tag = tag_new(); - - FLAC__StreamDecoderInitStatus status = - stream_init(flac_dec, &data, is_ogg); - if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { - flac_data_deinit(&data); - FLAC__stream_decoder_delete(flac_dec); -#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7 - g_warning("%s", FLAC__StreamDecoderInitStatusString[status]); -#endif - return; - } - - if (!flac_decoder_initialize(&data, flac_dec, 0)) { - flac_data_deinit(&data); - FLAC__stream_decoder_finish(flac_dec); - FLAC__stream_decoder_delete(flac_dec); - return; - } - - flac_decoder_loop(&data, flac_dec, 0, 0); - - flac_data_deinit(&data); - - FLAC__stream_decoder_finish(flac_dec); - FLAC__stream_decoder_delete(flac_dec); -} - -static void -flac_decode(struct decoder * decoder, struct input_stream *input_stream) -{ - flac_decode_internal(decoder, input_stream, false); -} - -#ifndef HAVE_OGGFLAC - -static bool -oggflac_init(G_GNUC_UNUSED const struct config_param *param) -{ -#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7 - return !!FLAC_API_SUPPORTS_OGG_FLAC; -#else - /* disable oggflac when libflac is too old */ - return false; -#endif -} - -#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7 - -static bool -oggflac_scan_file(const char *file, - const struct tag_handler *handler, void *handler_ctx) -{ - FLAC__Metadata_Iterator *it; - FLAC__StreamMetadata *block; - FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new(); - - if (!(FLAC__metadata_chain_read_ogg(chain, file))) { - FLAC__metadata_chain_delete(chain); - return false; - } - - it = FLAC__metadata_iterator_new(); - FLAC__metadata_iterator_init(it, chain); - - do { - if (!(block = FLAC__metadata_iterator_get_block(it))) - break; - - flac_scan_metadata(NULL, block, - handler, handler_ctx); - } while (FLAC__metadata_iterator_next(it)); - FLAC__metadata_iterator_delete(it); - - FLAC__metadata_chain_delete(chain); - return true; -} - -static void -oggflac_decode(struct decoder *decoder, struct input_stream *input_stream) -{ - if (ogg_stream_type_detect(input_stream) != FLAC) - return; - - /* rewind the stream, because ogg_stream_type_detect() has - moved it */ - input_stream_lock_seek(input_stream, 0, SEEK_SET, NULL); - - flac_decode_internal(decoder, input_stream, true); -} - -static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL }; -static const char *const oggflac_mime_types[] = { - "application/ogg", - "application/x-ogg", - "audio/ogg", - "audio/x-flac+ogg", - "audio/x-ogg", - NULL -}; - -#endif /* FLAC_API_VERSION_CURRENT >= 7 */ - -const struct decoder_plugin oggflac_decoder_plugin = { - .name = "oggflac", - .init = oggflac_init, -#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7 - .stream_decode = oggflac_decode, - .scan_file = oggflac_scan_file, - .suffixes = oggflac_suffixes, - .mime_types = oggflac_mime_types -#endif -}; - -#endif /* HAVE_OGGFLAC */ - -static const char *const flac_suffixes[] = { "flac", NULL }; -static const char *const flac_mime_types[] = { - "application/flac", - "application/x-flac", - "audio/flac", - "audio/x-flac", - NULL -}; - -const struct decoder_plugin flac_decoder_plugin = { - .name = "flac", - .stream_decode = flac_decode, - .scan_file = flac_scan_file, - .suffixes = flac_suffixes, - .mime_types = flac_mime_types, -}; diff --git a/src/decoder/flac_metadata.c b/src/decoder/flac_metadata.c deleted file mode 100644 index bd1eaf32..00000000 --- a/src/decoder/flac_metadata.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "flac_metadata.h" -#include "replay_gain_info.h" -#include "tag.h" -#include "tag_handler.h" -#include "tag_table.h" - -#include <glib.h> - -#include <assert.h> -#include <stdbool.h> -#include <stdlib.h> - -static bool -flac_find_float_comment(const FLAC__StreamMetadata *block, - const char *cmnt, float *fl) -{ - int offset; - size_t pos; - int len; - unsigned char tmp, *p; - - offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, - cmnt); - if (offset < 0) - return false; - - pos = strlen(cmnt) + 1; /* 1 is for '=' */ - len = block->data.vorbis_comment.comments[offset].length - pos; - if (len <= 0) - return false; - - p = &block->data.vorbis_comment.comments[offset].entry[pos]; - tmp = p[len]; - p[len] = '\0'; - *fl = (float)atof((char *)p); - p[len] = tmp; - - return true; -} - -bool -flac_parse_replay_gain(struct replay_gain_info *rgi, - const FLAC__StreamMetadata *block) -{ - bool found = false; - - replay_gain_info_init(rgi); - - if (flac_find_float_comment(block, "replaygain_album_gain", - &rgi->tuples[REPLAY_GAIN_ALBUM].gain)) - found = true; - if (flac_find_float_comment(block, "replaygain_album_peak", - &rgi->tuples[REPLAY_GAIN_ALBUM].peak)) - found = true; - if (flac_find_float_comment(block, "replaygain_track_gain", - &rgi->tuples[REPLAY_GAIN_TRACK].gain)) - found = true; - if (flac_find_float_comment(block, "replaygain_track_peak", - &rgi->tuples[REPLAY_GAIN_TRACK].peak)) - found = true; - - return found; -} - -static bool -flac_find_string_comment(const FLAC__StreamMetadata *block, - const char *cmnt, char **str) -{ - int offset; - size_t pos; - int len; - const unsigned char *p; - - *str = NULL; - offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, - cmnt); - if (offset < 0) - return false; - - pos = strlen(cmnt) + 1; /* 1 is for '=' */ - len = block->data.vorbis_comment.comments[offset].length - pos; - if (len <= 0) - return false; - - p = &block->data.vorbis_comment.comments[offset].entry[pos]; - *str = g_strndup((const char *)p, len); - - return true; -} - -bool -flac_parse_mixramp(char **mixramp_start, char **mixramp_end, - const FLAC__StreamMetadata *block) -{ - bool found = false; - - if (flac_find_string_comment(block, "mixramp_start", mixramp_start)) - found = true; - if (flac_find_string_comment(block, "mixramp_end", mixramp_end)) - found = true; - - return found; -} - -/** - * Checks if the specified name matches the entry's name, and if yes, - * returns the comment value (not null-temrinated). - */ -static const char * -flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, - const char *name, const char *char_tnum, size_t *length_r) -{ - size_t name_length = strlen(name); - size_t char_tnum_length = 0; - const char *comment = (const char*)entry->entry; - - if (entry->length <= name_length || - g_ascii_strncasecmp(comment, name, name_length) != 0) - return NULL; - - if (char_tnum != NULL) { - char_tnum_length = strlen(char_tnum); - if (entry->length > name_length + char_tnum_length + 2 && - comment[name_length] == '[' && - g_ascii_strncasecmp(comment + name_length + 1, - char_tnum, char_tnum_length) == 0 && - comment[name_length + char_tnum_length + 1] == ']') - name_length = name_length + char_tnum_length + 2; - else if (entry->length > name_length + char_tnum_length && - g_ascii_strncasecmp(comment + name_length, - char_tnum, char_tnum_length) == 0) - name_length = name_length + char_tnum_length; - } - - if (comment[name_length] == '=') { - *length_r = entry->length - name_length - 1; - return comment + name_length + 1; - } - - return NULL; -} - -/** - * Check if the comment's name equals the passed name, and if so, copy - * the comment value into the tag. - */ -static bool -flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry, - const char *name, enum tag_type tag_type, - const char *char_tnum, - const struct tag_handler *handler, void *handler_ctx) -{ - const char *value; - size_t value_length; - - value = flac_comment_value(entry, name, char_tnum, &value_length); - if (value != NULL) { - char *p = g_strndup(value, value_length); - tag_handler_invoke_tag(handler, handler_ctx, tag_type, p); - g_free(p); - return true; - } - - return false; -} - -static const struct tag_table flac_tags[] = { - { "tracknumber", TAG_TRACK }, - { "discnumber", TAG_DISC }, - { "album artist", TAG_ALBUM_ARTIST }, - { NULL, TAG_NUM_OF_ITEM_TYPES } -}; - -static void -flac_scan_comment(const char *char_tnum, - const FLAC__StreamMetadata_VorbisComment_Entry *entry, - const struct tag_handler *handler, void *handler_ctx) -{ - if (handler->pair != NULL) { - char *name = g_strdup((const char*)entry->entry); - char *value = strchr(name, '='); - - if (value != NULL && value > name) { - *value++ = 0; - tag_handler_invoke_pair(handler, handler_ctx, - name, value); - } - - g_free(name); - } - - for (const struct tag_table *i = flac_tags; i->name != NULL; ++i) - if (flac_copy_comment(entry, i->name, i->type, char_tnum, - handler, handler_ctx)) - return; - - for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) - if (flac_copy_comment(entry, - tag_item_names[i], i, char_tnum, - handler, handler_ctx)) - return; -} - -static void -flac_scan_comments(const char *char_tnum, - const FLAC__StreamMetadata_VorbisComment *comment, - const struct tag_handler *handler, void *handler_ctx) -{ - for (unsigned i = 0; i < comment->num_comments; ++i) - flac_scan_comment(char_tnum, &comment->comments[i], - handler, handler_ctx); -} - -void -flac_scan_metadata(const char *track, - const FLAC__StreamMetadata *block, - const struct tag_handler *handler, void *handler_ctx) -{ - switch (block->type) { - case FLAC__METADATA_TYPE_VORBIS_COMMENT: - flac_scan_comments(track, &block->data.vorbis_comment, - handler, handler_ctx); - break; - - case FLAC__METADATA_TYPE_STREAMINFO: - if (block->data.stream_info.sample_rate > 0) - tag_handler_invoke_duration(handler, handler_ctx, - flac_duration(&block->data.stream_info)); - break; - - default: - break; - } -} - -void -flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum, - const FLAC__StreamMetadata_VorbisComment *comment) -{ - flac_scan_comments(char_tnum, comment, - &add_tag_handler, tag); -} - -bool -flac_scan_file2(const char *file, const char *char_tnum, - const struct tag_handler *handler, void *handler_ctx) -{ - FLAC__Metadata_SimpleIterator *it; - FLAC__StreamMetadata *block = NULL; - - it = FLAC__metadata_simple_iterator_new(); - if (!FLAC__metadata_simple_iterator_init(it, file, 1, 0)) { - const char *err; - FLAC_API FLAC__Metadata_SimpleIteratorStatus s; - - s = FLAC__metadata_simple_iterator_status(it); - - switch (s) { /* slightly more human-friendly messages: */ - case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT: - err = "illegal input"; - break; - case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE: - err = "error opening file"; - break; - case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE: - err = "not a FLAC file"; - break; - default: - err = FLAC__Metadata_SimpleIteratorStatusString[s]; - } - g_debug("Reading '%s' metadata gave the following error: %s\n", - file, err); - FLAC__metadata_simple_iterator_delete(it); - return false; - } - - do { - block = FLAC__metadata_simple_iterator_get_block(it); - if (!block) - break; - - flac_scan_metadata(char_tnum, block, handler, handler_ctx); - FLAC__metadata_object_delete(block); - } while (FLAC__metadata_simple_iterator_next(it)); - - FLAC__metadata_simple_iterator_delete(it); - - return true; -} - -struct tag * -flac_tag_load(const char *file, const char *char_tnum) -{ - struct tag *tag = tag_new(); - - if (!flac_scan_file2(file, char_tnum, &add_tag_handler, tag) || - tag_is_empty(tag)) { - tag_free(tag); - tag = NULL; - } - - return tag; -} diff --git a/src/decoder/flac_metadata.h b/src/decoder/flac_metadata.h deleted file mode 100644 index 3c463d5d..00000000 --- a/src/decoder/flac_metadata.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_FLAC_METADATA_H -#define MPD_FLAC_METADATA_H - -#include <assert.h> -#include <stdbool.h> -#include <FLAC/metadata.h> - -struct tag_handler; -struct tag; -struct replay_gain_info; - -static inline unsigned -flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info) -{ - assert(stream_info->sample_rate > 0); - - return (stream_info->total_samples + stream_info->sample_rate - 1) / - stream_info->sample_rate; -} - -bool -flac_parse_replay_gain(struct replay_gain_info *rgi, - const FLAC__StreamMetadata *block); - -bool -flac_parse_mixramp(char **mixramp_start, char **mixramp_end, - const FLAC__StreamMetadata *block); - -void -flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum, - const FLAC__StreamMetadata_VorbisComment *comment); - -void -flac_scan_metadata(const char *track, - const FLAC__StreamMetadata *block, - const struct tag_handler *handler, void *handler_ctx); - -bool -flac_scan_file2(const char *file, const char *char_tnum, - const struct tag_handler *handler, void *handler_ctx); - -struct tag * -flac_tag_load(const char *file, const char *char_tnum); - -#endif diff --git a/src/decoder/flac_pcm.c b/src/decoder/flac_pcm.c deleted file mode 100644 index 6964d8ac..00000000 --- a/src/decoder/flac_pcm.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "flac_pcm.h" - -#include <assert.h> - -static void flac_convert_stereo16(int16_t *dest, - const FLAC__int32 * const buf[], - unsigned int position, unsigned int end) -{ - for (; position < end; ++position) { - *dest++ = buf[0][position]; - *dest++ = buf[1][position]; - } -} - -static void -flac_convert_16(int16_t *dest, - unsigned int num_channels, - const FLAC__int32 * const buf[], - unsigned int position, unsigned int end) -{ - unsigned int c_chan; - - for (; position < end; ++position) - for (c_chan = 0; c_chan < num_channels; c_chan++) - *dest++ = buf[c_chan][position]; -} - -/** - * Note: this function also handles 24 bit files! - */ -static void -flac_convert_32(int32_t *dest, - unsigned int num_channels, - const FLAC__int32 * const buf[], - unsigned int position, unsigned int end) -{ - unsigned int c_chan; - - for (; position < end; ++position) - for (c_chan = 0; c_chan < num_channels; c_chan++) - *dest++ = buf[c_chan][position]; -} - -static void -flac_convert_8(int8_t *dest, - unsigned int num_channels, - const FLAC__int32 * const buf[], - unsigned int position, unsigned int end) -{ - unsigned int c_chan; - - for (; position < end; ++position) - for (c_chan = 0; c_chan < num_channels; c_chan++) - *dest++ = buf[c_chan][position]; -} - -void -flac_convert(void *dest, - unsigned int num_channels, enum sample_format sample_format, - const FLAC__int32 *const buf[], - unsigned int position, unsigned int end) -{ - switch (sample_format) { - case SAMPLE_FORMAT_S16: - if (num_channels == 2) - flac_convert_stereo16((int16_t*)dest, buf, - position, end); - else - flac_convert_16((int16_t*)dest, num_channels, buf, - position, end); - break; - - case SAMPLE_FORMAT_S24_P32: - case SAMPLE_FORMAT_S32: - flac_convert_32((int32_t*)dest, num_channels, buf, - position, end); - break; - - case SAMPLE_FORMAT_S8: - flac_convert_8((int8_t*)dest, num_channels, buf, - position, end); - break; - - case SAMPLE_FORMAT_FLOAT: - case SAMPLE_FORMAT_DSD: - case SAMPLE_FORMAT_UNDEFINED: - /* unreachable */ - assert(false); - } -} diff --git a/src/decoder/flac_pcm.h b/src/decoder/flac_pcm.h deleted file mode 100644 index a931998c..00000000 --- a/src/decoder/flac_pcm.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_FLAC_PCM_H -#define MPD_FLAC_PCM_H - -#include "audio_format.h" - -#include <FLAC/ordinals.h> - -void -flac_convert(void *dest, - unsigned int num_channels, enum sample_format sample_format, - const FLAC__int32 *const buf[], - unsigned int position, unsigned int end); - -#endif diff --git a/src/decoder/fluidsynth_decoder_plugin.c b/src/decoder/fluidsynth_decoder_plugin.c deleted file mode 100644 index 085f84f1..00000000 --- a/src/decoder/fluidsynth_decoder_plugin.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* - * WARNING! This plugin suffers from major shortcomings in the - * libfluidsynth API, which render it practically unusable. For a - * discussion, see the post on the fluidsynth mailing list: - * - * http://www.mail-archive.com/fluid-dev@nongnu.org/msg01099.html - * - */ - -#include "config.h" -#include "decoder_api.h" -#include "timer.h" -#include "conf.h" - -#include <glib.h> - -#include <fluidsynth.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "fluidsynth" - -/** - * Convert a fluidsynth log level to a GLib log level. - */ -static GLogLevelFlags -fluidsynth_level_to_glib(enum fluid_log_level level) -{ - switch (level) { - case FLUID_PANIC: - case FLUID_ERR: - return G_LOG_LEVEL_CRITICAL; - - case FLUID_WARN: - return G_LOG_LEVEL_WARNING; - - case FLUID_INFO: - return G_LOG_LEVEL_INFO; - - case FLUID_DBG: - case LAST_LOG_LEVEL: - return G_LOG_LEVEL_DEBUG; - } - - /* invalid fluidsynth log level */ - return G_LOG_LEVEL_MESSAGE; -} - -/** - * The fluidsynth logging callback. It forwards messages to the GLib - * logging library. - */ -static void -fluidsynth_mpd_log_function(int level, char *message, G_GNUC_UNUSED void *data) -{ - g_log(G_LOG_DOMAIN, fluidsynth_level_to_glib(level), "%s", message); -} - -static bool -fluidsynth_init(G_GNUC_UNUSED const struct config_param *param) -{ - fluid_set_log_function(LAST_LOG_LEVEL, - fluidsynth_mpd_log_function, NULL); - - return true; -} - -static void -fluidsynth_file_decode(struct decoder *decoder, const char *path_fs) -{ - static const struct audio_format audio_format = { - .sample_rate = 48000, - .format = SAMPLE_FORMAT_S16, - .channels = 2, - }; - char setting_sample_rate[] = "synth.sample-rate"; - /* - char setting_verbose[] = "synth.verbose"; - char setting_yes[] = "yes"; - */ - const char *soundfont_path; - fluid_settings_t *settings; - fluid_synth_t *synth; - fluid_player_t *player; - char *path_dup; - int ret; - struct timer *timer; - enum decoder_command cmd; - - soundfont_path = - config_get_string("soundfont", - "/usr/share/sounds/sf2/FluidR3_GM.sf2"); - - /* set up fluid settings */ - - settings = new_fluid_settings(); - if (settings == NULL) - return; - - fluid_settings_setnum(settings, setting_sample_rate, 48000); - - /* - fluid_settings_setstr(settings, setting_verbose, setting_yes); - */ - - /* create the fluid synth */ - - synth = new_fluid_synth(settings); - if (synth == NULL) { - delete_fluid_settings(settings); - return; - } - - ret = fluid_synth_sfload(synth, soundfont_path, true); - if (ret < 0) { - g_warning("fluid_synth_sfload() failed"); - delete_fluid_synth(synth); - delete_fluid_settings(settings); - return; - } - - /* create the fluid player */ - - player = new_fluid_player(synth); - if (player == NULL) { - delete_fluid_synth(synth); - delete_fluid_settings(settings); - return; - } - - /* temporarily duplicate the path_fs string, because - fluidsynth wants a writable string */ - path_dup = g_strdup(path_fs); - ret = fluid_player_add(player, path_dup); - g_free(path_dup); - if (ret != 0) { - g_warning("fluid_player_add() failed"); - delete_fluid_player(player); - delete_fluid_synth(synth); - delete_fluid_settings(settings); - return; - } - - /* start the player */ - - ret = fluid_player_play(player); - if (ret != 0) { - g_warning("fluid_player_play() failed"); - delete_fluid_player(player); - delete_fluid_synth(synth); - delete_fluid_settings(settings); - return; - } - - /* set up a timer for synchronization; fluidsynth always - decodes in real time, which forces us to synchronize */ - /* XXX is there any way to switch off real-time decoding? */ - - timer = timer_new(&audio_format); - timer_start(timer); - - /* initialization complete - announce the audio format to the - MPD core */ - - decoder_initialized(decoder, &audio_format, false, -1); - - do { - int16_t buffer[2048]; - const unsigned max_frames = G_N_ELEMENTS(buffer) / 2; - - /* synchronize with the fluid player */ - - timer_add(timer, sizeof(buffer)); - timer_sync(timer); - - /* read samples from fluidsynth and send them to the - MPD core */ - - ret = fluid_synth_write_s16(synth, max_frames, - buffer, 0, 2, - buffer, 1, 2); - /* XXX how do we see whether the player is done? We - can't access the private attribute - player->status */ - if (ret != 0) - break; - - cmd = decoder_data(decoder, NULL, buffer, sizeof(buffer), - 0); - } while (cmd == DECODE_COMMAND_NONE); - - /* clean up */ - - timer_free(timer); - - fluid_player_stop(player); - fluid_player_join(player); - - delete_fluid_player(player); - delete_fluid_synth(synth); - delete_fluid_settings(settings); -} - -static bool -fluidsynth_scan_file(const char *file, - G_GNUC_UNUSED const struct tag_handler *handler, - G_GNUC_UNUSED void *handler_ctx) -{ - /* to be implemented */ - (void)file; - - return true; -} - -static const char *const fluidsynth_suffixes[] = { - "mid", - NULL -}; - -const struct decoder_plugin fluidsynth_decoder_plugin = { - .name = "fluidsynth", - .init = fluidsynth_init, - .file_decode = fluidsynth_file_decode, - .scan_file = fluidsynth_scan_file, - .suffixes = fluidsynth_suffixes, -}; diff --git a/src/decoder/gme_decoder_plugin.c b/src/decoder/gme_decoder_plugin.c deleted file mode 100644 index 237a1deb..00000000 --- a/src/decoder/gme_decoder_plugin.c +++ /dev/null @@ -1,257 +0,0 @@ -#include "config.h" -#include "../decoder_api.h" -#include "audio_check.h" -#include "uri.h" -#include "tag_handler.h" - -#include <glib.h> -#include <assert.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> - -#include <gme/gme.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "gme" - -#define SUBTUNE_PREFIX "tune_" - -enum { - GME_SAMPLE_RATE = 44100, - GME_CHANNELS = 2, - GME_BUFFER_FRAMES = 2048, - GME_BUFFER_SAMPLES = GME_BUFFER_FRAMES * GME_CHANNELS, -}; - -/** - * returns the file path stripped of any /tune_xxx.* subtune - * suffix - */ -static char * -get_container_name(const char *path_fs) -{ - const char *subtune_suffix = uri_get_suffix(path_fs); - char *path_container = g_strdup(path_fs); - char *pat = g_strconcat("*/" SUBTUNE_PREFIX "???.", subtune_suffix, NULL); - GPatternSpec *path_with_subtune = g_pattern_spec_new(pat); - g_free(pat); - if (!g_pattern_match(path_with_subtune, - strlen(path_container), path_container, NULL)) { - g_pattern_spec_free(path_with_subtune); - return path_container; - } - - char *ptr = g_strrstr(path_container, "/" SUBTUNE_PREFIX); - if (ptr != NULL) - *ptr='\0'; - - g_pattern_spec_free(path_with_subtune); - return path_container; -} - -/** - * returns tune number from file.nsf/tune_xxx.* style path or 0 if no subtune - * is appended. - */ -static int -get_song_num(const char *path_fs) -{ - const char *subtune_suffix = uri_get_suffix(path_fs); - char *pat = g_strconcat("*/" SUBTUNE_PREFIX "???.", subtune_suffix, NULL); - GPatternSpec *path_with_subtune = g_pattern_spec_new(pat); - g_free(pat); - - if (g_pattern_match(path_with_subtune, - strlen(path_fs), path_fs, NULL)) { - char *sub = g_strrstr(path_fs, "/" SUBTUNE_PREFIX); - g_pattern_spec_free(path_with_subtune); - if(!sub) - return 0; - - sub += strlen("/" SUBTUNE_PREFIX); - int song_num = strtol(sub, NULL, 10); - - return song_num - 1; - } else { - g_pattern_spec_free(path_with_subtune); - return 0; - } -} - -static char * -gme_container_scan(const char *path_fs, const unsigned int tnum) -{ - Music_Emu *emu; - const char* gme_err; - unsigned int num_songs; - - gme_err = gme_open_file(path_fs, &emu, GME_SAMPLE_RATE); - if (gme_err != NULL) { - g_warning("%s", gme_err); - return NULL; - } - - num_songs = gme_track_count(emu); - /* if it only contains a single tune, don't treat as container */ - if (num_songs < 2) - return NULL; - - const char *subtune_suffix = uri_get_suffix(path_fs); - if (tnum <= num_songs){ - char *subtune = g_strdup_printf( - SUBTUNE_PREFIX "%03u.%s", tnum, subtune_suffix); - return subtune; - } else - return NULL; -} - -static void -gme_file_decode(struct decoder *decoder, const char *path_fs) -{ - float song_len; - Music_Emu *emu; - gme_info_t *ti; - struct audio_format audio_format; - enum decoder_command cmd; - short buf[GME_BUFFER_SAMPLES]; - const char* gme_err; - char *path_container = get_container_name(path_fs); - int song_num = get_song_num(path_fs); - - gme_err = gme_open_file(path_container, &emu, GME_SAMPLE_RATE); - g_free(path_container); - if (gme_err != NULL) { - g_warning("%s", gme_err); - return; - } - - if((gme_err = gme_track_info(emu, &ti, song_num)) != NULL){ - g_warning("%s", gme_err); - gme_delete(emu); - return; - } - - if(ti->length > 0) - song_len = ti->length / 1000.0; - else song_len = -1; - - /* initialize the MPD decoder */ - - GError *error = NULL; - if (!audio_format_init_checked(&audio_format, GME_SAMPLE_RATE, - SAMPLE_FORMAT_S16, GME_CHANNELS, - &error)) { - g_warning("%s", error->message); - g_error_free(error); - gme_free_info(ti); - gme_delete(emu); - return; - } - - decoder_initialized(decoder, &audio_format, true, song_len); - - if((gme_err = gme_start_track(emu, song_num)) != NULL) - g_warning("%s", gme_err); - - if(ti->length > 0) - gme_set_fade(emu, ti->length); - - /* play */ - do { - gme_err = gme_play(emu, GME_BUFFER_SAMPLES, buf); - if (gme_err != NULL) { - g_warning("%s", gme_err); - return; - } - cmd = decoder_data(decoder, NULL, buf, sizeof(buf), 0); - - if(cmd == DECODE_COMMAND_SEEK) { - float where = decoder_seek_where(decoder); - if((gme_err = gme_seek(emu, (int)where*1000)) != NULL) - g_warning("%s", gme_err); - decoder_command_finished(decoder); - } - - if(gme_track_ended(emu)) - break; - } while(cmd != DECODE_COMMAND_STOP); - - gme_free_info(ti); - gme_delete(emu); -} - -static bool -gme_scan_file(const char *path_fs, - const struct tag_handler *handler, void *handler_ctx) -{ - Music_Emu *emu; - gme_info_t *ti; - const char* gme_err; - char *path_container=get_container_name(path_fs); - int song_num; - song_num=get_song_num(path_fs); - - gme_err = gme_open_file(path_container, &emu, GME_SAMPLE_RATE); - g_free(path_container); - if (gme_err != NULL) { - g_warning("%s", gme_err); - return false; - } - if((gme_err = gme_track_info(emu, &ti, song_num)) != NULL){ - g_warning("%s", gme_err); - gme_delete(emu); - return false; - } - - assert(ti != NULL); - - if(ti->length > 0) - tag_handler_invoke_duration(handler, handler_ctx, - ti->length / 100); - - if(ti->song != NULL){ - if(gme_track_count(emu) > 1){ - /* start numbering subtunes from 1 */ - char *tag_title=g_strdup_printf("%s (%d/%d)", - ti->song, song_num+1, gme_track_count(emu)); - tag_handler_invoke_tag(handler, handler_ctx, - TAG_TITLE, tag_title); - g_free(tag_title); - }else - tag_handler_invoke_tag(handler, handler_ctx, - TAG_TITLE, ti->song); - } - if(ti->author != NULL) - tag_handler_invoke_tag(handler, handler_ctx, - TAG_ARTIST, ti->author); - if(ti->game != NULL) - tag_handler_invoke_tag(handler, handler_ctx, - TAG_ALBUM, ti->game); - if(ti->comment != NULL) - tag_handler_invoke_tag(handler, handler_ctx, - TAG_COMMENT, ti->comment); - if(ti->copyright != NULL) - tag_handler_invoke_tag(handler, handler_ctx, - TAG_DATE, ti->copyright); - - gme_free_info(ti); - gme_delete(emu); - - return true; -} - -static const char *const gme_suffixes[] = { - "ay", "gbs", "gym", "hes", "kss", "nsf", - "nsfe", "sap", "spc", "vgm", "vgz", - NULL -}; - -extern const struct decoder_plugin gme_decoder_plugin; -const struct decoder_plugin gme_decoder_plugin = { - .name = "gme", - .file_decode = gme_file_decode, - .scan_file = gme_scan_file, - .suffixes = gme_suffixes, - .container_scan = gme_container_scan, -}; diff --git a/src/decoder/mad_decoder_plugin.c b/src/decoder/mad_decoder_plugin.c deleted file mode 100644 index a69284be..00000000 --- a/src/decoder/mad_decoder_plugin.c +++ /dev/null @@ -1,1204 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "decoder_api.h" -#include "conf.h" -#include "tag_id3.h" -#include "tag_rva2.h" -#include "tag_handler.h" -#include "audio_check.h" - -#include <assert.h> -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <glib.h> -#include <mad.h> - -#ifdef HAVE_ID3TAG -#include <id3tag.h> -#endif - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "mad" - -#define FRAMES_CUSHION 2000 - -#define READ_BUFFER_SIZE 40960 - -enum mp3_action { - DECODE_SKIP = -3, - DECODE_BREAK = -2, - DECODE_CONT = -1, - DECODE_OK = 0 -}; - -enum muteframe { - MUTEFRAME_NONE, - MUTEFRAME_SKIP, - MUTEFRAME_SEEK -}; - -/* the number of samples of silence the decoder inserts at start */ -#define DECODERDELAY 529 - -#define DEFAULT_GAPLESS_MP3_PLAYBACK true - -static bool gapless_playback; - -static inline int32_t -mad_fixed_to_24_sample(mad_fixed_t sample) -{ - enum { - bits = 24, - MIN = -MAD_F_ONE, - MAX = MAD_F_ONE - 1 - }; - - /* round */ - sample = sample + (1L << (MAD_F_FRACBITS - bits)); - - /* clip */ - if (sample > MAX) - sample = MAX; - else if (sample < MIN) - sample = MIN; - - /* quantize */ - return sample >> (MAD_F_FRACBITS + 1 - bits); -} - -static void -mad_fixed_to_24_buffer(int32_t *dest, const struct mad_synth *synth, - unsigned int start, unsigned int end, - unsigned int num_channels) -{ - unsigned int i, c; - - for (i = start; i < end; ++i) { - for (c = 0; c < num_channels; ++c) - *dest++ = mad_fixed_to_24_sample(synth->pcm.samples[c][i]); - } -} - -static bool -mp3_plugin_init(G_GNUC_UNUSED const struct config_param *param) -{ - gapless_playback = config_get_bool(CONF_GAPLESS_MP3_PLAYBACK, - DEFAULT_GAPLESS_MP3_PLAYBACK); - return true; -} - -#define MP3_DATA_OUTPUT_BUFFER_SIZE 2048 - -struct mp3_data { - struct mad_stream stream; - struct mad_frame frame; - struct mad_synth synth; - mad_timer_t timer; - unsigned char input_buffer[READ_BUFFER_SIZE]; - int32_t output_buffer[MP3_DATA_OUTPUT_BUFFER_SIZE]; - float total_time; - float elapsed_time; - float seek_where; - enum muteframe mute_frame; - long *frame_offsets; - mad_timer_t *times; - unsigned long highest_frame; - unsigned long max_frames; - unsigned long current_frame; - unsigned int drop_start_frames; - unsigned int drop_end_frames; - unsigned int drop_start_samples; - unsigned int drop_end_samples; - bool found_replay_gain; - bool found_xing; - bool found_first_frame; - bool decoded_first_frame; - unsigned long bit_rate; - struct decoder *decoder; - struct input_stream *input_stream; - enum mad_layer layer; -}; - -static void -mp3_data_init(struct mp3_data *data, struct decoder *decoder, - struct input_stream *input_stream) -{ - data->mute_frame = MUTEFRAME_NONE; - data->highest_frame = 0; - data->max_frames = 0; - data->frame_offsets = NULL; - data->times = NULL; - data->current_frame = 0; - data->drop_start_frames = 0; - data->drop_end_frames = 0; - data->drop_start_samples = 0; - data->drop_end_samples = 0; - data->found_replay_gain = false; - data->found_xing = false; - data->found_first_frame = false; - data->decoded_first_frame = false; - data->decoder = decoder; - data->input_stream = input_stream; - data->layer = 0; - - mad_stream_init(&data->stream); - mad_stream_options(&data->stream, MAD_OPTION_IGNORECRC); - mad_frame_init(&data->frame); - mad_synth_init(&data->synth); - mad_timer_reset(&data->timer); -} - -static bool mp3_seek(struct mp3_data *data, long offset) -{ - if (!input_stream_lock_seek(data->input_stream, offset, SEEK_SET, NULL)) - return false; - - mad_stream_buffer(&data->stream, data->input_buffer, 0); - (data->stream).error = 0; - - return true; -} - -static bool -mp3_fill_buffer(struct mp3_data *data) -{ - size_t remaining, length; - unsigned char *dest; - - if (data->stream.next_frame != NULL) { - remaining = data->stream.bufend - data->stream.next_frame; - memmove(data->input_buffer, data->stream.next_frame, - remaining); - dest = (data->input_buffer) + remaining; - length = READ_BUFFER_SIZE - remaining; - } else { - remaining = 0; - length = READ_BUFFER_SIZE; - dest = data->input_buffer; - } - - /* we've exhausted the read buffer, so give up!, these potential - * mp3 frames are way too big, and thus unlikely to be mp3 frames */ - if (length == 0) - return false; - - length = decoder_read(data->decoder, data->input_stream, dest, length); - if (length == 0) - return false; - - mad_stream_buffer(&data->stream, data->input_buffer, - length + remaining); - (data->stream).error = 0; - - return true; -} - -#ifdef HAVE_ID3TAG -static bool -parse_id3_replay_gain_info(struct replay_gain_info *replay_gain_info, - struct id3_tag *tag) -{ - int i; - char *key; - char *value; - struct id3_frame *frame; - bool found = false; - - replay_gain_info_init(replay_gain_info); - - for (i = 0; (frame = id3_tag_findframe(tag, "TXXX", i)); i++) { - if (frame->nfields < 3) - continue; - - key = (char *) - id3_ucs4_latin1duplicate(id3_field_getstring - (&frame->fields[1])); - value = (char *) - id3_ucs4_latin1duplicate(id3_field_getstring - (&frame->fields[2])); - - if (g_ascii_strcasecmp(key, "replaygain_track_gain") == 0) { - replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain = atof(value); - found = true; - } else if (g_ascii_strcasecmp(key, "replaygain_album_gain") == 0) { - replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain = atof(value); - found = true; - } else if (g_ascii_strcasecmp(key, "replaygain_track_peak") == 0) { - replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak = atof(value); - found = true; - } else if (g_ascii_strcasecmp(key, "replaygain_album_peak") == 0) { - replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak = atof(value); - found = true; - } - - free(key); - free(value); - } - - return found || - /* fall back on RVA2 if no replaygain tags found */ - tag_rva2_parse(tag, replay_gain_info); -} -#endif - -#ifdef HAVE_ID3TAG -static bool -parse_id3_mixramp(char **mixramp_start, char **mixramp_end, - struct id3_tag *tag) -{ - int i; - char *key; - char *value; - struct id3_frame *frame; - bool found = false; - - *mixramp_start = NULL; - *mixramp_end = NULL; - - for (i = 0; (frame = id3_tag_findframe(tag, "TXXX", i)); i++) { - if (frame->nfields < 3) - continue; - - key = (char *) - id3_ucs4_latin1duplicate(id3_field_getstring - (&frame->fields[1])); - value = (char *) - id3_ucs4_latin1duplicate(id3_field_getstring - (&frame->fields[2])); - - if (g_ascii_strcasecmp(key, "mixramp_start") == 0) { - *mixramp_start = g_strdup(value); - found = true; - } else if (g_ascii_strcasecmp(key, "mixramp_end") == 0) { - *mixramp_end = g_strdup(value); - found = true; - } - - free(key); - free(value); - } - - return found; -} -#endif - -static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, - struct tag **mpd_tag) -{ -#ifdef HAVE_ID3TAG - struct id3_tag *id3_tag = NULL; - id3_length_t count; - id3_byte_t const *id3_data; - id3_byte_t *allocated = NULL; - - count = data->stream.bufend - data->stream.this_frame; - - if (tagsize <= count) { - id3_data = data->stream.this_frame; - mad_stream_skip(&(data->stream), tagsize); - } else { - allocated = g_malloc(tagsize); - memcpy(allocated, data->stream.this_frame, count); - mad_stream_skip(&(data->stream), count); - - while (count < tagsize) { - size_t len; - - len = decoder_read(data->decoder, data->input_stream, - allocated + count, tagsize - count); - if (len == 0) - break; - else - count += len; - } - - if (count != tagsize) { - g_debug("error parsing ID3 tag"); - g_free(allocated); - return; - } - - id3_data = allocated; - } - - id3_tag = id3_tag_parse(id3_data, tagsize); - if (id3_tag == NULL) { - g_free(allocated); - return; - } - - if (mpd_tag) { - struct tag *tmp_tag = tag_id3_import(id3_tag); - if (tmp_tag != NULL) { - if (*mpd_tag != NULL) - tag_free(*mpd_tag); - *mpd_tag = tmp_tag; - } - } - - if (data->decoder != NULL) { - struct replay_gain_info rgi; - char *mixramp_start; - char *mixramp_end; - float replay_gain_db = 0; - - if (parse_id3_replay_gain_info(&rgi, id3_tag)) { - replay_gain_db = decoder_replay_gain(data->decoder, &rgi); - data->found_replay_gain = true; - } - if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag)) { - g_debug("setting mixramp_tags"); - decoder_mixramp(data->decoder, replay_gain_db, - mixramp_start, mixramp_end); - } - } - - id3_tag_delete(id3_tag); - - g_free(allocated); -#else /* !HAVE_ID3TAG */ - (void)mpd_tag; - - /* This code is enabled when libid3tag is disabled. Instead - of parsing the ID3 frame, it just skips it. */ - - size_t count = data->stream.bufend - data->stream.this_frame; - - if (tagsize <= count) { - mad_stream_skip(&data->stream, tagsize); - } else { - mad_stream_skip(&data->stream, count); - - while (count < tagsize) { - size_t len = tagsize - count; - char ignored[1024]; - if (len > sizeof(ignored)) - len = sizeof(ignored); - - len = decoder_read(data->decoder, data->input_stream, - ignored, len); - if (len == 0) - break; - else - count += len; - } - } -#endif -} - -#ifndef HAVE_ID3TAG -/** - * This function emulates libid3tag when it is disabled. Instead of - * doing a real analyzation of the frame, it just checks whether the - * frame begins with the string "ID3". If so, it returns the length - * of the ID3 frame. - */ -static signed long -id3_tag_query(const void *p0, size_t length) -{ - const char *p = p0; - - return length >= 10 && memcmp(p, "ID3", 3) == 0 - ? (p[8] << 7) + p[9] + 10 - : 0; -} -#endif /* !HAVE_ID3TAG */ - -static enum mp3_action -decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag) -{ - enum mad_layer layer; - - if ((data->stream).buffer == NULL - || (data->stream).error == MAD_ERROR_BUFLEN) { - if (!mp3_fill_buffer(data)) - return DECODE_BREAK; - } - if (mad_header_decode(&data->frame.header, &data->stream)) { - if ((data->stream).error == MAD_ERROR_LOSTSYNC && - (data->stream).this_frame) { - signed long tagsize = id3_tag_query((data->stream). - this_frame, - (data->stream). - bufend - - (data->stream). - this_frame); - - if (tagsize > 0) { - if (tag && !(*tag)) { - mp3_parse_id3(data, (size_t)tagsize, - tag); - } else { - mad_stream_skip(&(data->stream), - tagsize); - } - return DECODE_CONT; - } - } - if (MAD_RECOVERABLE((data->stream).error)) { - return DECODE_SKIP; - } else { - if ((data->stream).error == MAD_ERROR_BUFLEN) - return DECODE_CONT; - else { - g_warning("unrecoverable frame level error " - "(%s).\n", - mad_stream_errorstr(&data->stream)); - return DECODE_BREAK; - } - } - } - - layer = data->frame.header.layer; - if (!data->layer) { - if (layer != MAD_LAYER_II && layer != MAD_LAYER_III) { - /* Only layer 2 and 3 have been tested to work */ - return DECODE_SKIP; - } - data->layer = layer; - } else if (layer != data->layer) { - /* Don't decode frames with a different layer than the first */ - return DECODE_SKIP; - } - - return DECODE_OK; -} - -static enum mp3_action -decodeNextFrame(struct mp3_data *data) -{ - if ((data->stream).buffer == NULL - || (data->stream).error == MAD_ERROR_BUFLEN) { - if (!mp3_fill_buffer(data)) - return DECODE_BREAK; - } - if (mad_frame_decode(&data->frame, &data->stream)) { - if ((data->stream).error == MAD_ERROR_LOSTSYNC) { - signed long tagsize = id3_tag_query((data->stream). - this_frame, - (data->stream). - bufend - - (data->stream). - this_frame); - if (tagsize > 0) { - mad_stream_skip(&(data->stream), tagsize); - return DECODE_CONT; - } - } - if (MAD_RECOVERABLE((data->stream).error)) { - return DECODE_SKIP; - } else { - if ((data->stream).error == MAD_ERROR_BUFLEN) - return DECODE_CONT; - else { - g_warning("unrecoverable frame level error " - "(%s).\n", - mad_stream_errorstr(&data->stream)); - return DECODE_BREAK; - } - } - } - - return DECODE_OK; -} - -/* xing stuff stolen from alsaplayer, and heavily modified by jat */ -#define XI_MAGIC (('X' << 8) | 'i') -#define NG_MAGIC (('n' << 8) | 'g') -#define IN_MAGIC (('I' << 8) | 'n') -#define FO_MAGIC (('f' << 8) | 'o') - -enum xing_magic { - XING_MAGIC_XING, /* VBR */ - XING_MAGIC_INFO /* CBR */ -}; - -struct xing { - long flags; /* valid fields (see below) */ - unsigned long frames; /* total number of frames */ - unsigned long bytes; /* total number of bytes */ - unsigned char toc[100]; /* 100-point seek table */ - long scale; /* VBR quality */ - enum xing_magic magic; /* header magic */ -}; - -enum { - XING_FRAMES = 0x00000001L, - XING_BYTES = 0x00000002L, - XING_TOC = 0x00000004L, - XING_SCALE = 0x00000008L -}; - -struct lame_version { - unsigned major; - unsigned minor; -}; - -struct lame { - char encoder[10]; /* 9 byte encoder name/version ("LAME3.97b") */ - struct lame_version version; /* struct containing just the version */ - float peak; /* replaygain peak */ - float track_gain; /* replaygain track gain */ - float album_gain; /* replaygain album gain */ - int encoder_delay; /* # of added samples at start of mp3 */ - int encoder_padding; /* # of added samples at end of mp3 */ - int crc; /* CRC of the first 190 bytes of this frame */ -}; - -static bool -parse_xing(struct xing *xing, struct mad_bitptr *ptr, int *oldbitlen) -{ - unsigned long bits; - int bitlen; - int bitsleft; - int i; - - bitlen = *oldbitlen; - - if (bitlen < 16) - return false; - - bits = mad_bit_read(ptr, 16); - bitlen -= 16; - - if (bits == XI_MAGIC) { - if (bitlen < 16) - return false; - - if (mad_bit_read(ptr, 16) != NG_MAGIC) - return false; - - bitlen -= 16; - xing->magic = XING_MAGIC_XING; - } else if (bits == IN_MAGIC) { - if (bitlen < 16) - return false; - - if (mad_bit_read(ptr, 16) != FO_MAGIC) - return false; - - bitlen -= 16; - xing->magic = XING_MAGIC_INFO; - } - else if (bits == NG_MAGIC) xing->magic = XING_MAGIC_XING; - else if (bits == FO_MAGIC) xing->magic = XING_MAGIC_INFO; - else - return false; - - if (bitlen < 32) - return false; - xing->flags = mad_bit_read(ptr, 32); - bitlen -= 32; - - if (xing->flags & XING_FRAMES) { - if (bitlen < 32) - return false; - xing->frames = mad_bit_read(ptr, 32); - bitlen -= 32; - } - - if (xing->flags & XING_BYTES) { - if (bitlen < 32) - return false; - xing->bytes = mad_bit_read(ptr, 32); - bitlen -= 32; - } - - if (xing->flags & XING_TOC) { - if (bitlen < 800) - return false; - for (i = 0; i < 100; ++i) xing->toc[i] = mad_bit_read(ptr, 8); - bitlen -= 800; - } - - if (xing->flags & XING_SCALE) { - if (bitlen < 32) - return false; - xing->scale = mad_bit_read(ptr, 32); - bitlen -= 32; - } - - /* Make sure we consume no less than 120 bytes (960 bits) in hopes that - * the LAME tag is found there, and not right after the Xing header */ - bitsleft = 960 - ((*oldbitlen) - bitlen); - if (bitsleft < 0) - return false; - else if (bitsleft > 0) { - mad_bit_read(ptr, bitsleft); - bitlen -= bitsleft; - } - - *oldbitlen = bitlen; - - return true; -} - -static bool -parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen) -{ - int adj = 0; - int name; - int orig; - int sign; - int gain; - int i; - - /* Unlike the xing header, the lame tag has a fixed length. Fail if - * not all 36 bytes (288 bits) are there. */ - if (*bitlen < 288) - return false; - - for (i = 0; i < 9; i++) - lame->encoder[i] = (char)mad_bit_read(ptr, 8); - lame->encoder[9] = '\0'; - - *bitlen -= 72; - - /* This is technically incorrect, since the encoder might not be lame. - * But there's no other way to determine if this is a lame tag, and we - * wouldn't want to go reading a tag that's not there. */ - if (!g_str_has_prefix(lame->encoder, "LAME")) - return false; - - if (sscanf(lame->encoder+4, "%u.%u", - &lame->version.major, &lame->version.minor) != 2) - return false; - - g_debug("detected LAME version %i.%i (\"%s\")\n", - lame->version.major, lame->version.minor, lame->encoder); - - /* The reference volume was changed from the 83dB used in the - * ReplayGain spec to 89dB in lame 3.95.1. Bump the gain for older - * versions, since everyone else uses 89dB instead of 83dB. - * Unfortunately, lame didn't differentiate between 3.95 and 3.95.1, so - * it's impossible to make the proper adjustment for 3.95. - * Fortunately, 3.95 was only out for about a day before 3.95.1 was - * released. -- tmz */ - if (lame->version.major < 3 || - (lame->version.major == 3 && lame->version.minor < 95)) - adj = 6; - - mad_bit_read(ptr, 16); - - lame->peak = mad_f_todouble(mad_bit_read(ptr, 32) << 5); /* peak */ - g_debug("LAME peak found: %f\n", lame->peak); - - lame->track_gain = 0; - name = mad_bit_read(ptr, 3); /* gain name */ - orig = mad_bit_read(ptr, 3); /* gain originator */ - sign = mad_bit_read(ptr, 1); /* sign bit */ - gain = mad_bit_read(ptr, 9); /* gain*10 */ - if (gain && name == 1 && orig != 0) { - lame->track_gain = ((sign ? -gain : gain) / 10.0) + adj; - g_debug("LAME track gain found: %f\n", lame->track_gain); - } - - /* tmz reports that this isn't currently written by any version of lame - * (as of 3.97). Since we have no way of testing it, don't use it. - * Wouldn't want to go blowing someone's ears just because we read it - * wrong. :P -- jat */ - lame->album_gain = 0; -#if 0 - name = mad_bit_read(ptr, 3); /* gain name */ - orig = mad_bit_read(ptr, 3); /* gain originator */ - sign = mad_bit_read(ptr, 1); /* sign bit */ - gain = mad_bit_read(ptr, 9); /* gain*10 */ - if (gain && name == 2 && orig != 0) { - lame->album_gain = ((sign ? -gain : gain) / 10.0) + adj; - g_debug("LAME album gain found: %f\n", lame->track_gain); - } -#else - mad_bit_read(ptr, 16); -#endif - - mad_bit_read(ptr, 16); - - lame->encoder_delay = mad_bit_read(ptr, 12); - lame->encoder_padding = mad_bit_read(ptr, 12); - - g_debug("encoder delay is %i, encoder padding is %i\n", - lame->encoder_delay, lame->encoder_padding); - - mad_bit_read(ptr, 80); - - lame->crc = mad_bit_read(ptr, 16); - - *bitlen -= 216; - - return true; -} - -static inline float -mp3_frame_duration(const struct mad_frame *frame) -{ - return mad_timer_count(frame->header.duration, - MAD_UNITS_MILLISECONDS) / 1000.0; -} - -static goffset -mp3_this_frame_offset(const struct mp3_data *data) -{ - goffset offset = data->input_stream->offset; - - if (data->stream.this_frame != NULL) - offset -= data->stream.bufend - data->stream.this_frame; - else - offset -= data->stream.bufend - data->stream.buffer; - - return offset; -} - -static goffset -mp3_rest_including_this_frame(const struct mp3_data *data) -{ - return data->input_stream->size - mp3_this_frame_offset(data); -} - -/** - * Attempt to calulcate the length of the song from filesize - */ -static void -mp3_filesize_to_song_length(struct mp3_data *data) -{ - goffset rest = mp3_rest_including_this_frame(data); - - if (rest > 0) { - float frame_duration = mp3_frame_duration(&data->frame); - - data->total_time = (rest * 8.0) / (data->frame).header.bitrate; - data->max_frames = data->total_time / frame_duration + - FRAMES_CUSHION; - } else { - data->max_frames = FRAMES_CUSHION; - data->total_time = 0; - } -} - -static bool -mp3_decode_first_frame(struct mp3_data *data, struct tag **tag) -{ - struct xing xing; - struct lame lame; - struct mad_bitptr ptr; - int bitlen; - enum mp3_action ret; - - /* stfu gcc */ - memset(&xing, 0, sizeof(struct xing)); - xing.flags = 0; - - while (true) { - do { - ret = decode_next_frame_header(data, tag); - } while (ret == DECODE_CONT); - if (ret == DECODE_BREAK) - return false; - if (ret == DECODE_SKIP) continue; - - do { - ret = decodeNextFrame(data); - } while (ret == DECODE_CONT); - if (ret == DECODE_BREAK) - return false; - if (ret == DECODE_OK) break; - } - - ptr = data->stream.anc_ptr; - bitlen = data->stream.anc_bitlen; - - mp3_filesize_to_song_length(data); - - /* - * if an xing tag exists, use that! - */ - if (parse_xing(&xing, &ptr, &bitlen)) { - data->found_xing = true; - data->mute_frame = MUTEFRAME_SKIP; - - if ((xing.flags & XING_FRAMES) && xing.frames) { - mad_timer_t duration = data->frame.header.duration; - mad_timer_multiply(&duration, xing.frames); - data->total_time = ((float)mad_timer_count(duration, MAD_UNITS_MILLISECONDS)) / 1000; - data->max_frames = xing.frames; - } - - if (parse_lame(&lame, &ptr, &bitlen)) { - if (gapless_playback && - data->input_stream->seekable) { - data->drop_start_samples = lame.encoder_delay + - DECODERDELAY; - data->drop_end_samples = lame.encoder_padding; - } - - /* Album gain isn't currently used. See comment in - * parse_lame() for details. -- jat */ - if (data->decoder != NULL && - !data->found_replay_gain && - lame.track_gain) { - struct replay_gain_info rgi; - replay_gain_info_init(&rgi); - rgi.tuples[REPLAY_GAIN_TRACK].gain = lame.track_gain; - rgi.tuples[REPLAY_GAIN_TRACK].peak = lame.peak; - decoder_replay_gain(data->decoder, &rgi); - } - } - } - - if (!data->max_frames) - return false; - - if (data->max_frames > 8 * 1024 * 1024) { - g_warning("mp3 file header indicates too many frames: %lu\n", - data->max_frames); - return false; - } - - data->frame_offsets = g_malloc(sizeof(long) * data->max_frames); - data->times = g_malloc(sizeof(mad_timer_t) * data->max_frames); - - return true; -} - -static void mp3_data_finish(struct mp3_data *data) -{ - mad_synth_finish(&data->synth); - mad_frame_finish(&data->frame); - mad_stream_finish(&data->stream); - - g_free(data->frame_offsets); - g_free(data->times); -} - -/* this is primarily used for getting total time for tags */ -static int -mad_decoder_total_file_time(struct input_stream *is) -{ - struct mp3_data data; - int ret; - - mp3_data_init(&data, NULL, is); - if (!mp3_decode_first_frame(&data, NULL)) - ret = -1; - else - ret = data.total_time + 0.5; - mp3_data_finish(&data); - - return ret; -} - -static bool -mp3_open(struct input_stream *is, struct mp3_data *data, - struct decoder *decoder, struct tag **tag) -{ - mp3_data_init(data, decoder, is); - *tag = NULL; - if (!mp3_decode_first_frame(data, tag)) { - mp3_data_finish(data); - if (tag && *tag) - tag_free(*tag); - return false; - } - - return true; -} - -static long -mp3_time_to_frame(const struct mp3_data *data, double t) -{ - unsigned long i; - - for (i = 0; i < data->highest_frame; ++i) { - double frame_time = - mad_timer_count(data->times[i], - MAD_UNITS_MILLISECONDS) / 1000.; - if (frame_time >= t) - break; - } - - return i; -} - -static void -mp3_update_timer_next_frame(struct mp3_data *data) -{ - if (data->current_frame >= data->highest_frame) { - /* record this frame's properties in - data->frame_offsets (for seeking) and - data->times */ - data->bit_rate = (data->frame).header.bitrate; - - if (data->current_frame >= data->max_frames) - /* cap data->current_frame */ - data->current_frame = data->max_frames - 1; - else - data->highest_frame++; - - data->frame_offsets[data->current_frame] = - mp3_this_frame_offset(data); - - mad_timer_add(&data->timer, (data->frame).header.duration); - data->times[data->current_frame] = data->timer; - } else - /* get the new timer value from data->times */ - data->timer = data->times[data->current_frame]; - - data->current_frame++; - data->elapsed_time = - mad_timer_count(data->timer, MAD_UNITS_MILLISECONDS) / 1000.0; -} - -/** - * Sends the synthesized current frame via decoder_data(). - */ -static enum decoder_command -mp3_send_pcm(struct mp3_data *data, unsigned i, unsigned pcm_length) -{ - unsigned max_samples; - - max_samples = sizeof(data->output_buffer) / - sizeof(data->output_buffer[0]) / - MAD_NCHANNELS(&(data->frame).header); - - while (i < pcm_length) { - enum decoder_command cmd; - unsigned int num_samples = pcm_length - i; - if (num_samples > max_samples) - num_samples = max_samples; - - i += num_samples; - - mad_fixed_to_24_buffer(data->output_buffer, - &data->synth, - i - num_samples, i, - MAD_NCHANNELS(&(data->frame).header)); - num_samples *= MAD_NCHANNELS(&(data->frame).header); - - cmd = decoder_data(data->decoder, data->input_stream, - data->output_buffer, - sizeof(data->output_buffer[0]) * num_samples, - data->bit_rate / 1000); - if (cmd != DECODE_COMMAND_NONE) - return cmd; - } - - return DECODE_COMMAND_NONE; -} - -/** - * Synthesize the current frame and send it via decoder_data(). - */ -static enum decoder_command -mp3_synth_and_send(struct mp3_data *data) -{ - unsigned i, pcm_length; - enum decoder_command cmd; - - mad_synth_frame(&data->synth, &data->frame); - - if (!data->found_first_frame) { - unsigned int samples_per_frame = data->synth.pcm.length; - data->drop_start_frames = data->drop_start_samples / samples_per_frame; - data->drop_end_frames = data->drop_end_samples / samples_per_frame; - data->drop_start_samples = data->drop_start_samples % samples_per_frame; - data->drop_end_samples = data->drop_end_samples % samples_per_frame; - data->found_first_frame = true; - } - - if (data->drop_start_frames > 0) { - data->drop_start_frames--; - return DECODE_COMMAND_NONE; - } else if ((data->drop_end_frames > 0) && - (data->current_frame == (data->max_frames + 1 - data->drop_end_frames))) { - /* stop decoding, effectively dropping all remaining - frames */ - return DECODE_COMMAND_STOP; - } - - if (!data->decoded_first_frame) { - i = data->drop_start_samples; - data->decoded_first_frame = true; - } else - i = 0; - - pcm_length = data->synth.pcm.length; - if (data->drop_end_samples && - (data->current_frame == data->max_frames - data->drop_end_frames)) { - if (data->drop_end_samples >= pcm_length) - pcm_length = 0; - else - pcm_length -= data->drop_end_samples; - } - - cmd = mp3_send_pcm(data, i, pcm_length); - if (cmd != DECODE_COMMAND_NONE) - return cmd; - - if (data->drop_end_samples && - (data->current_frame == data->max_frames - data->drop_end_frames)) - /* stop decoding, effectively dropping - * all remaining samples */ - return DECODE_COMMAND_STOP; - - return DECODE_COMMAND_NONE; -} - -static bool -mp3_read(struct mp3_data *data) -{ - struct decoder *decoder = data->decoder; - enum mp3_action ret; - enum decoder_command cmd; - - mp3_update_timer_next_frame(data); - - switch (data->mute_frame) { - case MUTEFRAME_SKIP: - data->mute_frame = MUTEFRAME_NONE; - break; - case MUTEFRAME_SEEK: - if (data->elapsed_time >= data->seek_where) - data->mute_frame = MUTEFRAME_NONE; - break; - case MUTEFRAME_NONE: - cmd = mp3_synth_and_send(data); - if (cmd == DECODE_COMMAND_SEEK) { - unsigned long j; - - assert(data->input_stream->seekable); - - j = mp3_time_to_frame(data, - decoder_seek_where(decoder)); - if (j < data->highest_frame) { - if (mp3_seek(data, data->frame_offsets[j])) { - data->current_frame = j; - decoder_command_finished(decoder); - } else - decoder_seek_error(decoder); - } else { - data->seek_where = decoder_seek_where(decoder); - data->mute_frame = MUTEFRAME_SEEK; - decoder_command_finished(decoder); - } - } else if (cmd != DECODE_COMMAND_NONE) - return false; - } - - while (true) { - bool skip = false; - - do { - struct tag *tag = NULL; - - ret = decode_next_frame_header(data, &tag); - - if (tag != NULL) { - decoder_tag(decoder, data->input_stream, tag); - tag_free(tag); - } - } while (ret == DECODE_CONT); - if (ret == DECODE_BREAK) - return false; - else if (ret == DECODE_SKIP) - skip = true; - - if (data->mute_frame == MUTEFRAME_NONE) { - do { - ret = decodeNextFrame(data); - } while (ret == DECODE_CONT); - if (ret == DECODE_BREAK) - return false; - } - - if (!skip && ret == DECODE_OK) - break; - } - - return ret != DECODE_BREAK; -} - -static void -mp3_decode(struct decoder *decoder, struct input_stream *input_stream) -{ - struct mp3_data data; - GError *error = NULL; - struct tag *tag = NULL; - struct audio_format audio_format; - - if (!mp3_open(input_stream, &data, decoder, &tag)) { - if (decoder_get_command(decoder) == DECODE_COMMAND_NONE) - g_warning - ("Input does not appear to be a mp3 bit stream.\n"); - return; - } - - if (!audio_format_init_checked(&audio_format, - data.frame.header.samplerate, - SAMPLE_FORMAT_S24_P32, - MAD_NCHANNELS(&data.frame.header), - &error)) { - g_warning("%s", error->message); - g_error_free(error); - - if (tag != NULL) - tag_free(tag); - mp3_data_finish(&data); - return; - } - - decoder_initialized(decoder, &audio_format, - data.input_stream->seekable, data.total_time); - - if (tag != NULL) { - decoder_tag(decoder, input_stream, tag); - tag_free(tag); - } - - while (mp3_read(&data)) ; - - mp3_data_finish(&data); -} - -static bool -mad_decoder_scan_stream(struct input_stream *is, - const struct tag_handler *handler, void *handler_ctx) -{ - int total_time; - - total_time = mad_decoder_total_file_time(is); - if (total_time < 0) - return false; - - tag_handler_invoke_duration(handler, handler_ctx, total_time); - return true; -} - -static const char *const mp3_suffixes[] = { "mp3", "mp2", NULL }; -static const char *const mp3_mime_types[] = { "audio/mpeg", NULL }; - -const struct decoder_plugin mad_decoder_plugin = { - .name = "mad", - .init = mp3_plugin_init, - .stream_decode = mp3_decode, - .scan_stream = mad_decoder_scan_stream, - .suffixes = mp3_suffixes, - .mime_types = mp3_mime_types -}; diff --git a/src/decoder/mikmod_decoder_plugin.c b/src/decoder/mikmod_decoder_plugin.c deleted file mode 100644 index 5681a7a5..00000000 --- a/src/decoder/mikmod_decoder_plugin.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "decoder_api.h" -#include "mpd_error.h" -#include "tag_handler.h" - -#include <glib.h> -#include <mikmod.h> -#include <assert.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "mikmod" - -/* this is largely copied from alsaplayer */ - -#define MIKMOD_FRAME_SIZE 4096 - -static BOOL -mikmod_mpd_init(void) -{ - return VC_Init(); -} - -static void -mikmod_mpd_exit(void) -{ - VC_Exit(); -} - -static void -mikmod_mpd_update(void) -{ -} - -static BOOL -mikmod_mpd_is_present(void) -{ - return true; -} - -static char drv_name[] = PACKAGE_NAME; -static char drv_version[] = VERSION; - -#if (LIBMIKMOD_VERSION > 0x030106) -static char drv_alias[] = PACKAGE; -#endif - -static MDRIVER drv_mpd = { - NULL, - drv_name, - drv_version, - 0, - 255, -#if (LIBMIKMOD_VERSION > 0x030106) - drv_alias, -#if (LIBMIKMOD_VERSION >= 0x030200) - NULL, /* CmdLineHelp */ -#endif - NULL, /* CommandLine */ -#endif - mikmod_mpd_is_present, - VC_SampleLoad, - VC_SampleUnload, - VC_SampleSpace, - VC_SampleLength, - mikmod_mpd_init, - mikmod_mpd_exit, - NULL, - VC_SetNumVoices, - VC_PlayStart, - VC_PlayStop, - mikmod_mpd_update, - NULL, - VC_VoiceSetVolume, - VC_VoiceGetVolume, - VC_VoiceSetFrequency, - VC_VoiceGetFrequency, - VC_VoiceSetPanning, - VC_VoiceGetPanning, - VC_VoicePlay, - VC_VoiceStop, - VC_VoiceStopped, - VC_VoiceGetPosition, - VC_VoiceRealVolume -}; - -static unsigned mikmod_sample_rate; - -static bool -mikmod_decoder_init(const struct config_param *param) -{ - static char params[] = ""; - - mikmod_sample_rate = config_get_block_unsigned(param, "sample_rate", - 44100); - if (!audio_valid_sample_rate(mikmod_sample_rate)) - MPD_ERROR("Invalid sample rate in line %d: %u", - param->line, mikmod_sample_rate); - - md_device = 0; - md_reverb = 0; - - MikMod_RegisterDriver(&drv_mpd); - MikMod_RegisterAllLoaders(); - - md_pansep = 64; - md_mixfreq = mikmod_sample_rate; - md_mode = (DMODE_SOFT_MUSIC | DMODE_INTERP | DMODE_STEREO | - DMODE_16BITS); - - if (MikMod_Init(params)) { - g_warning("Could not init MikMod: %s\n", - MikMod_strerror(MikMod_errno)); - return false; - } - - return true; -} - -static void -mikmod_decoder_finish(void) -{ - MikMod_Exit(); -} - -static void -mikmod_decoder_file_decode(struct decoder *decoder, const char *path_fs) -{ - char *path2; - MODULE *handle; - struct audio_format audio_format; - int ret; - SBYTE buffer[MIKMOD_FRAME_SIZE]; - enum decoder_command cmd = DECODE_COMMAND_NONE; - - path2 = g_strdup(path_fs); - handle = Player_Load(path2, 128, 0); - g_free(path2); - - if (handle == NULL) { - g_warning("failed to open mod: %s", path_fs); - return; - } - - /* Prevent module from looping forever */ - handle->loop = 0; - - audio_format_init(&audio_format, mikmod_sample_rate, SAMPLE_FORMAT_S16, 2); - assert(audio_format_valid(&audio_format)); - - decoder_initialized(decoder, &audio_format, false, 0); - - Player_Start(handle); - while (cmd == DECODE_COMMAND_NONE && Player_Active()) { - ret = VC_WriteBytes(buffer, sizeof(buffer)); - cmd = decoder_data(decoder, NULL, buffer, ret, 0); - } - - Player_Stop(); - Player_Free(handle); -} - -static bool -mikmod_decoder_scan_file(const char *path_fs, - const struct tag_handler *handler, void *handler_ctx) -{ - char *path2 = g_strdup(path_fs); - MODULE *handle = Player_Load(path2, 128, 0); - - if (handle == NULL) { - g_free(path2); - g_debug("Failed to open file: %s", path_fs); - return false; - - } - - Player_Free(handle); - - char *title = Player_LoadTitle(path2); - g_free(path2); - - if (title != NULL) { - tag_handler_invoke_tag(handler, handler_ctx, - TAG_TITLE, title); - free(title); - } - - return true; -} - -static const char *const mikmod_decoder_suffixes[] = { - "amf", - "dsm", - "far", - "gdm", - "imf", - "it", - "med", - "mod", - "mtm", - "s3m", - "stm", - "stx", - "ult", - "uni", - "xm", - NULL -}; - -const struct decoder_plugin mikmod_decoder_plugin = { - .name = "mikmod", - .init = mikmod_decoder_init, - .finish = mikmod_decoder_finish, - .file_decode = mikmod_decoder_file_decode, - .scan_file = mikmod_decoder_scan_file, - .suffixes = mikmod_decoder_suffixes, -}; diff --git a/src/decoder/modplug_decoder_plugin.c b/src/decoder/modplug_decoder_plugin.c deleted file mode 100644 index 21ee79e7..00000000 --- a/src/decoder/modplug_decoder_plugin.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "decoder_api.h" -#include "tag_handler.h" - -#include <glib.h> -#include <modplug.h> -#include <assert.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "modplug" - -enum { - MODPLUG_FRAME_SIZE = 4096, - MODPLUG_PREALLOC_BLOCK = 256 * 1024, - MODPLUG_READ_BLOCK = 128 * 1024, - MODPLUG_FILE_LIMIT = 100 * 1024 * 1024, -}; - -static GByteArray *mod_loadfile(struct decoder *decoder, struct input_stream *is) -{ - unsigned char *data; - GByteArray *bdatas; - size_t ret; - - if (is->size == 0) { - g_warning("file is empty"); - return NULL; - } - - if (is->size > MODPLUG_FILE_LIMIT) { - g_warning("file too large"); - return NULL; - } - - //known/unknown size, preallocate array, lets read in chunks - if (is->size > 0) { - bdatas = g_byte_array_sized_new(is->size); - } else { - bdatas = g_byte_array_sized_new(MODPLUG_PREALLOC_BLOCK); - } - - data = g_malloc(MODPLUG_READ_BLOCK); - - while (true) { - ret = decoder_read(decoder, is, data, MODPLUG_READ_BLOCK); - if (ret == 0) { - if (input_stream_lock_eof(is)) - /* end of file */ - break; - - /* I/O error - skip this song */ - g_free(data); - g_byte_array_free(bdatas, true); - return NULL; - } - - if (bdatas->len + ret > MODPLUG_FILE_LIMIT) { - g_warning("stream too large\n"); - g_free(data); - g_byte_array_free(bdatas, TRUE); - return NULL; - } - - g_byte_array_append(bdatas, data, ret); - } - - g_free(data); - - return bdatas; -} - -static void -mod_decode(struct decoder *decoder, struct input_stream *is) -{ - ModPlugFile *f; - ModPlug_Settings settings; - GByteArray *bdatas; - struct audio_format audio_format; - int ret; - char audio_buffer[MODPLUG_FRAME_SIZE]; - enum decoder_command cmd = DECODE_COMMAND_NONE; - - bdatas = mod_loadfile(decoder, is); - - if (!bdatas) { - g_warning("could not load stream\n"); - return; - } - - ModPlug_GetSettings(&settings); - /* alter setting */ - settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; /* RESAMP */ - settings.mChannels = 2; - settings.mBits = 16; - settings.mFrequency = 44100; - /* insert more setting changes here */ - ModPlug_SetSettings(&settings); - - f = ModPlug_Load(bdatas->data, bdatas->len); - g_byte_array_free(bdatas, TRUE); - if (!f) { - g_warning("could not decode stream\n"); - return; - } - - audio_format_init(&audio_format, 44100, SAMPLE_FORMAT_S16, 2); - assert(audio_format_valid(&audio_format)); - - decoder_initialized(decoder, &audio_format, - is->seekable, ModPlug_GetLength(f) / 1000.0); - - do { - ret = ModPlug_Read(f, audio_buffer, MODPLUG_FRAME_SIZE); - if (ret <= 0) - break; - - cmd = decoder_data(decoder, NULL, - audio_buffer, ret, - 0); - - if (cmd == DECODE_COMMAND_SEEK) { - float where = decoder_seek_where(decoder); - - ModPlug_Seek(f, (int)(where * 1000.0)); - - decoder_command_finished(decoder); - } - - } while (cmd != DECODE_COMMAND_STOP); - - ModPlug_Unload(f); -} - -static bool -modplug_scan_stream(struct input_stream *is, - const struct tag_handler *handler, void *handler_ctx) -{ - ModPlugFile *f; - GByteArray *bdatas; - - bdatas = mod_loadfile(NULL, is); - if (!bdatas) - return false; - - f = ModPlug_Load(bdatas->data, bdatas->len); - g_byte_array_free(bdatas, TRUE); - if (f == NULL) - return false; - - tag_handler_invoke_duration(handler, handler_ctx, - ModPlug_GetLength(f) / 1000); - - const char *title = ModPlug_GetName(f); - if (title != NULL) - tag_handler_invoke_tag(handler, handler_ctx, - TAG_TITLE, title); - - ModPlug_Unload(f); - - return true; -} - -static const char *const mod_suffixes[] = { - "669", "amf", "ams", "dbm", "dfm", "dsm", "far", "it", - "med", "mdl", "mod", "mtm", "mt2", "okt", "s3m", "stm", - "ult", "umx", "xm", - NULL -}; - -const struct decoder_plugin modplug_decoder_plugin = { - .name = "modplug", - .stream_decode = mod_decode, - .scan_stream = modplug_scan_stream, - .suffixes = mod_suffixes, -}; diff --git a/src/decoder/mp4ff_decoder_plugin.c b/src/decoder/mp4ff_decoder_plugin.c deleted file mode 100644 index ca78a22d..00000000 --- a/src/decoder/mp4ff_decoder_plugin.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "decoder_api.h" -#include "audio_check.h" -#include "tag_table.h" -#include "tag_handler.h" - -#include <glib.h> - -#include <mp4ff.h> -#include <faad.h> - -#include <assert.h> -#include <stdlib.h> -#include <unistd.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "mp4ff" - -/* all code here is either based on or copied from FAAD2's frontend code */ - -struct mp4ff_input_stream { - mp4ff_callback_t callback; - - struct decoder *decoder; - struct input_stream *input_stream; -}; - -static int -mp4_get_aac_track(mp4ff_t * infile, faacDecHandle decoder, - uint32_t *sample_rate, unsigned char *channels_r) -{ -#ifdef HAVE_FAAD_LONG - /* neaacdec.h declares all arguments as "unsigned long", but - internally expects uint32_t pointers. To avoid gcc - warnings, use this workaround. */ - unsigned long *sample_rate_r = (unsigned long*)sample_rate; -#else - uint32_t *sample_rate_r = sample_rate; -#endif - int i, rc; - int num_tracks = mp4ff_total_tracks(infile); - - for (i = 0; i < num_tracks; i++) { - unsigned char *buff = NULL; - unsigned int buff_size = 0; - - if (mp4ff_get_track_type(infile, i) != 1) - /* not an audio track */ - continue; - - if (decoder == NULL) - /* have don't have a decoder to initialize - - we're done now, because we found an audio - track */ - return i; - - mp4ff_get_decoder_config(infile, i, &buff, &buff_size); - if (buff == NULL) - continue; - - rc = faacDecInit2(decoder, buff, buff_size, - sample_rate_r, channels_r); - free(buff); - - if (rc >= 0) - /* found a valid AAC track */ - return i; - } - - /* can't decode this */ - return -1; -} - -static uint32_t -mp4_read(void *user_data, void *buffer, uint32_t length) -{ - struct mp4ff_input_stream *mis = user_data; - - if (length == 0) - /* libmp4ff is known to attempt to read 0 bytes - make - this a special case, because the input_stream API - would not allow this */ - return 0; - - return decoder_read(mis->decoder, mis->input_stream, buffer, length); -} - -static uint32_t -mp4_seek(void *user_data, uint64_t position) -{ - struct mp4ff_input_stream *mis = user_data; - - return input_stream_lock_seek(mis->input_stream, position, SEEK_SET, - NULL) - ? 0 : -1; -} - -static const mp4ff_callback_t mpd_mp4ff_callback = { - .read = mp4_read, - .seek = mp4_seek, -}; - -static mp4ff_t * -mp4ff_input_stream_open(struct mp4ff_input_stream *mis, - struct decoder *decoder, - struct input_stream *input_stream) -{ - mis->callback = mpd_mp4ff_callback; - mis->callback.user_data = mis; - mis->decoder = decoder; - mis->input_stream = input_stream; - - return mp4ff_open_read(&mis->callback); -} - -static faacDecHandle -mp4_faad_new(mp4ff_t *mp4fh, int *track_r, struct audio_format *audio_format) -{ - faacDecHandle decoder; - faacDecConfigurationPtr config; - int track; - uint32_t sample_rate; - unsigned char channels; - GError *error = NULL; - - decoder = faacDecOpen(); - - config = faacDecGetCurrentConfiguration(decoder); - config->outputFormat = FAAD_FMT_16BIT; -#ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX - config->downMatrix = 1; -#endif -#ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR - config->dontUpSampleImplicitSBR = 0; -#endif - faacDecSetConfiguration(decoder, config); - - track = mp4_get_aac_track(mp4fh, decoder, &sample_rate, &channels); - if (track < 0) { - g_warning("No AAC track found"); - faacDecClose(decoder); - return NULL; - } - - if (!audio_format_init_checked(audio_format, sample_rate, - SAMPLE_FORMAT_S16, channels, - &error)) { - g_warning("%s", error->message); - g_error_free(error); - faacDecClose(decoder); - return NULL; - } - - *track_r = track; - - return decoder; -} - -static void -mp4_decode(struct decoder *mpd_decoder, struct input_stream *input_stream) -{ - struct mp4ff_input_stream mis; - mp4ff_t *mp4fh; - int32_t track; - float file_time, total_time; - int32_t scale; - faacDecHandle decoder; - struct audio_format audio_format; - faacDecFrameInfo frame_info; - unsigned char *mp4_buffer; - unsigned int mp4_buffer_size; - long sample_id; - long num_samples; - long dur; - unsigned int sample_count; - char *sample_buffer; - size_t sample_buffer_length; - unsigned int initial = 1; - float *seek_table; - long seek_table_end = -1; - bool seek_position_found = false; - long offset; - uint16_t bit_rate = 0; - bool seeking = false; - double seek_where = 0; - enum decoder_command cmd = DECODE_COMMAND_NONE; - - mp4fh = mp4ff_input_stream_open(&mis, mpd_decoder, input_stream); - if (!mp4fh) { - g_warning("Input does not appear to be a mp4 stream.\n"); - return; - } - - decoder = mp4_faad_new(mp4fh, &track, &audio_format); - if (decoder == NULL) { - mp4ff_close(mp4fh); - return; - } - - file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track); - scale = mp4ff_time_scale(mp4fh, track); - - if (scale < 0) { - g_warning("Error getting audio format of mp4 AAC track.\n"); - faacDecClose(decoder); - mp4ff_close(mp4fh); - return; - } - total_time = ((float)file_time) / scale; - - num_samples = mp4ff_num_samples(mp4fh, track); - if (num_samples > (long)(G_MAXINT / sizeof(float))) { - g_warning("Integer overflow.\n"); - faacDecClose(decoder); - mp4ff_close(mp4fh); - return; - } - - file_time = 0.0; - - seek_table = input_stream->seekable - ? g_malloc(sizeof(float) * num_samples) - : NULL; - - decoder_initialized(mpd_decoder, &audio_format, - input_stream->seekable, - total_time); - - for (sample_id = 0; - sample_id < num_samples && cmd != DECODE_COMMAND_STOP; - sample_id++) { - if (cmd == DECODE_COMMAND_SEEK) { - assert(seek_table != NULL); - - seeking = true; - seek_where = decoder_seek_where(mpd_decoder); - } - - if (seeking && seek_table_end > 1 && - seek_table[seek_table_end] >= seek_where) { - int i = 2; - - assert(seek_table != NULL); - - while (seek_table[i] < seek_where) - i++; - sample_id = i - 1; - file_time = seek_table[sample_id]; - } - - dur = mp4ff_get_sample_duration(mp4fh, track, sample_id); - offset = mp4ff_get_sample_offset(mp4fh, track, sample_id); - - if (seek_table != NULL && sample_id > seek_table_end) { - seek_table[sample_id] = file_time; - seek_table_end = sample_id; - } - - if (sample_id == 0) - dur = 0; - if (offset > dur) - dur = 0; - else - dur -= offset; - file_time += ((float)dur) / scale; - - if (seeking && file_time >= seek_where) - seek_position_found = true; - - if (seeking && seek_position_found) { - seek_position_found = false; - seeking = 0; - decoder_command_finished(mpd_decoder); - } - - if (seeking) - continue; - - if (mp4ff_read_sample(mp4fh, track, sample_id, &mp4_buffer, - &mp4_buffer_size) == 0) - break; - -#ifdef HAVE_FAAD_BUFLEN_FUNCS - sample_buffer = faacDecDecode(decoder, &frame_info, mp4_buffer, - mp4_buffer_size); -#else - sample_buffer = faacDecDecode(decoder, &frame_info, mp4_buffer); -#endif - - free(mp4_buffer); - - if (frame_info.error > 0) { - g_warning("faad2 error: %s\n", - faacDecGetErrorMessage(frame_info.error)); - break; - } - - if (frame_info.channels != audio_format.channels) { - g_warning("channel count changed from %u to %u", - audio_format.channels, frame_info.channels); - break; - } - -#ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE - if (frame_info.samplerate != audio_format.sample_rate) { - g_warning("sample rate changed from %u to %lu", - audio_format.sample_rate, - (unsigned long)frame_info.samplerate); - break; - } -#endif - - if (audio_format.channels * (unsigned long)(dur + offset) > frame_info.samples) { - dur = frame_info.samples / audio_format.channels; - offset = 0; - } - - sample_count = (unsigned long)(dur * audio_format.channels); - - if (sample_count > 0) { - initial = 0; - bit_rate = frame_info.bytesconsumed * 8.0 * - frame_info.channels * scale / - frame_info.samples / 1000 + 0.5; - } - - sample_buffer_length = sample_count * 2; - - sample_buffer += offset * audio_format.channels * 2; - - cmd = decoder_data(mpd_decoder, input_stream, - sample_buffer, sample_buffer_length, - bit_rate); - } - - g_free(seek_table); - faacDecClose(decoder); - mp4ff_close(mp4fh); -} - -static const struct tag_table mp4ff_tags[] = { - { "album artist", TAG_ALBUM_ARTIST }, - { "writer", TAG_COMPOSER }, - { "band", TAG_PERFORMER }, - { NULL, TAG_NUM_OF_ITEM_TYPES } -}; - -static enum tag_type -mp4ff_tag_name_parse(const char *name) -{ - enum tag_type type = tag_table_lookup_i(mp4ff_tags, name); - if (type == TAG_NUM_OF_ITEM_TYPES) - type = tag_name_parse_i(name); - - if (g_ascii_strcasecmp(name, "albumartist") == 0 || - g_ascii_strcasecmp(name, "album_artist") == 0) - return TAG_ALBUM_ARTIST; - - return type; -} - -static bool -mp4ff_scan_stream(struct input_stream *is, - const struct tag_handler *handler, void *handler_ctx) -{ - struct mp4ff_input_stream mis; - int32_t track; - int32_t file_time; - int32_t scale; - int i; - - mp4ff_t *mp4fh = mp4ff_input_stream_open(&mis, NULL, is); - if (mp4fh == NULL) - return false; - - track = mp4_get_aac_track(mp4fh, NULL, NULL, NULL); - if (track < 0) { - mp4ff_close(mp4fh); - return false; - } - - file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track); - scale = mp4ff_time_scale(mp4fh, track); - if (scale < 0) { - mp4ff_close(mp4fh); - return false; - } - - tag_handler_invoke_duration(handler, handler_ctx, - ((float)file_time) / scale + 0.5); - - for (i = 0; i < mp4ff_meta_get_num_items(mp4fh); i++) { - char *item; - char *value; - - mp4ff_meta_get_by_index(mp4fh, i, &item, &value); - - tag_handler_invoke_pair(handler, handler_ctx, item, value); - - enum tag_type type = mp4ff_tag_name_parse(item); - if (type != TAG_NUM_OF_ITEM_TYPES) - tag_handler_invoke_tag(handler, handler_ctx, - type, value); - - free(item); - free(value); - } - - mp4ff_close(mp4fh); - - return true; -} - -static const char *const mp4_suffixes[] = { - "m4a", - "m4b", - "mp4", - NULL -}; - -static const char *const mp4_mime_types[] = { "audio/mp4", "audio/m4a", NULL }; - -const struct decoder_plugin mp4ff_decoder_plugin = { - .name = "mp4ff", - .stream_decode = mp4_decode, - .scan_stream = mp4ff_scan_stream, - .suffixes = mp4_suffixes, - .mime_types = mp4_mime_types, -}; diff --git a/src/decoder/mpcdec_decoder_plugin.c b/src/decoder/mpcdec_decoder_plugin.c deleted file mode 100644 index d4768b35..00000000 --- a/src/decoder/mpcdec_decoder_plugin.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "decoder_api.h" -#include "audio_check.h" -#include "tag_handler.h" - -#ifdef MPC_IS_OLD_API -#include <mpcdec/mpcdec.h> -#else -#include <mpc/mpcdec.h> -#include <math.h> -#endif - -#include <glib.h> -#include <assert.h> -#include <unistd.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "mpcdec" - -struct mpc_decoder_data { - struct input_stream *is; - struct decoder *decoder; -}; - -#ifdef MPC_IS_OLD_API -#define cb_first_arg void *vdata -#define cb_data vdata -#else -#define cb_first_arg mpc_reader *reader -#define cb_data reader->data -#endif - -static mpc_int32_t -mpc_read_cb(cb_first_arg, void *ptr, mpc_int32_t size) -{ - struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data; - - return decoder_read(data->decoder, data->is, ptr, size); -} - -static mpc_bool_t -mpc_seek_cb(cb_first_arg, mpc_int32_t offset) -{ - struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data; - - return input_stream_lock_seek(data->is, offset, SEEK_SET, NULL); -} - -static mpc_int32_t -mpc_tell_cb(cb_first_arg) -{ - struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data; - - return (long)(data->is->offset); -} - -static mpc_bool_t -mpc_canseek_cb(cb_first_arg) -{ - struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data; - - return data->is->seekable; -} - -static mpc_int32_t -mpc_getsize_cb(cb_first_arg) -{ - struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data; - - return data->is->size; -} - -/* this _looks_ performance-critical, don't de-inline -- eric */ -static inline int32_t -mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample) -{ - /* only doing 16-bit audio for now */ - int32_t val; - - enum { - bits = 24, - }; - - const int clip_min = -1 << (bits - 1); - const int clip_max = (1 << (bits - 1)) - 1; - -#ifdef MPC_FIXED_POINT - const int shift = bits - MPC_FIXED_POINT_SCALE_SHIFT; - - if (shift < 0) - val = sample >> -shift; - else - val = sample << shift; -#else - const int float_scale = 1 << (bits - 1); - - val = sample * float_scale; -#endif - - if (val < clip_min) - val = clip_min; - else if (val > clip_max) - val = clip_max; - - return val; -} - -static void -mpc_to_mpd_buffer(int32_t *dest, const MPC_SAMPLE_FORMAT *src, - unsigned num_samples) -{ - while (num_samples-- > 0) - *dest++ = mpc_to_mpd_sample(*src++); -} - -static void -mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is) -{ -#ifdef MPC_IS_OLD_API - mpc_decoder decoder; -#else - mpc_demux *demux; - mpc_frame_info frame; - mpc_status status; -#endif - mpc_reader reader; - mpc_streaminfo info; - GError *error = NULL; - struct audio_format audio_format; - - struct mpc_decoder_data data; - - MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH]; - - mpc_uint32_t ret; - int32_t chunk[G_N_ELEMENTS(sample_buffer)]; - long bit_rate = 0; - mpc_uint32_t vbr_update_bits; - enum decoder_command cmd = DECODE_COMMAND_NONE; - - data.is = is; - data.decoder = mpd_decoder; - - reader.read = mpc_read_cb; - reader.seek = mpc_seek_cb; - reader.tell = mpc_tell_cb; - reader.get_size = mpc_getsize_cb; - reader.canseek = mpc_canseek_cb; - reader.data = &data; - -#ifdef MPC_IS_OLD_API - mpc_streaminfo_init(&info); - - if ((ret = mpc_streaminfo_read(&info, &reader)) != ERROR_CODE_OK) { - if (decoder_get_command(mpd_decoder) != DECODE_COMMAND_STOP) - g_warning("Not a valid musepack stream\n"); - return; - } - - mpc_decoder_setup(&decoder, &reader); - - if (!mpc_decoder_initialize(&decoder, &info)) { - if (decoder_get_command(mpd_decoder) != DECODE_COMMAND_STOP) - g_warning("Not a valid musepack stream\n"); - return; - } -#else - demux = mpc_demux_init(&reader); - if (demux == NULL) { - if (decoder_get_command(mpd_decoder) != DECODE_COMMAND_STOP) - g_warning("Not a valid musepack stream"); - return; - } - - mpc_demux_get_info(demux, &info); -#endif - - if (!audio_format_init_checked(&audio_format, info.sample_freq, - SAMPLE_FORMAT_S24_P32, - info.channels, &error)) { - g_warning("%s", error->message); - g_error_free(error); -#ifndef MPC_IS_OLD_API - mpc_demux_exit(demux); -#endif - return; - } - - struct replay_gain_info replay_gain_info; - replay_gain_info_init(&replay_gain_info); -#ifdef MPC_IS_OLD_API - replay_gain_info.tuples[REPLAY_GAIN_ALBUM].gain = info.gain_album * 0.01; - replay_gain_info.tuples[REPLAY_GAIN_ALBUM].peak = info.peak_album / 32767.0; - replay_gain_info.tuples[REPLAY_GAIN_TRACK].gain = info.gain_title * 0.01; - replay_gain_info.tuples[REPLAY_GAIN_TRACK].peak = info.peak_title / 32767.0; -#else - replay_gain_info.tuples[REPLAY_GAIN_ALBUM].gain = MPC_OLD_GAIN_REF - (info.gain_album / 256.); - replay_gain_info.tuples[REPLAY_GAIN_ALBUM].peak = pow(10, info.peak_album / 256. / 20) / 32767; - replay_gain_info.tuples[REPLAY_GAIN_TRACK].gain = MPC_OLD_GAIN_REF - (info.gain_title / 256.); - replay_gain_info.tuples[REPLAY_GAIN_TRACK].peak = pow(10, info.peak_title / 256. / 20) / 32767; -#endif - - decoder_replay_gain(mpd_decoder, &replay_gain_info); - - decoder_initialized(mpd_decoder, &audio_format, - is->seekable, - mpc_streaminfo_get_length(&info)); - - do { - if (cmd == DECODE_COMMAND_SEEK) { - mpc_int64_t where = decoder_seek_where(mpd_decoder) * - audio_format.sample_rate; - bool success; - -#ifdef MPC_IS_OLD_API - success = mpc_decoder_seek_sample(&decoder, where); -#else - success = mpc_demux_seek_sample(demux, where) - == MPC_STATUS_OK; -#endif - if (success) - decoder_command_finished(mpd_decoder); - else - decoder_seek_error(mpd_decoder); - } - - vbr_update_bits = 0; - -#ifdef MPC_IS_OLD_API - mpc_uint32_t vbr_update_acc = 0; - - ret = mpc_decoder_decode(&decoder, sample_buffer, - &vbr_update_acc, &vbr_update_bits); - if (ret == 0 || ret == (mpc_uint32_t)-1) - break; -#else - frame.buffer = (MPC_SAMPLE_FORMAT *)sample_buffer; - status = mpc_demux_decode(demux, &frame); - if (status != MPC_STATUS_OK) { - g_warning("Failed to decode sample"); - break; - } - - if (frame.bits == -1) - break; - - ret = frame.samples; -#endif - - ret *= info.channels; - - mpc_to_mpd_buffer(chunk, sample_buffer, ret); - - bit_rate = vbr_update_bits * audio_format.sample_rate - / 1152 / 1000; - - cmd = decoder_data(mpd_decoder, is, - chunk, ret * sizeof(chunk[0]), - bit_rate); - } while (cmd != DECODE_COMMAND_STOP); - -#ifndef MPC_IS_OLD_API - mpc_demux_exit(demux); -#endif -} - -static float -mpcdec_get_file_duration(struct input_stream *is) -{ - float total_time = -1; - - mpc_reader reader; -#ifndef MPC_IS_OLD_API - mpc_demux *demux; -#endif - mpc_streaminfo info; - struct mpc_decoder_data data; - - data.is = is; - data.decoder = NULL; - - reader.read = mpc_read_cb; - reader.seek = mpc_seek_cb; - reader.tell = mpc_tell_cb; - reader.get_size = mpc_getsize_cb; - reader.canseek = mpc_canseek_cb; - reader.data = &data; - -#ifdef MPC_IS_OLD_API - mpc_streaminfo_init(&info); - - if (mpc_streaminfo_read(&info, &reader) != ERROR_CODE_OK) - return -1; -#else - demux = mpc_demux_init(&reader); - if (demux == NULL) - return -1; - - mpc_demux_get_info(demux, &info); - mpc_demux_exit(demux); -#endif - - total_time = mpc_streaminfo_get_length(&info); - - return total_time; -} - -static bool -mpcdec_scan_stream(struct input_stream *is, - const struct tag_handler *handler, void *handler_ctx) -{ - float total_time = mpcdec_get_file_duration(is); - - if (total_time < 0) - return false; - - tag_handler_invoke_duration(handler, handler_ctx, total_time); - return true; -} - -static const char *const mpcdec_suffixes[] = { "mpc", NULL }; - -const struct decoder_plugin mpcdec_decoder_plugin = { - .name = "mpcdec", - .stream_decode = mpcdec_decode, - .scan_stream = mpcdec_scan_stream, - .suffixes = mpcdec_suffixes, -}; diff --git a/src/decoder/mpg123_decoder_plugin.c b/src/decoder/mpg123_decoder_plugin.c deleted file mode 100644 index 657a9c88..00000000 --- a/src/decoder/mpg123_decoder_plugin.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" /* must be first for large file support */ -#include "decoder_api.h" -#include "audio_check.h" -#include "tag_handler.h" - -#include <glib.h> - -#include <mpg123.h> -#include <stdio.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "mpg123" - -static bool -mpd_mpg123_init(G_GNUC_UNUSED const struct config_param *param) -{ - mpg123_init(); - - return true; -} - -static void -mpd_mpg123_finish(void) -{ - mpg123_exit(); -} - -/** - * Opens a file with an existing #mpg123_handle. - * - * @param handle a handle which was created before; on error, this - * function will not free it - * @param audio_format this parameter is filled after successful - * return - * @return true on success - */ -static bool -mpd_mpg123_open(mpg123_handle *handle, const char *path_fs, - struct audio_format *audio_format) -{ - GError *gerror = NULL; - char *path_dup; - int error; - int channels, encoding; - long rate; - - /* mpg123_open() wants a writable string :-( */ - path_dup = g_strdup(path_fs); - - error = mpg123_open(handle, path_dup); - g_free(path_dup); - if (error != MPG123_OK) { - g_warning("libmpg123 failed to open %s: %s", - path_fs, mpg123_plain_strerror(error)); - return false; - } - - /* obtain the audio format */ - - error = mpg123_getformat(handle, &rate, &channels, &encoding); - if (error != MPG123_OK) { - g_warning("mpg123_getformat() failed: %s", - mpg123_plain_strerror(error)); - return false; - } - - if (encoding != MPG123_ENC_SIGNED_16) { - /* other formats not yet implemented */ - g_warning("expected MPG123_ENC_SIGNED_16, got %d", encoding); - return false; - } - - if (!audio_format_init_checked(audio_format, rate, SAMPLE_FORMAT_S16, - channels, &gerror)) { - g_warning("%s", gerror->message); - g_error_free(gerror); - return false; - } - - return true; -} - -static void -mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs) -{ - struct audio_format audio_format; - mpg123_handle *handle; - int error; - off_t num_samples; - enum decoder_command cmd; - struct mpg123_frameinfo info; - - /* open the file */ - - handle = mpg123_new(NULL, &error); - if (handle == NULL) { - g_warning("mpg123_new() failed: %s", - mpg123_plain_strerror(error)); - return; - } - - if (!mpd_mpg123_open(handle, path_fs, &audio_format)) { - mpg123_delete(handle); - return; - } - - num_samples = mpg123_length(handle); - - /* tell MPD core we're ready */ - - decoder_initialized(decoder, &audio_format, true, - (float)num_samples / - (float)audio_format.sample_rate); - - if (mpg123_info(handle, &info) != MPG123_OK) { - info.vbr = MPG123_CBR; - info.bitrate = 0; - } - - switch (info.vbr) { - case MPG123_ABR: - info.bitrate = info.abr_rate; - break; - case MPG123_CBR: - break; - default: - info.bitrate = 0; - } - - /* the decoder main loop */ - - do { - unsigned char buffer[8192]; - size_t nbytes; - - /* decode */ - - error = mpg123_read(handle, buffer, sizeof(buffer), &nbytes); - if (error != MPG123_OK) { - if (error != MPG123_DONE) - g_warning("mpg123_read() failed: %s", - mpg123_plain_strerror(error)); - break; - } - - /* update bitrate for ABR/VBR */ - if (info.vbr != MPG123_CBR) { - /* FIXME: maybe skip, as too expensive? */ - /* FIXME: maybe, (info.vbr == MPG123_VBR) ? */ - if (mpg123_info (handle, &info) != MPG123_OK) - info.bitrate = 0; - } - - /* send to MPD */ - - cmd = decoder_data(decoder, NULL, buffer, nbytes, info.bitrate); - - if (cmd == DECODE_COMMAND_SEEK) { - off_t c = decoder_seek_where(decoder)*audio_format.sample_rate; - c = mpg123_seek(handle, c, SEEK_SET); - if (c < 0) - decoder_seek_error(decoder); - else { - decoder_command_finished(decoder); - decoder_timestamp(decoder, c/(double)audio_format.sample_rate); - } - - cmd = DECODE_COMMAND_NONE; - } - } while (cmd == DECODE_COMMAND_NONE); - - /* cleanup */ - - mpg123_delete(handle); -} - -static bool -mpd_mpg123_scan_file(const char *path_fs, - const struct tag_handler *handler, void *handler_ctx) -{ - struct audio_format audio_format; - mpg123_handle *handle; - int error; - off_t num_samples; - - handle = mpg123_new(NULL, &error); - if (handle == NULL) { - g_warning("mpg123_new() failed: %s", - mpg123_plain_strerror(error)); - return false; - } - - if (!mpd_mpg123_open(handle, path_fs, &audio_format)) { - mpg123_delete(handle); - return false; - } - - num_samples = mpg123_length(handle); - if (num_samples <= 0) { - mpg123_delete(handle); - return false; - } - - /* ID3 tag support not yet implemented */ - - mpg123_delete(handle); - - tag_handler_invoke_duration(handler, handler_ctx, - num_samples / audio_format.sample_rate); - return true; -} - -static const char *const mpg123_suffixes[] = { - "mp3", - NULL -}; - -const struct decoder_plugin mpg123_decoder_plugin = { - .name = "mpg123", - .init = mpd_mpg123_init, - .finish = mpd_mpg123_finish, - .file_decode = mpd_mpg123_file_decode, - /* streaming not yet implemented */ - .scan_file = mpd_mpg123_scan_file, - .suffixes = mpg123_suffixes, -}; diff --git a/src/decoder/pcm_decoder_plugin.c b/src/decoder/pcm_decoder_plugin.c deleted file mode 100644 index fc7dffc0..00000000 --- a/src/decoder/pcm_decoder_plugin.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "decoder/pcm_decoder_plugin.h" -#include "decoder_api.h" -#include "util/byte_reverse.h" - -#include <glib.h> -#include <unistd.h> -#include <stdio.h> /* for SEEK_SET */ - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "pcm" - -static void -pcm_stream_decode(struct decoder *decoder, struct input_stream *is) -{ - static const struct audio_format audio_format = { - .sample_rate = 44100, - .format = SAMPLE_FORMAT_S16, - .channels = 2, - }; - - const bool reverse_endian = is->mime != NULL && - strcmp(is->mime, "audio/x-mpd-cdda-pcm-reverse") == 0; - - GError *error = NULL; - enum decoder_command cmd; - - double time_to_size = audio_format_time_to_size(&audio_format); - - float total_time = -1; - if (is->size >= 0) - total_time = is->size / time_to_size; - - decoder_initialized(decoder, &audio_format, is->seekable, total_time); - - do { - char buffer[4096]; - - size_t nbytes = decoder_read(decoder, is, - buffer, sizeof(buffer)); - - if (nbytes == 0 && input_stream_lock_eof(is)) - break; - - if (reverse_endian) - /* make sure we deliver samples in host byte order */ - reverse_bytes_16((uint16_t *)buffer, - (uint16_t *)buffer, - (uint16_t *)(buffer + nbytes)); - - cmd = nbytes > 0 - ? decoder_data(decoder, is, - buffer, nbytes, 0) - : decoder_get_command(decoder); - if (cmd == DECODE_COMMAND_SEEK) { - goffset offset = (goffset)(time_to_size * - decoder_seek_where(decoder)); - if (input_stream_lock_seek(is, offset, SEEK_SET, - &error)) { - decoder_command_finished(decoder); - } else { - g_warning("seeking failed: %s", error->message); - g_error_free(error); - decoder_seek_error(decoder); - } - - cmd = DECODE_COMMAND_NONE; - } - } while (cmd == DECODE_COMMAND_NONE); -} - -static const char *const pcm_mime_types[] = { - /* for streams obtained by the cdio_paranoia input plugin */ - "audio/x-mpd-cdda-pcm", - - /* same as above, but with reverse byte order */ - "audio/x-mpd-cdda-pcm-reverse", - - NULL -}; - -const struct decoder_plugin pcm_decoder_plugin = { - .name = "pcm", - .stream_decode = pcm_stream_decode, - .mime_types = pcm_mime_types, -}; diff --git a/src/decoder/pcm_decoder_plugin.h b/src/decoder/pcm_decoder_plugin.h deleted file mode 100644 index 11df8015..00000000 --- a/src/decoder/pcm_decoder_plugin.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/** \file - * - * Not really a decoder; this plugin forwards its input data "as-is". - * - * It was written only to support the "cdio_paranoia" input plugin, - * which does not need a decoder. - */ - -#ifndef MPD_DECODER_PCM_H -#define MPD_DECODER_PCM_H - -extern const struct decoder_plugin pcm_decoder_plugin; - -#endif diff --git a/src/decoder/sidplay_decoder_plugin.cxx b/src/decoder/sidplay_decoder_plugin.cxx deleted file mode 100644 index 5d162f17..00000000 --- a/src/decoder/sidplay_decoder_plugin.cxx +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" - -extern "C" { -#include "../decoder_api.h" -#include "tag_handler.h" -} - -#include <errno.h> -#include <stdlib.h> -#include <glib.h> - -#include <sidplay/sidplay2.h> -#include <sidplay/builders/resid.h> -#include <sidplay/utils/SidTuneMod.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "sidplay" - -#define SUBTUNE_PREFIX "tune_" - -static GPatternSpec *path_with_subtune; -static const char *songlength_file; -static GKeyFile *songlength_database; - -static bool all_files_are_containers; -static unsigned default_songlength; - -static bool filter_setting; - -static GKeyFile * -sidplay_load_songlength_db(const char *path) -{ - GError *error = NULL; - gchar *data; - gsize size; - - if (!g_file_get_contents(path, &data, &size, &error)) { - g_warning("unable to read songlengths file %s: %s", - path, error->message); - g_error_free(error); - return NULL; - } - - /* replace any ; comment characters with # */ - for (gsize i = 0; i < size; i++) - if (data[i] == ';') - data[i] = '#'; - - GKeyFile *db = g_key_file_new(); - bool success = g_key_file_load_from_data(db, data, size, - G_KEY_FILE_NONE, &error); - g_free(data); - if (!success) { - g_warning("unable to parse songlengths file %s: %s", - path, error->message); - g_error_free(error); - g_key_file_free(db); - return NULL; - } - - g_key_file_set_list_separator(db, ' '); - return db; -} - -static bool -sidplay_init(const struct config_param *param) -{ - /* read the songlengths database file */ - songlength_file=config_get_block_string(param, - "songlength_database", NULL); - if (songlength_file != NULL) - songlength_database = sidplay_load_songlength_db(songlength_file); - - default_songlength=config_get_block_unsigned(param, - "default_songlength", 0); - - all_files_are_containers=config_get_block_bool(param, - "all_files_are_containers", true); - - path_with_subtune=g_pattern_spec_new( - "*/" SUBTUNE_PREFIX "???.sid"); - - filter_setting=config_get_block_bool(param, "filter", true); - - return true; -} - -void -sidplay_finish() -{ - g_pattern_spec_free(path_with_subtune); - - if(songlength_database) - g_key_file_free(songlength_database); -} - -/** - * returns the file path stripped of any /tune_xxx.sid subtune - * suffix - */ -static char * -get_container_name(const char *path_fs) -{ - char *path_container=g_strdup(path_fs); - - if(!g_pattern_match(path_with_subtune, - strlen(path_container), path_container, NULL)) - return path_container; - - char *ptr=g_strrstr(path_container, "/" SUBTUNE_PREFIX); - if(ptr) *ptr='\0'; - - return path_container; -} - -/** - * returns tune number from file.sid/tune_xxx.sid style path or 1 if - * no subtune is appended - */ -static int -get_song_num(const char *path_fs) -{ - if(g_pattern_match(path_with_subtune, - strlen(path_fs), path_fs, NULL)) { - char *sub=g_strrstr(path_fs, "/" SUBTUNE_PREFIX); - if(!sub) return 1; - - sub+=strlen("/" SUBTUNE_PREFIX); - int song_num=strtol(sub, NULL, 10); - - if (errno == EINVAL) - return 1; - else - return song_num; - } else - return 1; -} - -/* get the song length in seconds */ -static int -get_song_length(const char *path_fs) -{ - if (songlength_database == NULL) - return -1; - - gchar *sid_file=get_container_name(path_fs); - SidTuneMod tune(sid_file); - g_free(sid_file); - if(!tune) { - g_warning("failed to load file for calculating md5 sum"); - return -1; - } - char md5sum[SIDTUNE_MD5_LENGTH+1]; - tune.createMD5(md5sum); - - int song_num=get_song_num(path_fs); - - gsize num_items; - gchar **values=g_key_file_get_string_list(songlength_database, - "Database", md5sum, &num_items, NULL); - if(!values || song_num>num_items) { - g_strfreev(values); - return -1; - } - - int minutes=strtol(values[song_num-1], NULL, 10); - if(errno==EINVAL) minutes=0; - - int seconds; - char *ptr=strchr(values[song_num-1], ':'); - if(ptr) { - seconds=strtol(ptr+1, NULL, 10); - if(errno==EINVAL) seconds=0; - } else - seconds=0; - - g_strfreev(values); - - return (minutes*60)+seconds; -} - -static void -sidplay_file_decode(struct decoder *decoder, const char *path_fs) -{ - int channels; - - /* load the tune */ - - char *path_container=get_container_name(path_fs); - SidTune tune(path_container, NULL, true); - g_free(path_container); - if (!tune) { - g_warning("failed to load file"); - return; - } - - int song_num=get_song_num(path_fs); - tune.selectSong(song_num); - - int song_len=get_song_length(path_fs); - if(song_len==-1) song_len=default_songlength; - - /* initialize the player */ - - sidplay2 player; - int iret = player.load(&tune); - if (iret != 0) { - g_warning("sidplay2.load() failed: %s", player.error()); - return; - } - - /* initialize the builder */ - - ReSIDBuilder builder("ReSID"); - if (!builder) { - g_warning("failed to initialize ReSIDBuilder"); - return; - } - - builder.create(player.info().maxsids); - if (!builder) { - g_warning("ReSIDBuilder.create() failed"); - return; - } - - builder.filter(filter_setting); - if (!builder) { - g_warning("ReSIDBuilder.filter() failed"); - return; - } - - /* configure the player */ - - sid2_config_t config = player.config(); - - config.clockDefault = SID2_CLOCK_PAL; - config.clockForced = true; - config.clockSpeed = SID2_CLOCK_CORRECT; - config.frequency = 48000; - config.optimisation = SID2_DEFAULT_OPTIMISATION; - - config.precision = 16; - config.sidDefault = SID2_MOS6581; - config.sidEmulation = &builder; - config.sidModel = SID2_MODEL_CORRECT; - config.sidSamples = true; -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - config.sampleFormat = SID2_LITTLE_SIGNED; -#else - config.sampleFormat = SID2_BIG_SIGNED; -#endif - if (tune.isStereo()) { - config.playback = sid2_stereo; - channels = 2; - } else { - config.playback = sid2_mono; - channels = 1; - } - - iret = player.config(config); - if (iret != 0) { - g_warning("sidplay2.config() failed: %s", player.error()); - return; - } - - /* initialize the MPD decoder */ - - struct audio_format audio_format; - audio_format_init(&audio_format, 48000, SAMPLE_FORMAT_S16, channels); - assert(audio_format_valid(&audio_format)); - - decoder_initialized(decoder, &audio_format, true, (float)song_len); - - /* .. and play */ - - const unsigned timebase = player.timebase(); - song_len *= timebase; - - enum decoder_command cmd; - do { - char buffer[4096]; - size_t nbytes; - - nbytes = player.play(buffer, sizeof(buffer)); - if (nbytes == 0) - break; - - decoder_timestamp(decoder, (double)player.time() / timebase); - - cmd = decoder_data(decoder, NULL, buffer, nbytes, 0); - - if(cmd==DECODE_COMMAND_SEEK) { - unsigned data_time = player.time(); - unsigned target_time = (unsigned) - (decoder_seek_where(decoder) * timebase); - - /* can't rewind so return to zero and seek forward */ - if(target_time<data_time) { - player.stop(); - data_time=0; - } - - /* ignore data until target time is reached */ - while(data_time<target_time) { - nbytes=player.play(buffer, sizeof(buffer)); - if(nbytes==0) - break; - data_time = player.time(); - } - - decoder_command_finished(decoder); - } - - if (song_len > 0 && player.time() >= song_len) - break; - - } while (cmd != DECODE_COMMAND_STOP); -} - -static bool -sidplay_scan_file(const char *path_fs, - const struct tag_handler *handler, void *handler_ctx) -{ - int song_num=get_song_num(path_fs); - char *path_container=get_container_name(path_fs); - - SidTune tune(path_container, NULL, true); - g_free(path_container); - if (!tune) - return false; - - const SidTuneInfo &info = tune.getInfo(); - - /* title */ - const char *title; - if (info.numberOfInfoStrings > 0 && info.infoString[0] != NULL) - title=info.infoString[0]; - else - title=""; - - if(info.songs>1) { - char *tag_title=g_strdup_printf("%s (%d/%d)", - title, song_num, info.songs); - tag_handler_invoke_tag(handler, handler_ctx, - TAG_TITLE, tag_title); - g_free(tag_title); - } else - tag_handler_invoke_tag(handler, handler_ctx, TAG_TITLE, title); - - /* artist */ - if (info.numberOfInfoStrings > 1 && info.infoString[1] != NULL) - tag_handler_invoke_tag(handler, handler_ctx, TAG_ARTIST, - info.infoString[1]); - - /* track */ - char *track=g_strdup_printf("%d", song_num); - tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track); - g_free(track); - - /* time */ - int song_len=get_song_length(path_fs); - if (song_len >= 0) - tag_handler_invoke_duration(handler, handler_ctx, song_len); - - return true; -} - -static char * -sidplay_container_scan(const char *path_fs, const unsigned int tnum) -{ - SidTune tune(path_fs, NULL, true); - if (!tune) - return NULL; - - const SidTuneInfo &info=tune.getInfo(); - - /* Don't treat sids containing a single tune - as containers */ - if(!all_files_are_containers && info.songs<2) - return NULL; - - /* Construct container/tune path names, eg. - Delta.sid/tune_001.sid */ - if(tnum<=info.songs) { - char *subtune= g_strdup_printf( - SUBTUNE_PREFIX "%03u.sid", tnum); - return subtune; - } else - return NULL; -} - -static const char *const sidplay_suffixes[] = { - "sid", - "mus", - "str", - "prg", - "P00", - NULL -}; - -extern const struct decoder_plugin sidplay_decoder_plugin; -const struct decoder_plugin sidplay_decoder_plugin = { - "sidplay", - sidplay_init, - sidplay_finish, - NULL, /* stream_decode() */ - sidplay_file_decode, - sidplay_scan_file, - NULL, /* stream_tag() */ - sidplay_container_scan, - sidplay_suffixes, - NULL, /* mime_types */ -}; diff --git a/src/decoder/sndfile_decoder_plugin.c b/src/decoder/sndfile_decoder_plugin.c deleted file mode 100644 index 8dd98236..00000000 --- a/src/decoder/sndfile_decoder_plugin.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "decoder_api.h" -#include "audio_check.h" -#include "tag_handler.h" - -#include <sndfile.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "sndfile" - -static sf_count_t -sndfile_vio_get_filelen(void *user_data) -{ - const struct input_stream *is = user_data; - - return is->size; -} - -static sf_count_t -sndfile_vio_seek(sf_count_t offset, int whence, void *user_data) -{ - struct input_stream *is = user_data; - bool success; - - success = input_stream_lock_seek(is, offset, whence, NULL); - if (!success) - return -1; - - return is->offset; -} - -static sf_count_t -sndfile_vio_read(void *ptr, sf_count_t count, void *user_data) -{ - struct input_stream *is = user_data; - GError *error = NULL; - size_t nbytes; - - nbytes = input_stream_lock_read(is, ptr, count, &error); - if (nbytes == 0 && error != NULL) { - g_warning("%s", error->message); - g_error_free(error); - return -1; - } - - return nbytes; -} - -static sf_count_t -sndfile_vio_write(G_GNUC_UNUSED const void *ptr, - G_GNUC_UNUSED sf_count_t count, - G_GNUC_UNUSED void *user_data) -{ - /* no writing! */ - return -1; -} - -static sf_count_t -sndfile_vio_tell(void *user_data) -{ - const struct input_stream *is = user_data; - - return is->offset; -} - -/** - * This SF_VIRTUAL_IO implementation wraps MPD's #input_stream to a - * libsndfile stream. - */ -static SF_VIRTUAL_IO vio = { - .get_filelen = sndfile_vio_get_filelen, - .seek = sndfile_vio_seek, - .read = sndfile_vio_read, - .write = sndfile_vio_write, - .tell = sndfile_vio_tell, -}; - -/** - * Converts a frame number to a timestamp (in seconds). - */ -static float -frame_to_time(sf_count_t frame, const struct audio_format *audio_format) -{ - return (float)frame / (float)audio_format->sample_rate; -} - -/** - * Converts a timestamp (in seconds) to a frame number. - */ -static sf_count_t -time_to_frame(float t, const struct audio_format *audio_format) -{ - return (sf_count_t)(t * audio_format->sample_rate); -} - -static void -sndfile_stream_decode(struct decoder *decoder, struct input_stream *is) -{ - GError *error = NULL; - SNDFILE *sf; - SF_INFO info; - struct audio_format audio_format; - size_t frame_size; - sf_count_t read_frames, num_frames; - int buffer[4096]; - enum decoder_command cmd; - - info.format = 0; - - sf = sf_open_virtual(&vio, SFM_READ, &info, is); - if (sf == NULL) { - g_warning("sf_open_virtual() failed"); - return; - } - - /* for now, always read 32 bit samples. Later, we could lower - MPD's CPU usage by reading 16 bit samples with - sf_readf_short() on low-quality source files. */ - if (!audio_format_init_checked(&audio_format, info.samplerate, - SAMPLE_FORMAT_S32, - info.channels, &error)) { - g_warning("%s", error->message); - g_error_free(error); - return; - } - - decoder_initialized(decoder, &audio_format, info.seekable, - frame_to_time(info.frames, &audio_format)); - - frame_size = audio_format_frame_size(&audio_format); - read_frames = sizeof(buffer) / frame_size; - - do { - num_frames = sf_readf_int(sf, buffer, read_frames); - if (num_frames <= 0) - break; - - cmd = decoder_data(decoder, is, - buffer, num_frames * frame_size, - 0); - if (cmd == DECODE_COMMAND_SEEK) { - sf_count_t c = - time_to_frame(decoder_seek_where(decoder), - &audio_format); - c = sf_seek(sf, c, SEEK_SET); - if (c < 0) - decoder_seek_error(decoder); - else - decoder_command_finished(decoder); - cmd = DECODE_COMMAND_NONE; - } - } while (cmd == DECODE_COMMAND_NONE); - - sf_close(sf); -} - -static bool -sndfile_scan_file(const char *path_fs, - const struct tag_handler *handler, void *handler_ctx) -{ - SNDFILE *sf; - SF_INFO info; - const char *p; - - info.format = 0; - - sf = sf_open(path_fs, SFM_READ, &info); - if (sf == NULL) - return false; - - if (!audio_valid_sample_rate(info.samplerate)) { - sf_close(sf); - g_warning("Invalid sample rate in %s\n", path_fs); - return false; - } - - tag_handler_invoke_duration(handler, handler_ctx, - info.frames / info.samplerate); - - p = sf_get_string(sf, SF_STR_TITLE); - if (p != NULL) - tag_handler_invoke_tag(handler, handler_ctx, - TAG_TITLE, p); - - p = sf_get_string(sf, SF_STR_ARTIST); - if (p != NULL) - tag_handler_invoke_tag(handler, handler_ctx, - TAG_ARTIST, p); - - p = sf_get_string(sf, SF_STR_DATE); - if (p != NULL) - tag_handler_invoke_tag(handler, handler_ctx, - TAG_DATE, p); - - sf_close(sf); - - return true; -} - -static const char *const sndfile_suffixes[] = { - "wav", "aiff", "aif", /* Microsoft / SGI / Apple */ - "au", "snd", /* Sun / DEC / NeXT */ - "paf", /* Paris Audio File */ - "iff", "svx", /* Commodore Amiga IFF / SVX */ - "sf", /* IRCAM */ - "voc", /* Creative */ - "w64", /* Soundforge */ - "pvf", /* Portable Voice Format */ - "xi", /* Fasttracker */ - "htk", /* HMM Tool Kit */ - "caf", /* Apple */ - "sd2", /* Sound Designer II */ - - /* libsndfile also supports FLAC and Ogg Vorbis, but only by - linking with libFLAC and libvorbis - we can do better, we - have native plugins for these libraries */ - - NULL -}; - -static const char *const sndfile_mime_types[] = { - "audio/x-wav", - "audio/x-aiff", - - /* what are the MIME types of the other supported formats? */ - - NULL -}; - -const struct decoder_plugin sndfile_decoder_plugin = { - .name = "sndfile", - .stream_decode = sndfile_stream_decode, - .scan_file = sndfile_scan_file, - .suffixes = sndfile_suffixes, - .mime_types = sndfile_mime_types, -}; diff --git a/src/decoder/vorbis_comments.c b/src/decoder/vorbis_comments.c deleted file mode 100644 index 6c2d57b7..00000000 --- a/src/decoder/vorbis_comments.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "vorbis_comments.h" -#include "tag.h" -#include "tag_table.h" -#include "tag_handler.h" -#include "replay_gain_info.h" - -#include <glib.h> -#include <assert.h> -#include <stddef.h> -#include <string.h> -#include <stdlib.h> - -static const char * -vorbis_comment_value(const char *comment, const char *needle) -{ - size_t len = strlen(needle); - - if (g_ascii_strncasecmp(comment, needle, len) == 0 && - comment[len] == '=') - return comment + len + 1; - - return NULL; -} - -bool -vorbis_comments_to_replay_gain(struct replay_gain_info *rgi, char **comments) -{ - const char *temp; - bool found = false; - - replay_gain_info_init(rgi); - - while (*comments) { - if ((temp = - vorbis_comment_value(*comments, "replaygain_track_gain"))) { - rgi->tuples[REPLAY_GAIN_TRACK].gain = atof(temp); - found = true; - } else if ((temp = vorbis_comment_value(*comments, - "replaygain_album_gain"))) { - rgi->tuples[REPLAY_GAIN_ALBUM].gain = atof(temp); - found = true; - } else if ((temp = vorbis_comment_value(*comments, - "replaygain_track_peak"))) { - rgi->tuples[REPLAY_GAIN_TRACK].peak = atof(temp); - found = true; - } else if ((temp = vorbis_comment_value(*comments, - "replaygain_album_peak"))) { - rgi->tuples[REPLAY_GAIN_ALBUM].peak = atof(temp); - found = true; - } - - comments++; - } - - return found; -} - -/** - * Check if the comment's name equals the passed name, and if so, copy - * the comment value into the tag. - */ -static bool -vorbis_copy_comment(const char *comment, - const char *name, enum tag_type tag_type, - const struct tag_handler *handler, void *handler_ctx) -{ - const char *value; - - value = vorbis_comment_value(comment, name); - if (value != NULL) { - tag_handler_invoke_tag(handler, handler_ctx, tag_type, value); - return true; - } - - return false; -} - -static const struct tag_table vorbis_tags[] = { - { "tracknumber", TAG_TRACK }, - { "discnumber", TAG_DISC }, - { "album artist", TAG_ALBUM_ARTIST }, - { NULL, TAG_NUM_OF_ITEM_TYPES } -}; - -static void -vorbis_scan_comment(const char *comment, - const struct tag_handler *handler, void *handler_ctx) -{ - if (handler->pair != NULL) { - char *name = g_strdup((const char*)comment); - char *value = strchr(name, '='); - - if (value != NULL && value > name) { - *value++ = 0; - tag_handler_invoke_pair(handler, handler_ctx, - name, value); - } - - g_free(name); - } - - for (const struct tag_table *i = vorbis_tags; i->name != NULL; ++i) - if (vorbis_copy_comment(comment, i->name, i->type, - handler, handler_ctx)) - return; - - for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) - if (vorbis_copy_comment(comment, - tag_item_names[i], i, - handler, handler_ctx)) - return; -} - -void -vorbis_comments_scan(char **comments, - const struct tag_handler *handler, void *handler_ctx) -{ - while (*comments) - vorbis_scan_comment(*comments++, - handler, handler_ctx); - -} - -struct tag * -vorbis_comments_to_tag(char **comments) -{ - struct tag *tag = tag_new(); - vorbis_comments_scan(comments, &add_tag_handler, tag); - - if (tag_is_empty(tag)) { - tag_free(tag); - tag = NULL; - } - - return tag; -} diff --git a/src/decoder/vorbis_comments.h b/src/decoder/vorbis_comments.h deleted file mode 100644 index c1509693..00000000 --- a/src/decoder/vorbis_comments.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2003-2012 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_VORBIS_COMMENTS_H -#define MPD_VORBIS_COMMENTS_H - -#include "check.h" - -#include <stdbool.h> - -struct replay_gain_info; -struct tag_handler; - -bool -vorbis_comments_to_replay_gain(struct replay_gain_info *rgi, char **comments); - -void -vorbis_comments_scan(char **comments, - const struct tag_handler *handler, void *handler_ctx); - -struct tag * -vorbis_comments_to_tag(char **comments); - -#endif diff --git a/src/decoder/vorbis_decoder_plugin.c b/src/decoder/vorbis_decoder_plugin.c deleted file mode 100644 index 15cdc0ca..00000000 --- a/src/decoder/vorbis_decoder_plugin.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "vorbis_comments.h" -#include "_ogg_common.h" -#include "audio_check.h" -#include "uri.h" -#include "tag_handler.h" - -#ifndef HAVE_TREMOR -#define OV_EXCLUDE_STATIC_CALLBACKS -#include <vorbis/vorbisfile.h> -#else -#include <tremor/ivorbisfile.h> -/* Macros to make Tremor's API look like libogg. Tremor always - returns host-byte-order 16-bit signed data, and uses integer - milliseconds where libogg uses double seconds. -*/ -#define ov_read(VF, BUFFER, LENGTH, BIGENDIANP, WORD, SGNED, BITSTREAM) \ - ov_read(VF, BUFFER, LENGTH, BITSTREAM) -#define ov_time_total(VF, I) ((double)ov_time_total(VF, I)/1000) -#define ov_time_tell(VF) ((double)ov_time_tell(VF)/1000) -#define ov_time_seek_page(VF, S) (ov_time_seek_page(VF, (S)*1000)) -#endif /* HAVE_TREMOR */ - -#include <glib.h> - -#include <assert.h> -#include <errno.h> -#include <unistd.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "vorbis" -#define OGG_CHUNK_SIZE 4096 - -#if G_BYTE_ORDER == G_BIG_ENDIAN -#define OGG_DECODE_USE_BIGENDIAN 1 -#else -#define OGG_DECODE_USE_BIGENDIAN 0 -#endif - -struct vorbis_input_stream { - struct decoder *decoder; - - struct input_stream *input_stream; - bool seekable; -}; - -static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data) -{ - struct vorbis_input_stream *vis = data; - size_t ret; - - ret = decoder_read(vis->decoder, vis->input_stream, ptr, size * nmemb); - - errno = 0; - - return ret / size; -} - -static int ogg_seek_cb(void *data, ogg_int64_t offset, int whence) -{ - struct vorbis_input_stream *vis = data; - - return vis->seekable && - (!vis->decoder || decoder_get_command(vis->decoder) != DECODE_COMMAND_STOP) && - input_stream_lock_seek(vis->input_stream, offset, whence, NULL) - ? 0 : -1; -} - -/* TODO: check Ogg libraries API and see if we can just not have this func */ -static int ogg_close_cb(G_GNUC_UNUSED void *data) -{ - return 0; -} - -static long ogg_tell_cb(void *data) -{ - const struct vorbis_input_stream *vis = data; - - return (long)vis->input_stream->offset; -} - -static const ov_callbacks vorbis_is_callbacks = { - .read_func = ogg_read_cb, - .seek_func = ogg_seek_cb, - .close_func = ogg_close_cb, - .tell_func = ogg_tell_cb, -}; - -static const char * -vorbis_strerror(int code) -{ - switch (code) { - case OV_EREAD: - return "read error"; - - case OV_ENOTVORBIS: - return "not vorbis stream"; - - case OV_EVERSION: - return "vorbis version mismatch"; - - case OV_EBADHEADER: - return "invalid vorbis header"; - - case OV_EFAULT: - return "internal logic error"; - - default: - return "unknown error"; - } -} - -static bool -vorbis_is_open(struct vorbis_input_stream *vis, OggVorbis_File *vf, - struct decoder *decoder, struct input_stream *input_stream) -{ - vis->decoder = decoder; - vis->input_stream = input_stream; - vis->seekable = input_stream->seekable && - (input_stream->uri == NULL || - !uri_has_scheme(input_stream->uri)); - - int ret = ov_open_callbacks(vis, vf, NULL, 0, vorbis_is_callbacks); - if (ret < 0) { - if (decoder == NULL || - decoder_get_command(decoder) == DECODE_COMMAND_NONE) - g_warning("Failed to open Ogg Vorbis stream: %s", - vorbis_strerror(ret)); - return false; - } - - return true; -} - -static void -vorbis_send_comments(struct decoder *decoder, struct input_stream *is, - char **comments) -{ - struct tag *tag; - - tag = vorbis_comments_to_tag(comments); - if (!tag) - return; - - decoder_tag(decoder, is, tag); - tag_free(tag); -} - -/* public */ -static void -vorbis_stream_decode(struct decoder *decoder, - struct input_stream *input_stream) -{ - GError *error = NULL; - OggVorbis_File vf; - struct vorbis_input_stream vis; - struct audio_format audio_format; - float total_time; - int current_section; - int prev_section = -1; - long ret; - char chunk[OGG_CHUNK_SIZE]; - long bitRate = 0; - long test; - const vorbis_info *vi; - enum decoder_command cmd = DECODE_COMMAND_NONE; - - if (ogg_stream_type_detect(input_stream) != VORBIS) - return; - - /* rewind the stream, because ogg_stream_type_detect() has - moved it */ - input_stream_lock_seek(input_stream, 0, SEEK_SET, NULL); - - if (!vorbis_is_open(&vis, &vf, decoder, input_stream)) - return; - - vi = ov_info(&vf, -1); - if (vi == NULL) { - g_warning("ov_info() has failed"); - return; - } - - if (!audio_format_init_checked(&audio_format, vi->rate, - SAMPLE_FORMAT_S16, - vi->channels, &error)) { - g_warning("%s", error->message); - g_error_free(error); - return; - } - - total_time = ov_time_total(&vf, -1); - if (total_time < 0) - total_time = 0; - - decoder_initialized(decoder, &audio_format, vis.seekable, total_time); - - do { - if (cmd == DECODE_COMMAND_SEEK) { - double seek_where = decoder_seek_where(decoder); - if (0 == ov_time_seek_page(&vf, seek_where)) { - decoder_command_finished(decoder); - } else - decoder_seek_error(decoder); - } - - ret = ov_read(&vf, chunk, sizeof(chunk), - OGG_DECODE_USE_BIGENDIAN, 2, 1, ¤t_section); - if (ret == OV_HOLE) /* bad packet */ - ret = 0; - else if (ret <= 0) - /* break on EOF or other error */ - break; - - if (current_section != prev_section) { - char **comments; - - vi = ov_info(&vf, -1); - if (vi == NULL) { - g_warning("ov_info() has failed"); - break; - } - - if (vi->rate != (long)audio_format.sample_rate || - vi->channels != (int)audio_format.channels) { - /* we don't support audio format - change yet */ - g_warning("audio format change, stopping here"); - break; - } - - comments = ov_comment(&vf, -1)->user_comments; - vorbis_send_comments(decoder, input_stream, comments); - - struct replay_gain_info rgi; - if (vorbis_comments_to_replay_gain(&rgi, comments)) - decoder_replay_gain(decoder, &rgi); - - prev_section = current_section; - } - - if ((test = ov_bitrate_instant(&vf)) > 0) - bitRate = test / 1000; - - cmd = decoder_data(decoder, input_stream, - chunk, ret, - bitRate); - } while (cmd != DECODE_COMMAND_STOP); - - ov_clear(&vf); -} - -static bool -vorbis_scan_stream(struct input_stream *is, - const struct tag_handler *handler, void *handler_ctx) -{ - struct vorbis_input_stream vis; - OggVorbis_File vf; - - if (!vorbis_is_open(&vis, &vf, NULL, is)) - return false; - - tag_handler_invoke_duration(handler, handler_ctx, - (int)(ov_time_total(&vf, -1) + 0.5)); - - vorbis_comments_scan(ov_comment(&vf, -1)->user_comments, - handler, handler_ctx); - - ov_clear(&vf); - return true; -} - -static const char *const vorbis_suffixes[] = { - "ogg", "oga", NULL -}; - -static const char *const vorbis_mime_types[] = { - "application/ogg", - "application/x-ogg", - "audio/ogg", - "audio/vorbis", - "audio/vorbis+ogg", - "audio/x-ogg", - "audio/x-vorbis", - "audio/x-vorbis+ogg", - NULL -}; - -const struct decoder_plugin vorbis_decoder_plugin = { - .name = "vorbis", - .stream_decode = vorbis_stream_decode, - .scan_stream = vorbis_scan_stream, - .suffixes = vorbis_suffixes, - .mime_types = vorbis_mime_types -}; diff --git a/src/decoder/wavpack_decoder_plugin.c b/src/decoder/wavpack_decoder_plugin.c deleted file mode 100644 index ae85b0e2..00000000 --- a/src/decoder/wavpack_decoder_plugin.c +++ /dev/null @@ -1,600 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "decoder_api.h" -#include "audio_check.h" -#include "path.h" -#include "utils.h" -#include "tag_table.h" -#include "tag_handler.h" - -#include <wavpack/wavpack.h> -#include <glib.h> - -#include <assert.h> -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "wavpack" - -#define ERRORLEN 80 - -static const struct tag_table wavpack_tags[] = { - { "artist", TAG_ARTIST }, - { "album", TAG_ALBUM }, - { "title", TAG_TITLE }, - { "track", TAG_TRACK }, - { "name", TAG_NAME }, - { "genre", TAG_GENRE }, - { "date", TAG_DATE }, - { "composer", TAG_COMPOSER }, - { "performer", TAG_PERFORMER }, - { "comment", TAG_COMMENT }, - { "disc", TAG_DISC }, - { NULL, TAG_NUM_OF_ITEM_TYPES } -}; - -/** A pointer type for format converter function. */ -typedef void (*format_samples_t)( - int bytes_per_sample, - void *buffer, uint32_t count -); - -/* - * This function has been borrowed from the tiny player found on - * wavpack.com. Modifications were required because mpd only handles - * max 24-bit samples. - */ -static void -format_samples_int(int bytes_per_sample, void *buffer, uint32_t count) -{ - int32_t *src = buffer; - - switch (bytes_per_sample) { - case 1: { - int8_t *dst = buffer; - /* - * The asserts like the following one are because we do the - * formatting of samples within a single buffer. The size - * of the output samples never can be greater than the size - * of the input ones. Otherwise we would have an overflow. - */ - assert_static(sizeof(*dst) <= sizeof(*src)); - - /* pass through and align 8-bit samples */ - while (count--) { - *dst++ = *src++; - } - break; - } - case 2: { - uint16_t *dst = buffer; - assert_static(sizeof(*dst) <= sizeof(*src)); - - /* pass through and align 16-bit samples */ - while (count--) { - *dst++ = *src++; - } - break; - } - - case 3: - case 4: - /* do nothing */ - break; - } -} - -/* - * This function converts floating point sample data to 24-bit integer. - */ -static void -format_samples_float(G_GNUC_UNUSED int bytes_per_sample, void *buffer, - uint32_t count) -{ - float *p = buffer; - - while (count--) { - *p /= (1 << 23); - ++p; - } -} - -/** - * Choose a MPD sample format from libwavpacks' number of bits. - */ -static enum sample_format -wavpack_bits_to_sample_format(bool is_float, int bytes_per_sample) -{ - if (is_float) - return SAMPLE_FORMAT_FLOAT; - - switch (bytes_per_sample) { - case 1: - return SAMPLE_FORMAT_S8; - - case 2: - return SAMPLE_FORMAT_S16; - - case 3: - return SAMPLE_FORMAT_S24_P32; - - case 4: - return SAMPLE_FORMAT_S32; - - default: - return SAMPLE_FORMAT_UNDEFINED; - } -} - -/* - * This does the main decoding thing. - * Requires an already opened WavpackContext. - */ -static void -wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek) -{ - GError *error = NULL; - bool is_float; - enum sample_format sample_format; - struct audio_format audio_format; - format_samples_t format_samples; - float total_time; - int bytes_per_sample, output_sample_size; - - is_float = (WavpackGetMode(wpc) & MODE_FLOAT) != 0; - sample_format = - wavpack_bits_to_sample_format(is_float, - WavpackGetBytesPerSample(wpc)); - - if (!audio_format_init_checked(&audio_format, - WavpackGetSampleRate(wpc), - sample_format, - WavpackGetNumChannels(wpc), &error)) { - g_warning("%s", error->message); - g_error_free(error); - return; - } - - if (is_float) { - format_samples = format_samples_float; - } else { - format_samples = format_samples_int; - } - - total_time = WavpackGetNumSamples(wpc); - total_time /= audio_format.sample_rate; - bytes_per_sample = WavpackGetBytesPerSample(wpc); - output_sample_size = audio_format_frame_size(&audio_format); - - /* wavpack gives us all kind of samples in a 32-bit space */ - int32_t chunk[1024]; - const uint32_t samples_requested = G_N_ELEMENTS(chunk) / - audio_format.channels; - - decoder_initialized(decoder, &audio_format, can_seek, total_time); - - enum decoder_command cmd = decoder_get_command(decoder); - while (cmd != DECODE_COMMAND_STOP) { - if (cmd == DECODE_COMMAND_SEEK) { - if (can_seek) { - unsigned where = decoder_seek_where(decoder) * - audio_format.sample_rate; - - if (WavpackSeekSample(wpc, where)) { - decoder_command_finished(decoder); - } else { - decoder_seek_error(decoder); - } - } else { - decoder_seek_error(decoder); - } - } - - uint32_t samples_got = WavpackUnpackSamples(wpc, chunk, - samples_requested); - if (samples_got == 0) - break; - - int bitrate = (int)(WavpackGetInstantBitrate(wpc) / 1000 + - 0.5); - format_samples(bytes_per_sample, chunk, - samples_got * audio_format.channels); - - cmd = decoder_data(decoder, NULL, chunk, - samples_got * output_sample_size, - bitrate); - } -} - -/** - * Locate and parse a floating point tag. Returns true if it was - * found. - */ -static bool -wavpack_tag_float(WavpackContext *wpc, const char *key, float *value_r) -{ - char buffer[64]; - int ret; - - ret = WavpackGetTagItem(wpc, key, buffer, sizeof(buffer)); - if (ret <= 0) - return false; - - *value_r = atof(buffer); - return true; -} - -static bool -wavpack_replaygain(struct replay_gain_info *replay_gain_info, - WavpackContext *wpc) -{ - bool found = false; - - replay_gain_info_init(replay_gain_info); - - found |= wavpack_tag_float( - wpc, "replaygain_track_gain", - &replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain - ); - found |= wavpack_tag_float( - wpc, "replaygain_track_peak", - &replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak - ); - found |= wavpack_tag_float( - wpc, "replaygain_album_gain", - &replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain - ); - found |= wavpack_tag_float( - wpc, "replaygain_album_peak", - &replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak - ); - - return found; -} - -static void -wavpack_scan_tag_item(WavpackContext *wpc, const char *name, - enum tag_type type, - const struct tag_handler *handler, void *handler_ctx) -{ - char buffer[1024]; - int len = WavpackGetTagItem(wpc, name, buffer, sizeof(buffer)); - if (len <= 0 || (unsigned)len >= sizeof(buffer)) - return; - - tag_handler_invoke_tag(handler, handler_ctx, type, buffer); - -} - -static void -wavpack_scan_pair(WavpackContext *wpc, const char *name, - const struct tag_handler *handler, void *handler_ctx) -{ - char buffer[8192]; - int len = WavpackGetTagItem(wpc, name, buffer, sizeof(buffer)); - if (len <= 0 || (unsigned)len >= sizeof(buffer)) - return; - - tag_handler_invoke_pair(handler, handler_ctx, name, buffer); -} - -/* - * Reads metainfo from the specified file. - */ -static bool -wavpack_scan_file(const char *fname, - const struct tag_handler *handler, void *handler_ctx) -{ - WavpackContext *wpc; - char error[ERRORLEN]; - - wpc = WavpackOpenFileInput(fname, error, OPEN_TAGS, 0); - if (wpc == NULL) { - g_warning( - "failed to open WavPack file \"%s\": %s\n", - fname, error - ); - return false; - } - - tag_handler_invoke_duration(handler, handler_ctx, - WavpackGetNumSamples(wpc) / - WavpackGetSampleRate(wpc)); - - for (const struct tag_table *i = wavpack_tags; i->name != NULL; ++i) - wavpack_scan_tag_item(wpc, i->name, i->type, - handler, handler_ctx); - - if (handler->pair != NULL) { - char name[64]; - - for (int i = 0, n = WavpackGetNumTagItems(wpc); - i < n; ++i) { - int len = WavpackGetTagItemIndexed(wpc, i, name, - sizeof(name)); - if (len <= 0 || (unsigned)len >= sizeof(name)) - continue; - - wavpack_scan_pair(wpc, name, handler, handler_ctx); - } - } - - WavpackCloseFile(wpc); - - return true; -} - -/* - * mpd input_stream <=> WavpackStreamReader wrapper callbacks - */ - -/* This struct is needed for per-stream last_byte storage. */ -struct wavpack_input { - struct decoder *decoder; - struct input_stream *is; - /* Needed for push_back_byte() */ - int last_byte; -}; - -/** - * Little wrapper for struct wavpack_input to cast from void *. - */ -static struct wavpack_input * -wpin(void *id) -{ - assert(id); - return id; -} - -static int32_t -wavpack_input_read_bytes(void *id, void *data, int32_t bcount) -{ - uint8_t *buf = (uint8_t *)data; - int32_t i = 0; - - if (wpin(id)->last_byte != EOF) { - *buf++ = wpin(id)->last_byte; - wpin(id)->last_byte = EOF; - --bcount; - ++i; - } - - /* wavpack fails if we return a partial read, so we just wait - until the buffer is full */ - while (bcount > 0) { - size_t nbytes = decoder_read( - wpin(id)->decoder, wpin(id)->is, buf, bcount - ); - if (nbytes == 0) { - /* EOF, error or a decoder command */ - break; - } - - i += nbytes; - bcount -= nbytes; - buf += nbytes; - } - - return i; -} - -static uint32_t -wavpack_input_get_pos(void *id) -{ - return wpin(id)->is->offset; -} - -static int -wavpack_input_set_pos_abs(void *id, uint32_t pos) -{ - return input_stream_lock_seek(wpin(id)->is, pos, SEEK_SET, NULL) - ? 0 : -1; -} - -static int -wavpack_input_set_pos_rel(void *id, int32_t delta, int mode) -{ - return input_stream_lock_seek(wpin(id)->is, delta, mode, NULL) - ? 0 : -1; -} - -static int -wavpack_input_push_back_byte(void *id, int c) -{ - if (wpin(id)->last_byte == EOF) { - wpin(id)->last_byte = c; - return c; - } else { - return EOF; - } -} - -static uint32_t -wavpack_input_get_length(void *id) -{ - if (wpin(id)->is->size < 0) - return 0; - - return wpin(id)->is->size; -} - -static int -wavpack_input_can_seek(void *id) -{ - return wpin(id)->is->seekable; -} - -static WavpackStreamReader mpd_is_reader = { - .read_bytes = wavpack_input_read_bytes, - .get_pos = wavpack_input_get_pos, - .set_pos_abs = wavpack_input_set_pos_abs, - .set_pos_rel = wavpack_input_set_pos_rel, - .push_back_byte = wavpack_input_push_back_byte, - .get_length = wavpack_input_get_length, - .can_seek = wavpack_input_can_seek, - .write_bytes = NULL /* no need to write edited tags */ -}; - -static void -wavpack_input_init(struct wavpack_input *isp, struct decoder *decoder, - struct input_stream *is) -{ - isp->decoder = decoder; - isp->is = is; - isp->last_byte = EOF; -} - -static struct input_stream * -wavpack_open_wvc(struct decoder *decoder, const char *uri, - GMutex *mutex, GCond *cond, - struct wavpack_input *wpi) -{ - struct input_stream *is_wvc; - char *wvc_url = NULL; - char first_byte; - size_t nbytes; - - /* - * As we use dc->utf8url, this function will be bad for - * single files. utf8url is not absolute file path :/ - */ - if (uri == NULL) - return false; - - wvc_url = g_strconcat(uri, "c", NULL); - is_wvc = input_stream_open(wvc_url, mutex, cond, NULL); - g_free(wvc_url); - - if (is_wvc == NULL) - return NULL; - - /* - * And we try to buffer in order to get know - * about a possible 404 error. - */ - nbytes = decoder_read( - decoder, is_wvc, &first_byte, sizeof(first_byte) - ); - if (nbytes == 0) { - input_stream_close(is_wvc); - return NULL; - } - - /* push it back */ - wavpack_input_init(wpi, decoder, is_wvc); - wpi->last_byte = first_byte; - return is_wvc; -} - -/* - * Decodes a stream. - */ -static void -wavpack_streamdecode(struct decoder * decoder, struct input_stream *is) -{ - char error[ERRORLEN]; - WavpackContext *wpc; - struct input_stream *is_wvc; - int open_flags = OPEN_NORMALIZE; - struct wavpack_input isp, isp_wvc; - bool can_seek = is->seekable; - - is_wvc = wavpack_open_wvc(decoder, is->uri, is->mutex, is->cond, - &isp_wvc); - if (is_wvc != NULL) { - open_flags |= OPEN_WVC; - can_seek &= is_wvc->seekable; - } - - if (!can_seek) { - open_flags |= OPEN_STREAMING; - } - - wavpack_input_init(&isp, decoder, is); - wpc = WavpackOpenFileInputEx( - &mpd_is_reader, &isp, - open_flags & OPEN_WVC ? &isp_wvc : NULL, - error, open_flags, 23 - ); - - if (wpc == NULL) { - g_warning("failed to open WavPack stream: %s\n", error); - return; - } - - wavpack_decode(decoder, wpc, can_seek); - - WavpackCloseFile(wpc); - if (open_flags & OPEN_WVC) { - input_stream_close(is_wvc); - } -} - -/* - * Decodes a file. - */ -static void -wavpack_filedecode(struct decoder *decoder, const char *fname) -{ - char error[ERRORLEN]; - WavpackContext *wpc; - - wpc = WavpackOpenFileInput( - fname, error, - OPEN_TAGS | OPEN_WVC | OPEN_NORMALIZE, 23 - ); - if (wpc == NULL) { - g_warning( - "failed to open WavPack file \"%s\": %s\n", - fname, error - ); - return; - } - - struct replay_gain_info replay_gain_info; - if (wavpack_replaygain(&replay_gain_info, wpc)) - decoder_replay_gain(decoder, &replay_gain_info); - - wavpack_decode(decoder, wpc, true); - - WavpackCloseFile(wpc); -} - -static char const *const wavpack_suffixes[] = { - "wv", - NULL -}; - -static char const *const wavpack_mime_types[] = { - "audio/x-wavpack", - NULL -}; - -const struct decoder_plugin wavpack_decoder_plugin = { - .name = "wavpack", - .stream_decode = wavpack_streamdecode, - .file_decode = wavpack_filedecode, - .scan_file = wavpack_scan_file, - .suffixes = wavpack_suffixes, - .mime_types = wavpack_mime_types -}; diff --git a/src/decoder/wildmidi_decoder_plugin.c b/src/decoder/wildmidi_decoder_plugin.c deleted file mode 100644 index 2cdb30a9..00000000 --- a/src/decoder/wildmidi_decoder_plugin.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "decoder_api.h" -#include "tag_handler.h" -#include "glib_compat.h" - -#include <glib.h> - -#include <wildmidi_lib.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "wildmidi" - -enum { - WILDMIDI_SAMPLE_RATE = 48000, -}; - -static bool -wildmidi_init(const struct config_param *param) -{ - const char *config_file; - int ret; - - config_file = config_get_block_string(param, "config_file", - "/etc/timidity/timidity.cfg"); - if (!g_file_test(config_file, G_FILE_TEST_IS_REGULAR)) { - g_debug("configuration file does not exist: %s", config_file); - return false; - } - - ret = WildMidi_Init(config_file, WILDMIDI_SAMPLE_RATE, 0); - return ret == 0; -} - -static void -wildmidi_finish(void) -{ - WildMidi_Shutdown(); -} - -static void -wildmidi_file_decode(struct decoder *decoder, const char *path_fs) -{ - static const struct audio_format audio_format = { - .sample_rate = WILDMIDI_SAMPLE_RATE, - .format = SAMPLE_FORMAT_S16, - .channels = 2, - }; - midi *wm; - const struct _WM_Info *info; - enum decoder_command cmd; - - wm = WildMidi_Open(path_fs); - if (wm == NULL) - return; - - info = WildMidi_GetInfo(wm); - if (info == NULL) { - WildMidi_Close(wm); - return; - } - - decoder_initialized(decoder, &audio_format, true, - info->approx_total_samples / WILDMIDI_SAMPLE_RATE); - - do { - char buffer[4096]; - int len; - - info = WildMidi_GetInfo(wm); - if (info == NULL) - break; - - len = WildMidi_GetOutput(wm, buffer, sizeof(buffer)); - if (len <= 0) - break; - - cmd = decoder_data(decoder, NULL, buffer, len, 0); - - if (cmd == DECODE_COMMAND_SEEK) { - unsigned long seek_where = WILDMIDI_SAMPLE_RATE * - decoder_seek_where(decoder); - -#ifdef HAVE_WILDMIDI_SAMPLED_SEEK - WildMidi_SampledSeek(wm, &seek_where); -#else - WildMidi_FastSeek(wm, &seek_where); -#endif - decoder_command_finished(decoder); - cmd = DECODE_COMMAND_NONE; - } - - } while (cmd == DECODE_COMMAND_NONE); - - WildMidi_Close(wm); -} - -static bool -wildmidi_scan_file(const char *path_fs, - const struct tag_handler *handler, void *handler_ctx) -{ - midi *wm = WildMidi_Open(path_fs); - if (wm == NULL) - return false; - - const struct _WM_Info *info = WildMidi_GetInfo(wm); - if (info == NULL) { - WildMidi_Close(wm); - return false; - } - - int duration = info->approx_total_samples / WILDMIDI_SAMPLE_RATE; - tag_handler_invoke_duration(handler, handler_ctx, duration); - - WildMidi_Close(wm); - - return true; -} - -static const char *const wildmidi_suffixes[] = { - "mid", - NULL -}; - -const struct decoder_plugin wildmidi_decoder_plugin = { - .name = "wildmidi", - .init = wildmidi_init, - .finish = wildmidi_finish, - .file_decode = wildmidi_file_decode, - .scan_file = wildmidi_scan_file, - .suffixes = wildmidi_suffixes, -}; diff --git a/src/decoder_list.c b/src/decoder_list.c index 177b632a..7dc4c3be 100644 --- a/src/decoder_list.c +++ b/src/decoder_list.c @@ -23,91 +23,15 @@ #include "utils.h" #include "conf.h" #include "mpd_error.h" -#include "decoder/pcm_decoder_plugin.h" -#include "decoder/dsdiff_decoder_plugin.h" -#include "decoder/dsf_decoder_plugin.h" #include <glib.h> #include <string.h> -extern const struct decoder_plugin mad_decoder_plugin; -extern const struct decoder_plugin mpg123_decoder_plugin; -extern const struct decoder_plugin vorbis_decoder_plugin; -extern const struct decoder_plugin flac_decoder_plugin; -extern const struct decoder_plugin oggflac_decoder_plugin; -extern const struct decoder_plugin sndfile_decoder_plugin; -extern const struct decoder_plugin audiofile_decoder_plugin; -extern const struct decoder_plugin mp4ff_decoder_plugin; -extern const struct decoder_plugin faad_decoder_plugin; -extern const struct decoder_plugin mpcdec_decoder_plugin; -extern const struct decoder_plugin wavpack_decoder_plugin; -extern const struct decoder_plugin modplug_decoder_plugin; -extern const struct decoder_plugin mikmod_decoder_plugin; -extern const struct decoder_plugin sidplay_decoder_plugin; -extern const struct decoder_plugin wildmidi_decoder_plugin; -extern const struct decoder_plugin fluidsynth_decoder_plugin; extern const struct decoder_plugin ffmpeg_decoder_plugin; -extern const struct decoder_plugin gme_decoder_plugin; const struct decoder_plugin *const decoder_plugins[] = { -#ifdef HAVE_MAD - &mad_decoder_plugin, -#endif -#ifdef HAVE_MPG123 - &mpg123_decoder_plugin, -#endif -#ifdef ENABLE_VORBIS_DECODER - &vorbis_decoder_plugin, -#endif -#if defined(HAVE_FLAC) - &oggflac_decoder_plugin, -#endif -#ifdef HAVE_FLAC - &flac_decoder_plugin, -#endif -#ifdef ENABLE_SNDFILE - &sndfile_decoder_plugin, -#endif -#ifdef HAVE_AUDIOFILE - &audiofile_decoder_plugin, -#endif - &dsdiff_decoder_plugin, - &dsf_decoder_plugin, -#ifdef HAVE_FAAD - &faad_decoder_plugin, -#endif -#ifdef HAVE_MP4 - &mp4ff_decoder_plugin, -#endif -#ifdef HAVE_MPCDEC - &mpcdec_decoder_plugin, -#endif -#ifdef HAVE_WAVPACK - &wavpack_decoder_plugin, -#endif -#ifdef HAVE_MODPLUG - &modplug_decoder_plugin, -#endif -#ifdef ENABLE_MIKMOD_DECODER - &mikmod_decoder_plugin, -#endif -#ifdef ENABLE_SIDPLAY - &sidplay_decoder_plugin, -#endif -#ifdef ENABLE_WILDMIDI - &wildmidi_decoder_plugin, -#endif -#ifdef ENABLE_FLUIDSYNTH - &fluidsynth_decoder_plugin, -#endif -#ifdef HAVE_FFMPEG &ffmpeg_decoder_plugin, -#endif -#ifdef HAVE_GME - &gme_decoder_plugin, -#endif - &pcm_decoder_plugin, NULL }; |