Compare commits

...

80 Commits

Author SHA1 Message Date
Max Kellermann
d4db873716 release v0.19.19 2016-08-23 10:19:10 +02:00
Thomas Klausner
de0752fd56 system/ByteOrder: gssupport non-x86 NetBSD 2016-08-23 10:15:54 +02:00
Max Kellermann
4204d4928b decoder/ffmpeg: no avcodec_parameters_to_context() with FFmpeg 3.0
This function exists since FFmpeg 3.1.  Fix a build failure with
FFmpeg 3.0.
2016-08-23 10:15:54 +02:00
Max Kellermann
05de0ecec3 decoder/ffmpeg: call avcodec_parameters_to_context()
These bug reports describe problems with some FFmpeg codecs:

 https://bugs.musicpd.org/view.php?id=4564
 https://bugs.musicpd.org/view.php?id=4568
 https://bugs.musicpd.org/view.php?id=4572

According to the FFmpeg bug tracker, a call to
avcodec_parameters_to_context() is required after
avcodec_alloc_context3():

 https://trac.ffmpeg.org/ticket/5781

This requirement was previously undocumented.
2016-08-23 09:59:25 +02:00
Max Kellermann
b05beb000f Compiler.h: work around clang 3.9 warning -Wexpansion-to-defined
Check {GCC,CLANG}_VERSION==0 or >0 instead of using defined(), which
may render undefined behavior.
2016-08-23 09:59:25 +02:00
Max Kellermann
093abaad29 Compiler.h: always define CLANG_VERSION 2016-08-23 09:54:09 +02:00
Max Kellermann
e84e4169f9 Compiler.h: remove redundant __GNUC__ check
GCC_VERSION>0 implies defined(__GNUC__).
2016-08-23 09:53:17 +02:00
Max Kellermann
cd6c5cfd4c Compiler.h: exclude clang from GCC_CHECK_VERSION() 2016-08-23 09:52:14 +02:00
Max Kellermann
b855f2fcc2 Chrono: use macro GCC_OLDER_THAN() 2016-08-23 09:51:41 +02:00
Max Kellermann
ba69ade024 Compiler.h: add macro CLANG_OR_GCC_VERSION() 2016-08-23 09:48:58 +02:00
Max Kellermann
a546bfe7d9 decoder/wildmidi: support libWildMidi 0.4 2016-08-15 10:08:35 +02:00
Max Kellermann
25deae6cc7 decoder/wildmidi: move code to wildmidi_output() 2016-08-15 10:07:08 +02:00
Nils Schneider
62000670e3 Support S24_P32/S32/FLOAT sample formats on Pulse
This is based on a patch from Ian Scott in 2014. It was never committed,
so I figured I'd fix the outstanding issue and resubmit it.

https://www.mail-archive.com/mpd-devel%40musicpd.org/msg00139.html
2016-08-15 10:02:29 +02:00
Max Kellermann
ac49043fbb output/pulse: move variable declaration down 2016-08-15 10:02:22 +02:00
Max Kellermann
37a7ca7f14 configure.ac: prepare for 0.19.19 2016-08-15 10:00:03 +02:00
Max Kellermann
2b97b124bd release v0.19.18 2016-08-05 18:45:23 +02:00
Max Kellermann
d042ab87da decoder/Thread: delete the InputStream on error
Fixes memory leak after stream failure.  See
https://bugs.musicpd.org/view.php?id=4562
2016-08-05 18:15:30 +02:00
Max Kellermann
588303b78d lib/nfs/Manager: add Compare(ManagedConnection, ManagedConnection)
Required for Boost 1.61, which uses that overload in a BOOST_ASSERT().
2016-08-05 18:06:07 +02:00
Max Kellermann
36704c5e18 Makefile.am: fix "undefined reference" linker error 2016-08-05 17:48:53 +02:00
Max Kellermann
5834843b8a decoder/ffmpeg: fix the AVCodecParameters API check
Turns out the libavcodec version numbers are not linear;  the feature
was added in FFmpeg 3.1 commit 998e1b8, libavcodec 57.14.0; but FFmpeg
3.0 has version 57.48.101.  Ouch!
2016-08-02 19:15:23 +02:00
Max Kellermann
762f3afb9d decoder/sidplay: allow building with libsidplayfp instead of libsidplay2
https://bugs.musicpd.org/view.php?id=4558
2016-07-29 19:32:21 +02:00
Max Kellermann
7fb2f15a1a decoder/ffmpeg: check avformat_open_input() return value 2016-07-29 19:32:21 +02:00
Max Kellermann
7456dccd3a decoder/ffmpeg: FfmpegOpenInput() returns Error 2016-07-29 19:32:21 +02:00
Max Kellermann
245f41bb7e decoder/ffmpeg: fix endless recursion in FfmpegScanStream()
Was accidently added by commit cafc266e0
2016-07-29 19:32:21 +02:00
Max Kellermann
9bfb844cfa decoder/sidplay: read the "date" tag 2016-07-29 17:47:08 +02:00
Max Kellermann
d790d3ba3c decoder/sidplay: add GetInfoString() 2016-07-29 17:38:04 +02:00
Max Kellermann
c3dbc92766 decoder/sidplay: use SidTune::getStatus() 2016-07-29 17:31:34 +02:00
Max Kellermann
0bd25f1e17 decoder/sidplay: log detailed error message 2016-07-29 17:04:38 +02:00
Max Kellermann
a4cd7411e8 decoder/sidplay: remove unnecessary error check
The ReSIDBuilder constructor cannot fail.
2016-07-29 17:03:26 +02:00
Max Kellermann
bf276f6235 decoder/sidplay: use SidTune::getStatus() 2016-07-29 16:58:58 +02:00
Max Kellermann
d916890a8f configure.ac: detect libsidplay2 and related with pkg-config
The comment about requiring libtool from 7 years ago is obsolete these
days.
2016-07-29 15:21:38 +02:00
Max Kellermann
071cacc9a4 decoder/sidplay: pass SidTuneMod to get_song_length()
Eliminate duplicate SidTune construction.
2016-07-29 14:56:05 +02:00
Max Kellermann
33f33323af decoder/sidplay: simplify the SidDatabase::length() call 2016-07-29 14:55:58 +02:00
Max Kellermann
388fae2c47 decoder/sidplay: include cleanup 2016-07-29 14:55:28 +02:00
Max Kellermann
9f878b77e9 decoder/sidplay: use class SidDatabase
Remove our own songlength database parser.
2016-07-29 14:55:28 +02:00
Max Kellermann
a547d2aaba decoder/sidplay: use config_param::GetBlockPath() 2016-07-29 14:55:28 +02:00
Max Kellermann
c013026821 decoder/sidplay: make "songlength_file" local 2016-07-29 14:55:28 +02:00
Max Kellermann
96b48a2404 decoder/sidplay: pass parsed path to get_song_length()
Eliminates duplicate ParseContainerPath() call.
2016-07-29 14:55:28 +02:00
Max Kellermann
9612975c2c decoder/sidplay: merge get_container_name() and get_song_num() 2016-07-29 14:55:28 +02:00
Max Kellermann
41bfd45a2e fs/Path: make IsAbsolute() const 2016-07-29 14:55:28 +02:00
Max Kellermann
bbdcbd1f08 fs/Path: add methods GetBase() and GetDirectoryName() 2016-07-29 14:55:28 +02:00
Max Kellermann
6b3c525a9d db/update/ExcludeList: declare exclude_list_domain only if HAVE_GLIB 2016-07-29 14:55:28 +02:00
Max Kellermann
83aed7051c output/shout: rename "encoding" to "encoder"
The user manual specifies "encoder", which is consistent with other
output plugins.  "encoding" should be deprecated.
2016-07-29 10:52:03 +02:00
Max Kellermann
77c6e45e65 Compiler.h: require gcc 4.7 or newer
The ScopeExit library uses C++11 initializers, which gcc 4.6 does not
support.  Let's kill support for this ancient incomplete C++11
compiler, nobody should be using it anymore.
2016-07-29 09:52:23 +02:00
Max Kellermann
8825393660 decoder/ffmpeg: use avcodec_alloc_context3()
This commit suppresses the remaining deprecation warnings with FFmpeg 3.1.
2016-07-29 09:20:36 +02:00
Max Kellermann
2b9246c6ad decoder/ffmpeg: use avcodec_send_packet() and avcodec_receive_frame() on FFmpeg 3.1 2016-07-29 09:20:05 +02:00
Max Kellermann
a9edb4de28 decoder/ffmpeg: use AtScopeExit() for safe cleanup 2016-07-29 09:08:14 +02:00
Max Kellermann
a076ddf38c util/ScopeExit: new utility library
Similar to boost::scope_exit, but fewer include dependencies.
2016-07-29 09:07:58 +02:00
Max Kellermann
cafc266e0b decoder/ffmpeg: merge avformat_close_input() calls 2016-07-28 20:38:07 +02:00
Max Kellermann
a3d020eff9 decoder/ffmpeg: use AVCodecParameters on FFmpeg 3.1
The AVCodecContext attribute is deprecated.
2016-07-28 19:50:25 +02:00
Max Kellermann
8412d94d05 decoder/ffmpeg: add GetCodecParameters()
Preparing for FFmpeg 3.1 support.
2016-07-28 19:49:47 +02:00
Max Kellermann
d1c5bb956a decoder/ffmpeg: move code to IsAudio() 2016-07-28 19:49:45 +02:00
Max Kellermann
70986bc120 decoder/ffmpeg: move code to FfmpegSendFrame() 2016-07-28 19:49:18 +02:00
Max Kellermann
f31fe8b865 decoder/ffmpeg: include cleanup 2016-07-28 19:49:17 +02:00
Max Kellermann
142a9fe530 decoder/ffmpeg: move code to pcm/Interleave.cxx 2016-07-28 19:49:13 +02:00
Max Kellermann
4dd2ad9b27 decoder/ffmpeg: check for commands earlier
Improve initial seek by not reading/decoding the first frame before
checking for the seek command.
2016-07-28 19:48:27 +02:00
Max Kellermann
62f7375804 decoder/ffmpeg: simplify mpd_ffmpeg_open_input() 2016-07-28 19:48:25 +02:00
Max Kellermann
543296b5ba decoder/ffmpeg: move code to lib/ffmpeg/Init.cxx 2016-07-28 19:48:22 +02:00
Max Kellermann
5fee130d00 decoder/ffmpeg: move code to lib/ffmpeg/LogCallback.cxx 2016-07-28 19:47:49 +02:00
Max Kellermann
073facea70 decoder/ffmpeg: remove obsolete comment 2016-07-28 19:47:47 +02:00
Max Kellermann
dbe3b6eee4 decoder/ffmpeg: convert enums to constexpr 2016-07-28 19:47:36 +02:00
Max Kellermann
df97049647 decoder/ffmpeg: move struct AvioStream to FfmpegIo.hxx 2016-07-28 19:47:31 +02:00
Max Kellermann
42c5f68362 decoder/ffmpeg: use AVStream::duration
Use the duration of the stream we're actually decoding - not the
"global" attribute AVFormatContext::duration which may differ.
2016-07-28 19:47:24 +02:00
Max Kellermann
cc19e760cf decoder/ffmpeg: use more references 2016-07-28 19:45:22 +02:00
Max Kellermann
0ff22a16fa decoder/ffmpeg: move code to lib/ffmpeg/Time.hxx 2016-07-28 19:45:11 +02:00
Max Kellermann
47360ec906 decoder/ffmpeg: use av_free() instead of av_freep() 2016-07-28 19:45:07 +02:00
Max Kellermann
087a9938d2 decoder/ffmpeg: add API documentation 2016-07-28 19:45:05 +02:00
Max Kellermann
26d8e41a6b decoder/ffmpeg: copy_interleave_frame() returns ConstBuffer 2016-07-28 19:45:01 +02:00
Max Kellermann
750ae1d3f3 decoder/ffmpeg: copy_interleave_frame() returns Error 2016-07-28 19:44:42 +02:00
Max Kellermann
f8a9a7a108 decoder/ffmpeg: simplify ffmpeg_send_packet() 2016-07-28 19:44:39 +02:00
Max Kellermann
eb192137d6 decoder/ffmpeg: copy the AVPacket in ffmpeg_send_packet()
Revert commit 70495aad by rewriting it.  Turns out, in old FFmpeg
versions, copying the AVPacket is necessary.
2016-07-28 19:42:25 +02:00
Max Kellermann
c25b464f37 decoder/ffmpeg: move code to class FfmpegBuffer 2016-07-27 17:31:02 +02:00
Max Kellermann
710b48d410 decoder/ffmpeg: log detailed error message 2016-07-27 17:28:12 +02:00
Max Kellermann
5e77a8199d decoder/ffmpeg: remove obsolete comment 2016-07-27 17:28:12 +02:00
Max Kellermann
6637db086b decoder/ffmpeg: add "pure" attributes 2016-07-27 17:28:12 +02:00
Max Kellermann
a271a55da7 decoder/ffpmeg: make variables more local 2016-07-27 17:28:12 +02:00
Max Kellermann
6eeec6cbfa decoder/ffpmeg: simplify ffmpeg_send_packet() 2016-07-27 17:22:13 +02:00
Max Kellermann
5e3f3b0400 decoder/ffpmeg: rename functions to CamelCase 2016-07-27 17:18:58 +02:00
Max Kellermann
923c402f69 decoder/ffmpeg: optimize ffmpeg_scan_dictionary()
Don't scan tag items if the handler doesn't implement the tag()
method.
2016-07-27 17:17:14 +02:00
Max Kellermann
4fed0b991c configure.ac: prepare for 0.19.18 2016-07-27 15:07:15 +02:00
47 changed files with 1664 additions and 677 deletions

View File

@@ -12,7 +12,7 @@ install MPD. If more information is desired, read the user manual:
Dependencies Dependencies
------------ ------------
gcc 4.6 or later - http://gcc.gnu.org/ gcc 4.7 or later - http://gcc.gnu.org/
clang 3.2 or later - http://clang.llvm.org/ clang 3.2 or later - http://clang.llvm.org/
Any other C++11 compliant compiler should also work. Any other C++11 compliant compiler should also work.

View File

@@ -359,6 +359,7 @@ libutil_a_SOURCES = \
src/util/Clamp.hxx \ src/util/Clamp.hxx \
src/util/Alloc.cxx src/util/Alloc.hxx \ src/util/Alloc.cxx src/util/Alloc.hxx \
src/util/VarSize.hxx \ src/util/VarSize.hxx \
src/util/ScopeExit.hxx \
src/util/Error.cxx src/util/Error.hxx \ src/util/Error.cxx src/util/Error.hxx \
src/util/Domain.hxx \ src/util/Domain.hxx \
src/util/ReusableArray.hxx \ src/util/ReusableArray.hxx \
@@ -462,6 +463,7 @@ ICU_LDADD = libicu.a $(ICU_LIBS)
libpcm_a_SOURCES = \ libpcm_a_SOURCES = \
src/pcm/Domain.cxx src/pcm/Domain.hxx \ src/pcm/Domain.cxx src/pcm/Domain.hxx \
src/pcm/Traits.hxx \ src/pcm/Traits.hxx \
src/pcm/Interleave.cxx src/pcm/Interleave.hxx \
src/pcm/PcmBuffer.cxx src/pcm/PcmBuffer.hxx \ src/pcm/PcmBuffer.cxx src/pcm/PcmBuffer.hxx \
src/pcm/PcmExport.cxx src/pcm/PcmExport.hxx \ src/pcm/PcmExport.cxx src/pcm/PcmExport.hxx \
src/pcm/PcmConvert.cxx src/pcm/PcmConvert.hxx \ src/pcm/PcmConvert.cxx src/pcm/PcmConvert.hxx \
@@ -528,7 +530,7 @@ libfs_a_SOURCES = \
src/fs/Traits.cxx src/fs/Traits.hxx \ src/fs/Traits.cxx src/fs/Traits.hxx \
src/fs/Config.cxx src/fs/Config.hxx \ src/fs/Config.cxx src/fs/Config.hxx \
src/fs/Charset.cxx src/fs/Charset.hxx \ src/fs/Charset.cxx src/fs/Charset.hxx \
src/fs/Path.cxx src/fs/Path.hxx \ src/fs/Path.cxx src/fs/Path2.cxx src/fs/Path.hxx \
src/fs/AllocatedPath.cxx src/fs/AllocatedPath.hxx \ src/fs/AllocatedPath.cxx src/fs/AllocatedPath.hxx \
src/fs/FileSystem.cxx src/fs/FileSystem.hxx \ src/fs/FileSystem.cxx src/fs/FileSystem.hxx \
src/fs/StandardDirectory.cxx src/fs/StandardDirectory.hxx \ src/fs/StandardDirectory.cxx src/fs/StandardDirectory.hxx \
@@ -799,6 +801,11 @@ endif
if HAVE_FFMPEG if HAVE_FFMPEG
noinst_LIBRARIES += libffmpeg.a noinst_LIBRARIES += libffmpeg.a
libffmpeg_a_SOURCES = \ libffmpeg_a_SOURCES = \
src/lib/ffmpeg/Init.cxx src/lib/ffmpeg/Init.hxx \
src/lib/ffmpeg/Time.hxx \
src/lib/ffmpeg/Buffer.hxx \
src/lib/ffmpeg/LogError.cxx src/lib/ffmpeg/LogError.hxx \
src/lib/ffmpeg/LogCallback.cxx src/lib/ffmpeg/LogCallback.hxx \
src/lib/ffmpeg/Error.cxx src/lib/ffmpeg/Error.hxx \ src/lib/ffmpeg/Error.cxx src/lib/ffmpeg/Error.hxx \
src/lib/ffmpeg/Domain.cxx src/lib/ffmpeg/Domain.hxx src/lib/ffmpeg/Domain.cxx src/lib/ffmpeg/Domain.hxx
libffmpeg_a_CPPFLAGS = $(AM_CPPFLAGS) \ libffmpeg_a_CPPFLAGS = $(AM_CPPFLAGS) \
@@ -980,6 +987,8 @@ endif
if HAVE_FFMPEG if HAVE_FFMPEG
libdecoder_a_SOURCES += \ libdecoder_a_SOURCES += \
src/decoder/plugins/FfmpegIo.cxx \
src/decoder/plugins/FfmpegIo.hxx \
src/decoder/plugins/FfmpegMetaData.cxx \ src/decoder/plugins/FfmpegMetaData.cxx \
src/decoder/plugins/FfmpegMetaData.hxx \ src/decoder/plugins/FfmpegMetaData.hxx \
src/decoder/plugins/FfmpegDecoderPlugin.cxx \ src/decoder/plugins/FfmpegDecoderPlugin.cxx \
@@ -1867,6 +1876,7 @@ test_run_convert_SOURCES = test/run_convert.cxx \
src/AudioParser.cxx src/AudioParser.cxx
test_run_convert_LDADD = \ test_run_convert_LDADD = \
$(PCM_LIBS) \ $(PCM_LIBS) \
libconf.a \
libutil.a \ libutil.a \
$(GLIB_LIBS) $(GLIB_LIBS)

24
NEWS
View File

@@ -1,3 +1,27 @@
ver 0.19.19 (2016/08/23)
* decoder
- ffmpeg: bug fix for FFmpeg 3.1 support
- wildmidi: support libWildMidi 0.4
* output
- pulse: support 32 bit, 24 bit and floating point playback
* support non-x86 NetBSD
* fix clang 3.9 warnings
ver 0.19.18 (2016/08/05)
* decoder
- ffmpeg: fix crash with older FFmpeg versions (< 3.0)
- ffmpeg: log detailed error message
- ffmpeg: support FFmpeg 3.1
- sidplay: detect libsidplay2 with pkg-config
- sidplay: log detailed error message
- sidplay: read the "date" tag
- sidplay: allow building with libsidplayfp instead of libsidplay2
* output
- shout: recognize setting "encoder" instead of "encoding"
* fix memory leak after stream failure
* fix build failure with Boost 1.61
* require gcc 4.7 or newer
ver 0.19.17 (2016/07/09) ver 0.19.17 (2016/07/09)
* decoder * decoder
- flac: fix assertion failure while seeking - flac: fix assertion failure while seeking

View File

@@ -1,10 +1,10 @@
AC_PREREQ(2.60) AC_PREREQ(2.60)
AC_INIT(mpd, 0.19.17, musicpd-dev-team@lists.sourceforge.net) AC_INIT(mpd, 0.19.19, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0 VERSION_MAJOR=0
VERSION_MINOR=19 VERSION_MINOR=19
VERSION_REVISION=17 VERSION_REVISION=19
VERSION_EXTRA=0 VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx]) AC_CONFIG_SRCDIR([src/Main.cxx])
@@ -542,8 +542,6 @@ AC_ARG_ENABLE(sidplay,
AS_HELP_STRING([--enable-sidplay], AS_HELP_STRING([--enable-sidplay],
[enable C64 SID support via libsidplay2]),, [enable C64 SID support via libsidplay2]),,
enable_sidplay=auto) enable_sidplay=auto)
MPD_DEPENDS([enable_sidplay], [enable_glib],
[Cannot use --enable-sidplay with --disable-glib])
AC_ARG_ENABLE(shine-encoder, AC_ARG_ENABLE(shine-encoder,
AS_HELP_STRING([--enable-shine-encoder], AS_HELP_STRING([--enable-shine-encoder],
@@ -1335,31 +1333,36 @@ AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enab
dnl --------------------------------- sidplay --------------------------------- dnl --------------------------------- sidplay ---------------------------------
if test x$enable_sidplay != xno; then if test x$enable_sidplay != xno; then
# we're not using pkg-config here dnl Check for libsidplayfp first
# because libsidplay2's .pc file requires libtool PKG_CHECK_MODULES([SIDPLAY], [libsidplayfp libsidutils],
AC_CHECK_LIB([sidplay2],[main],[found_sidplay=yes],[found_sidplay=no],[]) [found_sidplayfp=yes],
[found_sidplayfp=no])
found_sidplay=$found_sidplayfp
fi
if test x$enable_sidplay != xno && test x$found_sidplayfp = xno; then
PKG_CHECK_MODULES([SIDPLAY], [libsidplay2 libsidutils],
[found_sidplay=yes],
[found_sidplay=no])
MPD_AUTO_PRE(sidplay, [sidplay decoder plugin], MPD_AUTO_PRE(sidplay, [sidplay decoder plugin],
[libsidplay2 not found]) [libsidplay2 not found])
fi fi
if test x$enable_sidplay != xno; then if test x$enable_sidplay != xno && test x$found_sidplayfp = xno; then
AC_CHECK_LIB([resid-builder], [main], AC_CHECK_LIB([resid-builder], [main],
[found_sidplay=yes], [found_sidplay=no]) [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], MPD_AUTO_RESULT(sidplay, [sidplay decoder plugin],
[libresid-builder or libsidutils not found]) [libresid-builder not found])
fi fi
if test x$enable_sidplay = xyes; then if test x$enable_sidplay = xyes; then
AC_SUBST(SIDPLAY_LIBS,"-lsidplay2 -lresid-builder -lsidutils") SIDPLAY_LIBS="$SIDPLAY_LIBS -lresid-builder"
AC_SUBST(SIDPLAY_CFLAGS,)
AC_DEFINE(ENABLE_SIDPLAY, 1, [Define for libsidplay2 support]) AC_DEFINE(ENABLE_SIDPLAY, 1, [Define for libsidplay2 support])
if test x$found_sidplayfp = xyes; then
AC_DEFINE(HAVE_SIDPLAYFP, 1, [Define if libsidplayfp is used instead of libsidplay2])
fi
fi fi
AM_CONDITIONAL(ENABLE_SIDPLAY, test x$enable_sidplay = xyes) AM_CONDITIONAL(ENABLE_SIDPLAY, test x$enable_sidplay = xyes)

View File

@@ -41,7 +41,7 @@
<listitem> <listitem>
<para> <para>
the code should be C++11 compliant, and must compile with the code should be C++11 compliant, and must compile with
<application>GCC</application> 4.6 and <application>GCC</application> 4.7 and
<application>clang</application> 3.2 <application>clang</application> 3.2
</para> </para>
</listitem> </listitem>

View File

@@ -231,7 +231,7 @@ input {
# #
#audio_output { #audio_output {
# type "shout" # type "shout"
# encoding "ogg" # optional # encoder "vorbis" # optional
# name "My Shout Stream" # name "My Shout Stream"
# host "localhost" # host "localhost"
# port "8000" # port "8000"

View File

@@ -26,7 +26,7 @@
#include <utility> #include <utility>
#include <cstdint> #include <cstdint>
#if defined(__GNUC__) && !GCC_CHECK_VERSION(4,7) && !defined(__clang__) #if GCC_OLDER_THAN(4,7)
/* std::chrono::duration operators are "constexpr" since gcc 4.7 */ /* std::chrono::duration operators are "constexpr" since gcc 4.7 */
#define chrono_constexpr gcc_pure #define chrono_constexpr gcc_pure
#else #else

View File

@@ -28,24 +28,41 @@
#define GCC_VERSION 0 #define GCC_VERSION 0
#endif #endif
#ifdef __clang__
# define CLANG_VERSION GCC_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
#elif defined(__GNUC__)
# define CLANG_VERSION 0
#endif
/**
* Are we building with the specified version of gcc (not clang or any
* other compiler) or newer?
*/
#define GCC_CHECK_VERSION(major, minor) \ #define GCC_CHECK_VERSION(major, minor) \
(defined(__GNUC__) && GCC_VERSION >= GCC_MAKE_VERSION(major, minor, 0)) (CLANG_VERSION == 0 && \
GCC_VERSION >= GCC_MAKE_VERSION(major, minor, 0))
/**
* Are we building with clang (any version) or at least the specified
* gcc version?
*/
#define CLANG_OR_GCC_VERSION(major, minor) \
(CLANG_VERSION > 0 || GCC_CHECK_VERSION(major, minor))
/** /**
* Are we building with gcc (not clang or any other compiler) and a * Are we building with gcc (not clang or any other compiler) and a
* version older than the specified one? * version older than the specified one?
*/ */
#define GCC_OLDER_THAN(major, minor) \ #define GCC_OLDER_THAN(major, minor) \
(defined(__GNUC__) && !defined(__clang__) && \ (GCC_VERSION > 0 && CLANG_VERSION == 0 && \
GCC_VERSION < GCC_MAKE_VERSION(major, minor, 0)) GCC_VERSION < GCC_MAKE_VERSION(major, minor, 0))
#ifdef __clang__ #ifdef __clang__
# define CLANG_VERSION GCC_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
# if __clang_major__ < 3 # if __clang_major__ < 3
# error Sorry, your clang version is too old. You need at least version 3.1. # error Sorry, your clang version is too old. You need at least version 3.1.
# endif # endif
#elif defined(__GNUC__) #elif defined(__GNUC__)
# if GCC_OLDER_THAN(4,6) # if GCC_OLDER_THAN(4,7)
# error Sorry, your gcc version is too old. You need at least version 4.6. # error Sorry, your gcc version is too old. You need at least version 4.6.
# endif # endif
#else #else
@@ -56,10 +73,9 @@
* Are we building with the specified version of clang or newer? * Are we building with the specified version of clang or newer?
*/ */
#define CLANG_CHECK_VERSION(major, minor) \ #define CLANG_CHECK_VERSION(major, minor) \
(defined(__clang__) && \ (CLANG_VERSION >= GCC_MAKE_VERSION(major, minor, 0))
CLANG_VERSION >= GCC_MAKE_VERSION(major, minor, 0))
#if GCC_CHECK_VERSION(4,0) #if CLANG_OR_GCC_VERSION(4,0)
/* GCC 4.x */ /* GCC 4.x */
@@ -119,7 +135,7 @@
#endif #endif
#if GCC_CHECK_VERSION(4,3) #if CLANG_OR_GCC_VERSION(4,3)
#define gcc_hot __attribute__((hot)) #define gcc_hot __attribute__((hot))
#define gcc_cold __attribute__((cold)) #define gcc_cold __attribute__((cold))
@@ -131,7 +147,7 @@
#endif /* ! GCC_UNUSED >= 40300 */ #endif /* ! GCC_UNUSED >= 40300 */
#if GCC_CHECK_VERSION(4,6) && !defined(__clang__) #if GCC_CHECK_VERSION(4,6)
#define gcc_flatten __attribute__((flatten)) #define gcc_flatten __attribute__((flatten))
#else #else
#define gcc_flatten #define gcc_flatten
@@ -140,7 +156,7 @@
#ifndef __cplusplus #ifndef __cplusplus
/* plain C99 has "restrict" */ /* plain C99 has "restrict" */
#define gcc_restrict restrict #define gcc_restrict restrict
#elif GCC_CHECK_VERSION(4,0) #elif CLANG_OR_GCC_VERSION(4,0)
/* "__restrict__" is a GCC extension for C++ */ /* "__restrict__" is a GCC extension for C++ */
#define gcc_restrict __restrict__ #define gcc_restrict __restrict__
#else #else
@@ -158,7 +174,7 @@
#define final #define final
#endif #endif
#if defined(__clang__) || GCC_CHECK_VERSION(4,8) #if CLANG_OR_GCC_VERSION(4,8)
#define gcc_alignas(T, fallback) alignas(T) #define gcc_alignas(T, fallback) alignas(T)
#else #else
#define gcc_alignas(T, fallback) gcc_aligned(fallback) #define gcc_alignas(T, fallback) gcc_aligned(fallback)

View File

@@ -53,7 +53,7 @@ public:
Bzip2ArchiveFile(Path path, InputStream *_is) Bzip2ArchiveFile(Path path, InputStream *_is)
:ArchiveFile(bz2_archive_plugin), :ArchiveFile(bz2_archive_plugin),
name(PathTraitsFS::GetBase(path.c_str())), name(path.GetBase().c_str()),
istream(_is) { istream(_is) {
// remove .bz2 suffix // remove .bz2 suffix
const size_t len = name.length(); const size_t len = name.length();

View File

@@ -46,7 +46,7 @@ StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums,
for (const auto &item : tag) { for (const auto &item : tag) {
switch (item.type) { switch (item.type) {
case TAG_ARTIST: case TAG_ARTIST:
#if defined(__clang__) || GCC_CHECK_VERSION(4,8) #if CLANG_OR_GCC_VERSION(4,8)
artists.emplace(item.value); artists.emplace(item.value);
#else #else
artists.insert(item.value); artists.insert(item.value);
@@ -54,7 +54,7 @@ StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums,
break; break;
case TAG_ALBUM: case TAG_ALBUM:
#if defined(__clang__) || GCC_CHECK_VERSION(4,8) #if CLANG_OR_GCC_VERSION(4,8)
albums.emplace(item.value); albums.emplace(item.value);
#else #else
albums.insert(item.value); albums.insert(item.value);

View File

@@ -749,7 +749,7 @@ UpnpDatabase::VisitUniqueTags(const DatabaseSelection &selection,
const char *value = dirent.tag.GetValue(tag); const char *value = dirent.tag.GetValue(tag);
if (value != nullptr) { if (value != nullptr) {
#if defined(__clang__) || GCC_CHECK_VERSION(4,8) #if CLANG_OR_GCC_VERSION(4,8)
values.emplace(value); values.emplace(value);
#else #else
values.insert(value); values.insert(value);

View File

@@ -34,7 +34,9 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#ifdef HAVE_GLIB
static constexpr Domain exclude_list_domain("exclude_list"); static constexpr Domain exclude_list_domain("exclude_list");
#endif
bool bool
ExcludeList::LoadFile(Path path_fs) ExcludeList::LoadFile(Path path_fs)

View File

@@ -98,6 +98,7 @@ decoder_input_stream_open(DecoderControl &dc, const char *uri)
if (!is->Check(error)) { if (!is->Check(error)) {
dc.Unlock(); dc.Unlock();
delete is;
LogError(error); LogError(error);
return nullptr; return nullptr;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2003-2016 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.
*/
/* necessary because libavutil/common.h uses UINT64_C */
#define __STDC_CONSTANT_MACROS
#include "config.h"
#include "FfmpegIo.hxx"
#include "../DecoderAPI.hxx"
#include "input/InputStream.hxx"
#include "util/Error.hxx"
AvioStream::~AvioStream()
{
if (io != nullptr) {
av_free(io->buffer);
av_free(io);
}
}
static int
mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size)
{
AvioStream *stream = (AvioStream *)opaque;
return decoder_read(stream->decoder, stream->input,
(void *)buf, size);
}
static int64_t
mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
{
AvioStream *stream = (AvioStream *)opaque;
switch (whence) {
case SEEK_SET:
break;
case SEEK_CUR:
pos += stream->input.GetOffset();
break;
case SEEK_END:
if (!stream->input.KnownSize())
return -1;
pos += stream->input.GetSize();
break;
case AVSEEK_SIZE:
if (!stream->input.KnownSize())
return -1;
return stream->input.GetSize();
default:
return -1;
}
if (!stream->input.LockSeek(pos, IgnoreError()))
return -1;
return stream->input.GetOffset();
}
bool
AvioStream::Open()
{
constexpr size_t BUFFER_SIZE = 8192;
auto buffer = (unsigned char *)av_malloc(BUFFER_SIZE);
if (buffer == nullptr)
return false;
io = avio_alloc_context(buffer, BUFFER_SIZE,
false, this,
mpd_ffmpeg_stream_read, nullptr,
input.IsSeekable()
? mpd_ffmpeg_stream_seek : nullptr);
/* If avio_alloc_context() fails, who frees the buffer? The
libavformat API documentation does not specify this, it
only says that AVIOContext.buffer must be freed in the end,
however no AVIOContext exists in that failure code path. */
return io != nullptr;
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2003-2016 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_FFMPEG_IO_HXX
#define MPD_FFMPEG_IO_HXX
#include "check.h"
extern "C" {
#include "libavformat/avio.h"
}
#include <stdint.h>
class InputStream;
struct Decoder;
struct AvioStream {
Decoder *const decoder;
InputStream &input;
AVIOContext *io;
AvioStream(Decoder *_decoder, InputStream &_input)
:decoder(_decoder), input(_input), io(nullptr) {}
~AvioStream();
bool Open();
};
#endif

View File

@@ -36,9 +36,9 @@ static const struct tag_table ffmpeg_tags[] = {
}; };
static void static void
ffmpeg_copy_metadata(TagType type, FfmpegScanTag(TagType type,
AVDictionary *m, const char *name, AVDictionary *m, const char *name,
const struct tag_handler *handler, void *handler_ctx) const struct tag_handler *handler, void *handler_ctx)
{ {
AVDictionaryEntry *mt = nullptr; AVDictionaryEntry *mt = nullptr;
@@ -48,8 +48,8 @@ ffmpeg_copy_metadata(TagType type,
} }
static void static void
ffmpeg_scan_pairs(AVDictionary *dict, FfmpegScanPairs(AVDictionary *dict,
const struct tag_handler *handler, void *handler_ctx) const struct tag_handler *handler, void *handler_ctx)
{ {
AVDictionaryEntry *i = nullptr; AVDictionaryEntry *i = nullptr;
@@ -59,18 +59,20 @@ ffmpeg_scan_pairs(AVDictionary *dict,
} }
void void
ffmpeg_scan_dictionary(AVDictionary *dict, FfmpegScanDictionary(AVDictionary *dict,
const struct tag_handler *handler, void *handler_ctx) const struct tag_handler *handler, void *handler_ctx)
{ {
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) if (handler->tag != nullptr) {
ffmpeg_copy_metadata(TagType(i), dict, tag_item_names[i], for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
handler, handler_ctx); FfmpegScanTag(TagType(i), dict, tag_item_names[i],
handler, handler_ctx);
for (const struct tag_table *i = ffmpeg_tags; for (const struct tag_table *i = ffmpeg_tags;
i->name != nullptr; ++i) i->name != nullptr; ++i)
ffmpeg_copy_metadata(i->type, dict, i->name, FfmpegScanTag(i->type, dict, i->name,
handler, handler_ctx); handler, handler_ctx);
}
if (handler->pair != nullptr) if (handler->pair != nullptr)
ffmpeg_scan_pairs(dict, handler, handler_ctx); FfmpegScanPairs(dict, handler, handler_ctx);
} }

View File

@@ -32,7 +32,7 @@ extern "C" {
struct tag_handler; struct tag_handler;
void void
ffmpeg_scan_dictionary(AVDictionary *dict, FfmpegScanDictionary(AVDictionary *dict,
const tag_handler *handler, void *handler_ctx); const tag_handler *handler, void *handler_ctx);
#endif #endif

View File

@@ -22,67 +22,61 @@
#include "../DecoderAPI.hxx" #include "../DecoderAPI.hxx"
#include "tag/TagHandler.hxx" #include "tag/TagHandler.hxx"
#include "fs/Path.hxx" #include "fs/Path.hxx"
#include "fs/AllocatedPath.hxx"
#include "util/Macros.hxx"
#include "util/FormatString.hxx" #include "util/FormatString.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "util/Error.hxx"
#include "system/ByteOrder.hxx" #include "system/ByteOrder.hxx"
#include "system/FatalError.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <errno.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <glib.h>
#ifdef HAVE_SIDPLAYFP
#include <sidplayfp/sidplayfp.h>
#include <sidplayfp/SidInfo.h>
#include <sidplayfp/SidConfig.h>
#include <sidplayfp/SidTune.h>
#include <sidplayfp/SidTuneInfo.h>
#include <sidplayfp/builders/resid.h>
#include <sidplayfp/builders/residfp.h>
#include <sidplayfp/SidDatabase.h>
#else
#include <sidplay/sidplay2.h> #include <sidplay/sidplay2.h>
#include <sidplay/builders/resid.h> #include <sidplay/builders/resid.h>
#include <sidplay/utils/SidTuneMod.h> #include <sidplay/utils/SidTuneMod.h>
#include <sidplay/utils/SidDatabase.h>
#endif
#define SUBTUNE_PREFIX "tune_" #define SUBTUNE_PREFIX "tune_"
static constexpr Domain sidplay_domain("sidplay"); static constexpr Domain sidplay_domain("sidplay");
static GPatternSpec *path_with_subtune; static SidDatabase *songlength_database;
static const char *songlength_file;
static GKeyFile *songlength_database;
static bool all_files_are_containers; static bool all_files_are_containers;
static unsigned default_songlength; static unsigned default_songlength;
static bool filter_setting; static bool filter_setting;
static GKeyFile * static SidDatabase *
sidplay_load_songlength_db(const char *path) sidplay_load_songlength_db(const Path path)
{ {
GError *error = nullptr; SidDatabase *db = new SidDatabase();
gchar *data; #ifdef HAVE_SIDPLAYFP
gsize size; bool error = !db->open(path.c_str());
#else
if (!g_file_get_contents(path, &data, &size, &error)) { bool error = db->open(path.c_str()) < 0;
#endif
if (error) {
FormatError(sidplay_domain, FormatError(sidplay_domain,
"unable to read songlengths file %s: %s", "unable to read songlengths file %s: %s",
path, error->message); path.c_str(), db->error());
g_error_free(error); delete db;
return nullptr; return nullptr;
} }
/* 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) {
FormatError(sidplay_domain,
"unable to parse songlengths file %s: %s",
path, error->message);
g_error_free(error);
g_key_file_free(db);
return nullptr;
}
g_key_file_set_list_separator(db, ' ');
return db; return db;
} }
@@ -90,18 +84,18 @@ static bool
sidplay_init(const config_param &param) sidplay_init(const config_param &param)
{ {
/* read the songlengths database file */ /* read the songlengths database file */
songlength_file = param.GetBlockValue("songlength_database"); Error error;
if (songlength_file != nullptr) const auto database_path = param.GetBlockPath("songlength_database", error);
songlength_database = sidplay_load_songlength_db(songlength_file); if (!database_path.IsNull())
songlength_database = sidplay_load_songlength_db(database_path);
else if (error.IsDefined())
FatalError(error);
default_songlength = param.GetBlockValue("default_songlength", 0u); default_songlength = param.GetBlockValue("default_songlength", 0u);
all_files_are_containers = all_files_are_containers =
param.GetBlockValue("all_files_are_containers", true); param.GetBlockValue("all_files_are_containers", true);
path_with_subtune=g_pattern_spec_new(
"*/" SUBTUNE_PREFIX "???.sid");
filter_setting = param.GetBlockValue("filter", true); filter_setting = param.GetBlockValue("filter", true);
return true; return true;
@@ -110,98 +104,81 @@ sidplay_init(const config_param &param)
static void static void
sidplay_finish() sidplay_finish()
{ {
g_pattern_spec_free(path_with_subtune); delete songlength_database;
if(songlength_database)
g_key_file_free(songlength_database);
} }
/** struct SidplayContainerPath {
* returns the file path stripped of any /tune_xxx.sid subtune AllocatedPath path;
* suffix unsigned track;
*/ };
static char *
get_container_name(Path path_fs)
{
char *path_container = strdup(path_fs.c_str());
if(!g_pattern_match(path_with_subtune, gcc_pure
strlen(path_container), path_container, nullptr))
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 unsigned static unsigned
get_song_num(const char *path_fs) ParseSubtuneName(const char *base)
{ {
if(g_pattern_match(path_with_subtune, if (memcmp(base, SUBTUNE_PREFIX, sizeof(SUBTUNE_PREFIX) - 1) != 0)
strlen(path_fs), path_fs, nullptr)) { return 0;
char *sub=g_strrstr(path_fs, "/" SUBTUNE_PREFIX);
if(!sub) return 1;
sub+=strlen("/" SUBTUNE_PREFIX); base += sizeof(SUBTUNE_PREFIX) - 1;
int song_num=strtol(sub, nullptr, 10);
if (errno == EINVAL) char *endptr;
return 1; auto track = strtoul(base, &endptr, 10);
else if (endptr == base || *endptr != '.')
return song_num; return 0;
} else
return 1; return track;
} }
/* get the song length in seconds */ /**
* returns the file path stripped of any /tune_xxx.* subtune suffix
* and the track number (or 1 if no "tune_xxx" suffix is present).
*/
static SidplayContainerPath
ParseContainerPath(Path path_fs)
{
const Path base = path_fs.GetBase();
unsigned track;
if (base.IsNull() ||
(track = ParseSubtuneName(base.c_str())) < 1)
return { AllocatedPath(path_fs), 1 };
return { path_fs.GetDirectoryName(), track };
}
#ifdef HAVE_SIDPLAYFP
static SignedSongTime static SignedSongTime
get_song_length(Path path_fs) get_song_length(SidTune &tune)
{ {
if (songlength_database == nullptr) if (songlength_database == nullptr)
return SignedSongTime::Negative(); return SignedSongTime::Negative();
char *sid_file = get_container_name(path_fs); const auto length = songlength_database->length(tune);
SidTuneMod tune(sid_file); if (length < 0)
free(sid_file);
if(!tune) {
LogWarning(sidplay_domain,
"failed to load file for calculating md5 sum");
return SignedSongTime::Negative(); return SignedSongTime::Negative();
}
char md5sum[SIDTUNE_MD5_LENGTH+1];
tune.createMD5(md5sum);
const unsigned song_num = get_song_num(path_fs.c_str()); return SignedSongTime::FromS(length);
gsize num_items;
gchar **values=g_key_file_get_string_list(songlength_database,
"Database", md5sum, &num_items, nullptr);
if(!values || song_num>num_items) {
g_strfreev(values);
return SignedSongTime::Negative();
}
int minutes=strtol(values[song_num-1], nullptr, 10);
if(errno==EINVAL) minutes=0;
int seconds;
char *ptr=strchr(values[song_num-1], ':');
if(ptr) {
seconds=strtol(ptr+1, nullptr, 10);
if(errno==EINVAL) seconds=0;
} else
seconds=0;
g_strfreev(values);
return SignedSongTime::FromS((minutes * 60) + seconds);
} }
#else
static SignedSongTime
get_song_length(SidTuneMod &tune)
{
assert(tune);
if (songlength_database == nullptr)
return SignedSongTime::Negative();
const auto length = songlength_database->length(tune);
if (length < 0)
return SignedSongTime::Negative();
return SignedSongTime::FromS(length);
}
#endif
static void static void
sidplay_file_decode(Decoder &decoder, Path path_fs) sidplay_file_decode(Decoder &decoder, Path path_fs)
{ {
@@ -209,26 +186,43 @@ sidplay_file_decode(Decoder &decoder, Path path_fs)
/* load the tune */ /* load the tune */
char *path_container=get_container_name(path_fs); const auto container = ParseContainerPath(path_fs);
SidTune tune(path_container, nullptr, true); #ifdef HAVE_SIDPLAYFP
free(path_container); SidTune tune(container.path.c_str());
if (!tune) { #else
LogWarning(sidplay_domain, "failed to load file"); SidTuneMod tune(container.path.c_str());
#endif
if (!tune.getStatus()) {
#ifdef HAVE_SIDPLAYFP
const char *error = tune.statusString();
#else
const char *error = tune.getInfo().statusString;
#endif
FormatWarning(sidplay_domain, "failed to load file: %s",
error);
return; return;
} }
const int song_num = get_song_num(path_fs.c_str()); const int song_num = container.track;
tune.selectSong(song_num); tune.selectSong(song_num);
auto duration = get_song_length(path_fs); auto duration = get_song_length(tune);
if (duration.IsNegative() && default_songlength > 0) if (duration.IsNegative() && default_songlength > 0)
duration = SongTime::FromS(default_songlength); duration = SongTime::FromS(default_songlength);
/* initialize the player */ /* initialize the player */
#ifdef HAVE_SIDPLAYFP
sidplayfp player;
#else
sidplay2 player; sidplay2 player;
int iret = player.load(&tune); #endif
if (iret != 0) { #ifdef HAVE_SIDPLAYFP
bool error = !player.load(&tune);
#else
bool error = player.load(&tune) < 0;
#endif
if (error) {
FormatWarning(sidplay_domain, FormatWarning(sidplay_domain,
"sidplay2.load() failed: %s", player.error()); "sidplay2.load() failed: %s", player.error());
return; return;
@@ -236,53 +230,104 @@ sidplay_file_decode(Decoder &decoder, Path path_fs)
/* initialize the builder */ /* initialize the builder */
ReSIDBuilder builder("ReSID"); #ifdef HAVE_SIDPLAYFP
if (!builder) { ReSIDfpBuilder builder("ReSID");
LogWarning(sidplay_domain, if (!builder.getStatus()) {
"failed to initialize ReSIDBuilder"); FormatWarning(sidplay_domain,
"failed to initialize ReSIDfpBuilder: %s",
builder.error());
return; return;
} }
builder.create(player.info().maxsids());
if (!builder.getStatus()) {
FormatWarning(sidplay_domain,
"ReSIDfpBuilder.create() failed: %s",
builder.error());
return;
}
#else
ReSIDBuilder builder("ReSID");
builder.create(player.info().maxsids); builder.create(player.info().maxsids);
if (!builder) { if (!builder) {
LogWarning(sidplay_domain, "ReSIDBuilder.create() failed"); FormatWarning(sidplay_domain, "ReSIDBuilder.create() failed: %s",
builder.error());
return; return;
} }
#endif
builder.filter(filter_setting); builder.filter(filter_setting);
if (!builder) { #ifdef HAVE_SIDPLAYFP
LogWarning(sidplay_domain, "ReSIDBuilder.filter() failed"); if (!builder.getStatus()) {
FormatWarning(sidplay_domain,
"ReSIDfpBuilder.filter() failed: %s",
builder.error());
return; return;
} }
#else
if (!builder) {
FormatWarning(sidplay_domain, "ReSIDBuilder.filter() failed: %s",
builder.error());
return;
}
#endif
/* configure the player */ /* configure the player */
sid2_config_t config = player.config(); auto config = player.config();
#ifndef HAVE_SIDPLAYFP
config.clockDefault = SID2_CLOCK_PAL; config.clockDefault = SID2_CLOCK_PAL;
config.clockForced = true; config.clockForced = true;
config.clockSpeed = SID2_CLOCK_CORRECT; config.clockSpeed = SID2_CLOCK_CORRECT;
#endif
config.frequency = 48000; config.frequency = 48000;
#ifndef HAVE_SIDPLAYFP
config.optimisation = SID2_DEFAULT_OPTIMISATION; config.optimisation = SID2_DEFAULT_OPTIMISATION;
config.precision = 16; config.precision = 16;
config.sidDefault = SID2_MOS6581; config.sidDefault = SID2_MOS6581;
#endif
config.sidEmulation = &builder; config.sidEmulation = &builder;
#ifdef HAVE_SIDPLAYFP
config.samplingMethod = SidConfig::INTERPOLATE;
config.fastSampling = false;
#else
config.sidModel = SID2_MODEL_CORRECT; config.sidModel = SID2_MODEL_CORRECT;
config.sidSamples = true; config.sidSamples = true;
config.sampleFormat = IsLittleEndian() config.sampleFormat = IsLittleEndian()
? SID2_LITTLE_SIGNED ? SID2_LITTLE_SIGNED
: SID2_BIG_SIGNED; : SID2_BIG_SIGNED;
if (tune.isStereo()) { #endif
#ifdef HAVE_SIDPLAYFP
const bool stereo = tune.getInfo()->sidChips() >= 2;
#else
const bool stereo = tune.isStereo();
#endif
if (stereo) {
#ifdef HAVE_SIDPLAYFP
config.playback = SidConfig::STEREO;
#else
config.playback = sid2_stereo; config.playback = sid2_stereo;
#endif
channels = 2; channels = 2;
} else { } else {
#ifdef HAVE_SIDPLAYFP
config.playback = SidConfig::MONO;
#else
config.playback = sid2_mono; config.playback = sid2_mono;
#endif
channels = 1; channels = 1;
} }
iret = player.config(config); #ifdef HAVE_SIDPLAYFP
if (iret != 0) { error = !player.config(config);
#else
error = player.config(config) < 0;
#endif
if (error) {
FormatWarning(sidplay_domain, FormatWarning(sidplay_domain,
"sidplay2.config() failed: %s", player.error()); "sidplay2.config() failed: %s", player.error());
return; return;
@@ -297,17 +342,21 @@ sidplay_file_decode(Decoder &decoder, Path path_fs)
/* .. and play */ /* .. and play */
#ifdef HAVE_SIDPLAYFP
constexpr unsigned timebase = 1;
#else
const unsigned timebase = player.timebase(); const unsigned timebase = player.timebase();
#endif
const unsigned end = duration.IsNegative() const unsigned end = duration.IsNegative()
? 0u ? 0u
: duration.ToScale<uint64_t>(timebase); : duration.ToScale<uint64_t>(timebase);
DecoderCommand cmd; DecoderCommand cmd;
do { do {
char buffer[4096]; short buffer[4096];
size_t nbytes; size_t nbytes;
nbytes = player.play(buffer, sizeof(buffer)); nbytes = player.play(buffer, ARRAY_SIZE(buffer));
if (nbytes == 0) if (nbytes == 0)
break; break;
@@ -328,7 +377,7 @@ sidplay_file_decode(Decoder &decoder, Path path_fs)
/* ignore data until target time is reached */ /* ignore data until target time is reached */
while(data_time<target_time) { while(data_time<target_time) {
nbytes=player.play(buffer, sizeof(buffer)); nbytes=player.play(buffer, ARRAY_SIZE(buffer));
if(nbytes==0) if(nbytes==0)
break; break;
data_time = player.time(); data_time = player.time();
@@ -343,41 +392,72 @@ sidplay_file_decode(Decoder &decoder, Path path_fs)
} while (cmd != DecoderCommand::STOP); } while (cmd != DecoderCommand::STOP);
} }
gcc_pure
static const char *
GetInfoString(const SidTuneInfo &info, unsigned i)
{
#ifdef HAVE_SIDPLAYFP
return info.numberOfInfoStrings() > i
? info.infoString(i)
: nullptr;
#else
return info.numberOfInfoStrings > i
? info.infoString[i]
: nullptr;
#endif
}
static bool static bool
sidplay_scan_file(Path path_fs, sidplay_scan_file(Path path_fs,
const struct tag_handler *handler, void *handler_ctx) const struct tag_handler *handler, void *handler_ctx)
{ {
const int song_num = get_song_num(path_fs.c_str()); const auto container = ParseContainerPath(path_fs);
char *path_container=get_container_name(path_fs); const unsigned song_num = container.track;
SidTune tune(path_container, nullptr, true); #ifdef HAVE_SIDPLAYFP
free(path_container); SidTune tune(container.path.c_str());
if (!tune) #else
SidTuneMod tune(container.path.c_str());
#endif
if (!tune.getStatus())
return false; return false;
tune.selectSong(song_num);
#ifdef HAVE_SIDPLAYFP
const SidTuneInfo &info = *tune.getInfo();
const unsigned n_tracks = info.songs();
#else
const SidTuneInfo &info = tune.getInfo(); const SidTuneInfo &info = tune.getInfo();
const unsigned n_tracks = info.songs;
#endif
/* title */ /* title */
const char *title; const char *title = GetInfoString(info, 0);
if (info.numberOfInfoStrings > 0 && info.infoString[0] != nullptr) if (title == nullptr)
title=info.infoString[0]; title = "";
else
title="";
if(info.songs>1) { if (n_tracks > 1) {
char tag_title[1024]; char tag_title[1024];
snprintf(tag_title, sizeof(tag_title), snprintf(tag_title, sizeof(tag_title),
"%s (%d/%d)", "%s (%d/%u)",
title, song_num, info.songs); title, song_num, n_tracks);
tag_handler_invoke_tag(handler, handler_ctx, tag_handler_invoke_tag(handler, handler_ctx,
TAG_TITLE, tag_title); TAG_TITLE, tag_title);
} else } else
tag_handler_invoke_tag(handler, handler_ctx, TAG_TITLE, title); tag_handler_invoke_tag(handler, handler_ctx, TAG_TITLE, title);
/* artist */ /* artist */
if (info.numberOfInfoStrings > 1 && info.infoString[1] != nullptr) const char *artist = GetInfoString(info, 1);
if (artist != nullptr)
tag_handler_invoke_tag(handler, handler_ctx, TAG_ARTIST, tag_handler_invoke_tag(handler, handler_ctx, TAG_ARTIST,
info.infoString[1]); artist);
/* date */
const char *date = GetInfoString(info, 2);
if (date != nullptr)
tag_handler_invoke_tag(handler, handler_ctx, TAG_DATE,
date);
/* track */ /* track */
char track[16]; char track[16];
@@ -385,7 +465,7 @@ sidplay_scan_file(Path path_fs,
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track); tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track);
/* time */ /* time */
const auto duration = get_song_length(path_fs); const auto duration = get_song_length(tune);
if (!duration.IsNegative()) if (!duration.IsNegative())
tag_handler_invoke_duration(handler, handler_ctx, tag_handler_invoke_duration(handler, handler_ctx,
SongTime(duration)); SongTime(duration));
@@ -397,19 +477,25 @@ static char *
sidplay_container_scan(Path path_fs, const unsigned int tnum) sidplay_container_scan(Path path_fs, const unsigned int tnum)
{ {
SidTune tune(path_fs.c_str(), nullptr, true); SidTune tune(path_fs.c_str(), nullptr, true);
if (!tune) if (!tune.getStatus())
return nullptr; return nullptr;
const SidTuneInfo &info=tune.getInfo(); #ifdef HAVE_SIDPLAYFP
const SidTuneInfo &info = *tune.getInfo();
const unsigned n_tracks = info.songs();
#else
const SidTuneInfo &info = tune.getInfo();
const unsigned n_tracks = info.songs;
#endif
/* Don't treat sids containing a single tune /* Don't treat sids containing a single tune
as containers */ as containers */
if(!all_files_are_containers && info.songs<2) if(!all_files_are_containers && n_tracks < 2)
return nullptr; return nullptr;
/* Construct container/tune path names, eg. /* Construct container/tune path names, eg.
Delta.sid/tune_001.sid */ Delta.sid/tune_001.sid */
if(tnum<=info.songs) { if (tnum <= n_tracks) {
return FormatNew(SUBTUNE_PREFIX "%03u.sid", tnum); return FormatNew(SUBTUNE_PREFIX "%03u.sid", tnum);
} else } else
return nullptr; return nullptr;

View File

@@ -65,6 +65,24 @@ wildmidi_finish(void)
WildMidi_Shutdown(); WildMidi_Shutdown();
} }
static DecoderCommand
wildmidi_output(Decoder &decoder, midi *wm)
{
#ifdef LIBWILDMIDI_VER_MAJOR
/* WildMidi 0.4 has switched from "char*" to "int8_t*" */
int8_t buffer[4096];
#else
/* pre 0.4 */
char buffer[4096];
#endif
int length = WildMidi_GetOutput(wm, buffer, sizeof(buffer));
if (length <= 0)
return DecoderCommand::STOP;
return decoder_data(decoder, nullptr, buffer, length, 0);
}
static void static void
wildmidi_file_decode(Decoder &decoder, Path path_fs) wildmidi_file_decode(Decoder &decoder, Path path_fs)
{ {
@@ -94,18 +112,11 @@ wildmidi_file_decode(Decoder &decoder, Path path_fs)
DecoderCommand cmd; DecoderCommand cmd;
do { do {
char buffer[4096];
int len;
info = WildMidi_GetInfo(wm); info = WildMidi_GetInfo(wm);
if (info == nullptr) if (info == nullptr)
break; break;
len = WildMidi_GetOutput(wm, buffer, sizeof(buffer)); cmd = wildmidi_output(decoder, wm);
if (len <= 0)
break;
cmd = decoder_data(decoder, nullptr, buffer, len, 0);
if (cmd == DecoderCommand::SEEK) { if (cmd == DecoderCommand::SEEK) {
unsigned long seek_where = unsigned long seek_where =

View File

@@ -111,7 +111,7 @@ AllocatedPath::ChopSeparators()
while (l >= 2 && PathTraitsFS::IsSeparator(p[l - 1])) { while (l >= 2 && PathTraitsFS::IsSeparator(p[l - 1])) {
--l; --l;
#if GCC_CHECK_VERSION(4,7) && !defined(__clang__) #if GCC_CHECK_VERSION(4,7)
value.pop_back(); value.pop_back();
#else #else
value.erase(value.end() - 1, value.end()); value.erase(value.end() - 1, value.end());

View File

@@ -252,7 +252,7 @@ public:
void ChopSeparators(); void ChopSeparators();
gcc_pure gcc_pure
bool IsAbsolute() { bool IsAbsolute() const {
return PathTraitsFS::IsAbsolute(c_str()); return PathTraitsFS::IsAbsolute(c_str());
} }
}; };

View File

@@ -29,6 +29,8 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
class AllocatedPath;
/** /**
* A path name in the native file system character set. * A path name in the native file system character set.
* *
@@ -128,6 +130,22 @@ public:
gcc_pure gcc_pure
std::string ToUTF8() const; std::string ToUTF8() const;
/**
* Determine the "base" file name.
* The return value points inside this object.
*/
gcc_pure
Path GetBase() const {
return FromFS(PathTraitsFS::GetBase(value));
}
/**
* Gets directory name of this path.
* Returns a "nulled" instance on error.
*/
gcc_pure
AllocatedPath GetDirectoryName() const;
/** /**
* Determine the relative part of the given path to this * Determine the relative part of the given path to this
* object, not including the directory separator. Returns an * object, not including the directory separator. Returns an
@@ -140,7 +158,7 @@ public:
} }
gcc_pure gcc_pure
bool IsAbsolute() { bool IsAbsolute() const {
return PathTraitsFS::IsAbsolute(c_str()); return PathTraitsFS::IsAbsolute(c_str());
} }
}; };

28
src/fs/Path2.cxx Normal file
View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2003-2014 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 "Path.hxx"
#include "AllocatedPath.hxx"
AllocatedPath
Path::GetDirectoryName() const
{
return AllocatedPath::FromFS(PathTraitsFS::GetParent(c_str()));
}

72
src/lib/ffmpeg/Buffer.hxx Normal file
View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2003-2014 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_FFMPEG_BUFFER_HXX
#define MPD_FFMPEG_BUFFER_HXX
extern "C" {
#include <libavutil/mem.h>
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 18, 0)
#define HAVE_AV_FAST_MALLOC
#else
#include <libavcodec/avcodec.h>
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 25, 0)
#define HAVE_AV_FAST_MALLOC
#endif
#endif
}
#include <stddef.h>
/* suppress the ffmpeg compatibility macro */
#ifdef SampleFormat
#undef SampleFormat
#endif
class FfmpegBuffer {
void *data;
unsigned size;
public:
FfmpegBuffer():data(nullptr), size(0) {}
~FfmpegBuffer() {
av_free(data);
}
void *Get(size_t min_size) {
#ifdef HAVE_AV_FAST_MALLOC
av_fast_malloc(&data, &size, min_size);
#else
void *new_data = av_fast_realloc(data, &size, min_size);
if (new_data == nullptr)
return AVERROR(ENOMEM);
data = new_data;
#endif
return data;
}
template<typename T>
T *GetT(size_t n) {
return (T *)Get(n * sizeof(T));
}
};
#endif

38
src/lib/ffmpeg/Init.cxx Normal file
View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2003-2014 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.
*/
/* necessary because libavutil/common.h uses UINT64_C */
#define __STDC_CONSTANT_MACROS
#include "config.h"
#include "Init.hxx"
#include "LogCallback.hxx"
extern "C" {
#include <libavformat/avformat.h>
}
void
FfmpegInit()
{
av_log_set_callback(FfmpegLogCallback);
av_register_all();
}

26
src/lib/ffmpeg/Init.hxx Normal file
View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2003-2014 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_FFMPEG_INIT_HXX
#define MPD_FFMPEG_INIT_HXX
void
FfmpegInit();
#endif

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2003-2014 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.
*/
/* necessary because libavutil/common.h uses UINT64_C */
#define __STDC_CONSTANT_MACROS
#include "config.h"
#include "LogCallback.hxx"
#include "Domain.hxx"
#include "LogV.hxx"
#include "util/Domain.hxx"
extern "C" {
#include <libavutil/log.h>
}
#include <stdio.h>
gcc_const
static LogLevel
FfmpegImportLogLevel(int level)
{
if (level <= AV_LOG_FATAL)
return LogLevel::ERROR;
if (level <= AV_LOG_WARNING)
return LogLevel::WARNING;
if (level <= AV_LOG_INFO)
return LogLevel::INFO;
return LogLevel::DEBUG;
}
void
FfmpegLogCallback(gcc_unused void *ptr, int level, const char *fmt, va_list vl)
{
const AVClass * cls = nullptr;
if (ptr != nullptr)
cls = *(const AVClass *const*)ptr;
if (cls != nullptr) {
char domain[64];
snprintf(domain, sizeof(domain), "%s/%s",
ffmpeg_domain.GetName(), cls->item_name(ptr));
const Domain d(domain);
LogFormatV(d, FfmpegImportLogLevel(level), fmt, vl);
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2003-2014 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_FFMPEG_LOG_CALLBACK_HXX
#define MPD_FFMPEG_LOG_CALLBACK_HXX
#include "check.h"
#include <stdarg.h>
void
FfmpegLogCallback(void *ptr, int level, const char *fmt, va_list vl);
#endif

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2003-2014 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 "LogError.hxx"
#include "Domain.hxx"
#include "Log.hxx"
#include <cstdint> /* needed due to libavutil bug */
extern "C" {
#include <libavutil/error.h>
}
void
LogFfmpegError(int errnum)
{
char msg[256];
av_strerror(errnum, msg, sizeof(msg));
LogError(ffmpeg_domain, msg);
}
void
LogFfmpegError(int errnum, const char *prefix)
{
char msg[256];
av_strerror(errnum, msg, sizeof(msg));
FormatError(ffmpeg_domain, "%s: %s", prefix, msg);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2003-2014 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_FFMPEG_LOG_ERROR_HXX
#define MPD_FFMPEG_LOG_ERROR_HXX
void
LogFfmpegError(int errnum);
void
LogFfmpegError(int errnum, const char *prefix);
#endif

104
src/lib/ffmpeg/Time.hxx Normal file
View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2003-2014 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_FFMPEG_TIME_HXX
#define MPD_FFMPEG_TIME_HXX
#include "Chrono.hxx"
#include "Compiler.h"
extern "C" {
#include <libavutil/avutil.h>
#include <libavutil/mathematics.h>
}
#include <assert.h>
#include <stdint.h>
/* suppress the ffmpeg compatibility macro */
#ifdef SampleFormat
#undef SampleFormat
#endif
gcc_const
static inline double
FfmpegTimeToDouble(int64_t t, const AVRational time_base)
{
assert(t != (int64_t)AV_NOPTS_VALUE);
return (double)av_rescale_q(t, time_base, (AVRational){1, 1024})
/ (double)1024;
}
template<typename Ratio>
static inline constexpr AVRational
RatioToAVRational()
{
return { Ratio::num, Ratio::den };
}
/**
* Convert a FFmpeg time stamp to a #SongTime.
*/
gcc_const
static inline SongTime
FromFfmpegTime(int64_t t, const AVRational time_base)
{
assert(t != (int64_t)AV_NOPTS_VALUE);
return SongTime::FromMS(av_rescale_q(t, time_base,
(AVRational){1, 1000}));
}
/**
* Convert a FFmpeg time stamp to a #SignedSongTime.
*/
gcc_const
static inline SignedSongTime
FromFfmpegTimeChecked(int64_t t, const AVRational time_base)
{
return t != (int64_t)AV_NOPTS_VALUE
? SignedSongTime(FromFfmpegTime(t, time_base))
: SignedSongTime::Negative();
}
/**
* Convert a #SongTime to a FFmpeg time stamp with the given base.
*/
gcc_const
static inline int64_t
ToFfmpegTime(SongTime t, const AVRational time_base)
{
return av_rescale_q(t.count(),
RatioToAVRational<SongTime::period>(),
time_base);
}
/**
* Replace #AV_NOPTS_VALUE with the given fallback.
*/
static constexpr int64_t
FfmpegTimestampFallback(int64_t t, int64_t fallback)
{
return gcc_likely(t != int64_t(AV_NOPTS_VALUE))
? t
: fallback;
}
#endif

View File

@@ -59,6 +59,18 @@ NfsManager::Compare::operator()(const ManagedConnection &a,
return result < 0; return result < 0;
} }
inline bool
NfsManager::Compare::operator()(const ManagedConnection &a,
const ManagedConnection &b) const
{
int result = strcmp(a.GetServer(), b.GetServer());
if (result != 0)
return result < 0;
result = strcmp(a.GetExportName(), b.GetExportName());
return result < 0;
}
NfsManager::~NfsManager() NfsManager::~NfsManager()
{ {
assert(GetEventLoop().IsInside()); assert(GetEventLoop().IsInside());

View File

@@ -64,6 +64,10 @@ class NfsManager final : IdleMonitor {
gcc_pure gcc_pure
bool operator()(const ManagedConnection &a, bool operator()(const ManagedConnection &a,
const LookupKey b) const; const LookupKey b) const;
gcc_pure
bool operator()(const ManagedConnection &a,
const ManagedConnection &b) const;
}; };
/** /**

View File

@@ -216,7 +216,7 @@ SmbclientNeighborExplorer::Run()
prev = i; prev = i;
} else { } else {
/* can't see it anymore: move to "lost" */ /* can't see it anymore: move to "lost" */
#if defined(__clang__) || GCC_CHECK_VERSION(4,7) #if CLANG_OR_GCC_VERSION(4,7)
lost.splice_after(lost.before_begin(), list, prev); lost.splice_after(lost.before_begin(), list, prev);
#else #else
/* the forward_list::splice_after() lvalue /* the forward_list::splice_after() lvalue

View File

@@ -545,7 +545,6 @@ pulse_output_open(AudioOutput *ao, AudioFormat &audio_format,
Error &error) Error &error)
{ {
PulseOutput *po = (PulseOutput *)ao; PulseOutput *po = (PulseOutput *)ao;
pa_sample_spec ss;
assert(po->mainloop != nullptr); assert(po->mainloop != nullptr);
@@ -575,11 +574,30 @@ pulse_output_open(AudioOutput *ao, AudioFormat &audio_format,
return false; return false;
} }
/* MPD doesn't support the other pulseaudio sample formats, so /* Use the sample formats that our version of PulseAudio and MPD
we just force MPD to send us everything as 16 bit */ have in common, otherwise force MPD to send 16 bit */
audio_format.format = SampleFormat::S16;
pa_sample_spec ss;
switch (audio_format.format) {
case SampleFormat::FLOAT:
ss.format = PA_SAMPLE_FLOAT32NE;
break;
case SampleFormat::S32:
ss.format = PA_SAMPLE_S32NE;
break;
case SampleFormat::S24_P32:
ss.format = PA_SAMPLE_S24_32NE;
break;
case SampleFormat::S16:
ss.format = PA_SAMPLE_S16NE;
break;
default:
audio_format.format = SampleFormat::S16;
ss.format = PA_SAMPLE_S16NE;
break;
}
ss.format = PA_SAMPLE_S16NE;
ss.rate = audio_format.sample_rate; ss.rate = audio_format.sample_rate;
ss.channels = audio_format.channels; ss.channels = audio_format.channels;

View File

@@ -164,7 +164,9 @@ ShoutOutput::Configure(const config_param &param, Error &error)
} }
} }
const char *encoding = param.GetBlockValue("encoding", "ogg"); const char *encoding = param.GetBlockValue("encoder", nullptr);
if (encoding == nullptr)
encoding = param.GetBlockValue("encoding", "vorbis");
const auto encoder_plugin = shout_encoder_plugin_get(encoding); const auto encoder_plugin = shout_encoder_plugin_get(encoding);
if (encoder_plugin == nullptr) { if (encoder_plugin == nullptr) {
error.Format(config_domain, error.Format(config_domain,

View File

@@ -153,7 +153,7 @@ public:
HttpdOutput(EventLoop &_loop); HttpdOutput(EventLoop &_loop);
~HttpdOutput(); ~HttpdOutput();
#if defined(__clang__) || GCC_CHECK_VERSION(4,7) #if CLANG_OR_GCC_VERSION(4,7)
constexpr constexpr
#endif #endif
static HttpdOutput *Cast(AudioOutput *ao) { static HttpdOutput *Cast(AudioOutput *ao) {

47
src/pcm/Interleave.cxx Normal file
View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2003-2015 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 "Interleave.hxx"
#include <stdint.h>
#include <string.h>
static void
GenericPcmInterleave(uint8_t *dest, ConstBuffer<const uint8_t *> src,
size_t n_frames, size_t sample_size)
{
for (size_t frame = 0; frame < n_frames; ++frame) {
for (size_t channel = 0; channel < src.size; ++channel) {
memcpy(dest, src[channel] + frame * sample_size,
sample_size);
dest += sample_size;
}
}
}
void
PcmInterleave(void *dest, ConstBuffer<const void *> src,
size_t n_frames, size_t sample_size)
{
GenericPcmInterleave((uint8_t *)dest,
ConstBuffer<const uint8_t *>((const uint8_t *const*)src.data,
src.size),
n_frames, sample_size);
}

33
src/pcm/Interleave.hxx Normal file
View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2003-2015 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_PCM_INTERLEAVE_HXX
#define MPD_PCM_INTERLEAVE_HXX
#include "check.h"
#include "util/ConstBuffer.hxx"
/**
* Interleave planar PCM samples from #src to #dest.
*/
void
PcmInterleave(void *dest, ConstBuffer<const void *> src,
size_t n_frames, size_t sample_size);
#endif

View File

@@ -137,7 +137,7 @@ CompositeStorage::Directory::Make(const char *uri)
Directory *directory = this; Directory *directory = this;
while (*uri != 0) { while (*uri != 0) {
const std::string name = NextSegment(uri); const std::string name = NextSegment(uri);
#if defined(__clang__) || GCC_CHECK_VERSION(4,8) #if CLANG_OR_GCC_VERSION(4,8)
auto i = directory->children.emplace(std::move(name), auto i = directory->children.emplace(std::move(name),
Directory()); Directory());
#else #else

View File

@@ -40,7 +40,7 @@
/* well-known big-endian */ /* well-known big-endian */
# define IS_LITTLE_ENDIAN false # define IS_LITTLE_ENDIAN false
# define IS_BIG_ENDIAN true # define IS_BIG_ENDIAN true
#elif defined(__APPLE__) #elif defined(__APPLE__) || defined(__NetBSD__)
/* compile-time check for MacOS */ /* compile-time check for MacOS */
# include <machine/endian.h> # include <machine/endian.h>
# if BYTE_ORDER == LITTLE_ENDIAN # if BYTE_ORDER == LITTLE_ENDIAN

View File

@@ -75,7 +75,7 @@ TagSet::InsertUnique(const Tag &src, TagType type, const char *value,
else else
builder.AddItem(type, value); builder.AddItem(type, value);
CopyTagMask(builder, src, group_mask); CopyTagMask(builder, src, group_mask);
#if defined(__clang__) || GCC_CHECK_VERSION(4,8) #if CLANG_OR_GCC_VERSION(4,8)
emplace(builder.Commit()); emplace(builder.Commit());
#else #else
insert(builder.Commit()); insert(builder.Commit());

View File

@@ -91,7 +91,7 @@ calc_hash(TagType type, const char *p)
return hash ^ type; return hash ^ type;
} }
#if defined(__clang__) || GCC_CHECK_VERSION(4,7) #if CLANG_OR_GCC_VERSION(4,7)
constexpr constexpr
#endif #endif
static inline TagPoolSlot * static inline TagPoolSlot *

View File

@@ -84,7 +84,7 @@ ContainerAttributeOffset(const A C::*p)
* Cast the given pointer to a struct member to its parent structure. * Cast the given pointer to a struct member to its parent structure.
*/ */
template<class C, class A> template<class C, class A>
#if defined(__clang__) || GCC_CHECK_VERSION(4,7) #if CLANG_OR_GCC_VERSION(4,7)
constexpr constexpr
#endif #endif
static inline C & static inline C &
@@ -97,7 +97,7 @@ ContainerCast(A &a, A C::*member)
* Cast the given pointer to a struct member to its parent structure. * Cast the given pointer to a struct member to its parent structure.
*/ */
template<class C, class A> template<class C, class A>
#if defined(__clang__) || GCC_CHECK_VERSION(4,7) #if CLANG_OR_GCC_VERSION(4,7)
constexpr constexpr
#endif #endif
static inline const C & static inline const C &

View File

@@ -41,7 +41,7 @@
#include <assert.h> #include <assert.h>
#if defined(__clang__) || GCC_CHECK_VERSION(4,7) #if CLANG_OR_GCC_VERSION(4,7)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing" #pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif #endif
@@ -114,7 +114,7 @@ public:
} }
}; };
#if defined(__clang__) || GCC_VERSION >= 40700 #if CLANG_OR_GCC_VERSION(4,7)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif

89
src/util/ScopeExit.hxx Normal file
View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2015 Max Kellermann <max@duempel.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SCOPE_EXIT_HXX
#define SCOPE_EXIT_HXX
#include "Compiler.h"
#include <utility>
/**
* Internal class. Do not use directly.
*/
template<typename F>
class ScopeExitGuard : F {
bool enabled = true;
public:
explicit ScopeExitGuard(F &&f):F(std::forward<F>(f)) {}
ScopeExitGuard(ScopeExitGuard &&src)
:F(std::move(src)) {
src.enabled = false;
}
~ScopeExitGuard() {
if (enabled)
F::operator()();
}
ScopeExitGuard(const ScopeExitGuard &) = delete;
ScopeExitGuard &operator=(const ScopeExitGuard &) = delete;
};
/**
* Internal class. Do not use directly.
*/
struct ScopeExitTag {
/* this operator is a trick so we don't need to close
parantheses at the end of the expression AtScopeExit()
call */
template<typename F>
ScopeExitGuard<F> operator+(F &&f) {
return ScopeExitGuard<F>(std::forward<F>(f));
}
};
#define ScopeExitCat(a, b) a ## b
#define ScopeExitName(line) ScopeExitCat(at_scope_exit_, line)
/**
* Call the block after this macro at the end of the current scope.
* Parameters are lambda captures.
*
* This is exception-safe, however the given code block must not throw
* exceptions.
*
* This attempts to be a better boost/scope_exit.hpp, without all of
* Boost's compile-time and runtime bloat.
*/
#define AtScopeExit(...) auto ScopeExitName(__LINE__) = ScopeExitTag() + [__VA_ARGS__]()
#endif