Compare commits

...

178 Commits

Author SHA1 Message Date
Max Kellermann
3bbcda917c release v0.19.21 2016-12-13 10:54:04 +01:00
Max Kellermann
7e43fb79af Makefile.am: fix mpd.socket path in EXTRA_DIST 2016-12-13 10:53:41 +01:00
Max Kellermann
eb2b567da6 NEWS: fix version number 2016-12-13 10:45:53 +01:00
Max Kellermann
ab332d7b2e systemd: add user unit
The user unit omits the "ProtectKernelModules" setting which fails
with modular kernels:

 Failed at step CAPABILITIES spawning /usr/bin/mpd: Operation not permitted

It is unfortunate that systemd (version 232) is unable to reduce its
own capabilities, because this requires us to split system and user
units.

 https://bugs.musicpd.org/view.php?id=4608
2016-12-13 10:24:10 +01:00
Max Kellermann
53e22b81ef systemd: add "system" sub directory 2016-12-13 10:24:10 +01:00
Max Kellermann
3fc9d50adb doc/user: fix --with-systemdsystemunitdir example 2016-12-13 10:24:10 +01:00
Max Kellermann
c2da6dd45b test/test_queue_priority: fix unit test failure after recent "setprio" change 2016-12-13 08:36:42 +01:00
Max Kellermann
7146f825b2 decoder/ffmpeg: fix double free bug
From the avformat_open_input() API documentation:

 "Note that a user-supplied AVFormatContext will be freed on failure."

https://bugs.musicpd.org/view.php?id=4607
2016-12-13 08:34:05 +01:00
Max Kellermann
f61a5f5200 configure.ac: prepare for 0.19.21 2016-12-13 08:31:21 +01:00
Max Kellermann
fef45d469c release v0.19.20 2016-12-09 20:02:07 +01:00
Max Kellermann
e7353ec7e7 Queue: "setprio" re-enqueues old song if priority has been raised
This commit changes a minor queue priority design to something which
makes a little bit more sense.

Previously, a song that had already been played would only be
re-enqueued if its priority had just been raised above the current
song's.  This means that if it was already above, it was not
re-enqueued.  That is a surprising behavior, because users expect a
song to be played when its priority is raised.

Now the song is always re-enqueued if its priority is raised (and
above the current song's - no matter if it has already been above
before).

 https://bugs.musicpd.org/view.php?id=4592
2016-12-09 13:02:26 +01:00
Max Kellermann
e3237f057d systemd: more paranoid security settings 2016-12-09 10:41:44 +01:00
Florian Schlichting
54d5d9d1cc systemd: protect /usr when running under systemd 2016-12-09 10:41:44 +01:00
Clément B
31d9aebf0b systemd: also disable mpd.socket when disabling mpd.service
e.g. when running 'update-rc.d mpd disable'
2016-12-09 10:41:43 +01:00
Max Kellermann
301abac0c1 LogInit: initialize out_fd properly to avoid closing stdin 2016-12-04 20:13:37 +01:00
Max Kellermann
7019f6bea4 decoder/pcm: round buffer size down to nearest frame size
https://bugs.musicpd.org/view.php?id=4599
2016-11-17 21:58:27 +01:00
Wieland Hoffmann
8bde47280a doc/protocol: Turn the link to the UTF-8 FAQ into a ulink element 2016-11-16 21:25:45 +01:00
Wieland Hoffmann
521c6da830 doc/protocol: UTF=8 → UTF-8 2016-11-16 21:25:40 +01:00
Max Kellermann
5c3e55b5b1 {input,output}/alsa: fix gcc 7.0 -Wimplicit-fallthrough 2016-11-16 19:50:38 +01:00
Max Kellermann
1859ba5ec8 output/winmm: 8 bit playback is not supported
Everything must be S16.
2016-11-07 08:53:57 +01:00
Max Kellermann
ee026386e5 storage/Composite: avoid setting the error twice
If an error has already been set by f.directory->storage->GetInfo(),
don't set it again.
2016-10-27 21:26:55 +02:00
Max Kellermann
49c04ccfc7 decoder/sidplay: fix playback speed with libsidplayfp
https://bugs.musicpd.org/view.php?id=4577
2016-10-27 20:25:19 +02:00
Max Kellermann
11ba44870b decoder/sidplay: simplify seek loop 2016-10-27 20:25:12 +02:00
Max Kellermann
f9a64d24bf storage/Composite: eliminate the second FindStorage() overload
It was used in a wrong way, which did not deal with errors
consistently.  And if that's wrong, there is no need for FindStorage()
at all - let's remove it and the confusion around it.
2016-10-27 19:55:20 +02:00
Max Kellermann
e1a8dcfcc8 storage/Composite: add FindStorage() API documentation 2016-10-27 19:55:08 +02:00
Max Kellermann
1ee0e29974 storage/Composite: fix documentation typo 2016-10-27 17:12:24 +02:00
Max Kellermann
77a9940461 decoder/ffmpeg: ignore empty packets
An empty packet would be a command for avcodec_send_packet() to
finalize the codec.

Fixes https://bugs.musicpd.org/view.php?id=4588
2016-10-26 18:29:07 +02:00
Max Kellermann
9c1c180ae0 tag/Item: declare value[] to have only one element
By declaring the variable-length array to have a nominal size of 1,
struct TagPoolSlot shrinks from 24 bytes to 16 bytes, because "ref"
and "item" now both fit in one machine word.
2016-10-26 18:26:01 +02:00
Max Kellermann
06682bd2a9 tag/Item: remove "packed" attribute, add static_assert on alignment instead
The "packed" attribute triggers a clang 4.0 warning, and it's not
necessary.  All we want is correct allocation of this
dynamically-sized struct.
2016-10-26 18:24:16 +02:00
dennisschagt
7c251fe190 Fix comment in mpdconf.example ("can setting can" -> "setting can") 2016-10-20 15:02:36 +02:00
Max Kellermann
a85455fb3f configure.ac: prepare for 0.19.20 2016-09-27 22:04:46 +02:00
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
Max Kellermann
f28c746b6b release v0.19.17 2016-07-09 00:40:57 +02:00
Max Kellermann
ab95027fc6 decoder/flac: suppress warning at end of stream
This is required if a stream ands without another chained FLAC file.
2016-07-08 23:19:47 +02:00
Max Kellermann
ed3bc4ab63 decoder/flac: move code to FlacInitAndDecode() 2016-07-08 23:03:49 +02:00
Max Kellermann
68064f1aa6 decoder/flac: move duplicate code to flac_data::Initialize() 2016-07-08 22:44:23 +02:00
Max Kellermann
475ac76a5f decoder/flac: late "total_frames" initialization 2016-07-08 22:43:31 +02:00
Max Kellermann
79d4f8674c decoder/flac: remove "duration" parameter from flac_decoder_initialize()
It's always 0.
2016-07-08 22:41:19 +02:00
Max Kellermann
e42eed4d4c decoder/flac: remove pointless check 2016-07-08 22:41:19 +02:00
Max Kellermann
4a7042e847 decoder/flac: handle unknown duration correctly
If the duration is unknown, pass SignedSongTime::Negative(), as
documented for decoder_initialized().
2016-07-08 22:33:49 +02:00
Max Kellermann
7f36923eb4 decoder/flac: pass SignedSongTime to decoder_initialized() 2016-07-08 22:32:23 +02:00
Max Kellermann
2ca8d69126 decoder/flac: document flac_data::position 2016-07-08 22:20:16 +02:00
Max Kellermann
70367d70c8 decoder/flac: remove obsolete sub-song support
This is obsolete because it has been moved to the MPD core.
2016-07-08 21:59:30 +02:00
Max Kellermann
e6389ff5a1 client/ClientRead: call Break() before Close()
Referencing the attribute "partition" is illegal after Close(),
because Close() deletes "this".
2016-07-07 13:54:04 +02:00
Max Kellermann
b46cf57d98 event/BufferedSocket: OnSocketReady() returns true after close
Fixes use-after-free bug (https://bugs.musicpd.org/view.php?id=4548).
2016-07-07 13:52:20 +02:00
Max Kellermann
6f59d71e07 decoder/API: check initial_seek_running in _check_cancel_read()
The "seeking" flag is not set for the initial seek, and so
decoder_read() could be canceled when another SEEK was emitted during
initial seek.

This fixes several seek problems, for example the one reported for the
FLAC decoder plugin:

 https://bugs.musicpd.org/view.php?id=4552
2016-07-06 15:46:04 +02:00
Max Kellermann
f9130f42a2 decoder/flac: try to recover from seek error()
libFLAC API documentation suggests that FLAC__stream_decoder_flush()
should be called to recover from FLAC__STREAM_DECODER_SEEK_ERROR.
2016-07-05 19:29:56 +02:00
Max Kellermann
faf2eeaa99 decoder/flac: evaluate all possible FLAC__stream_decoder_get_state() values
Stop after all fatal errors.  This fixes assertion failures in
libFLAC.
2016-07-05 19:27:40 +02:00
Max Kellermann
1c7de0b4ac output/shout: remove pointless memset() call 2016-07-05 18:02:35 +02:00
Max Kellermann
58487e484f filter/route: use PcmSilence() 2016-07-05 18:01:29 +02:00
Max Kellermann
104075f3e0 PlayerThread: use PcmSilence() in SendSilence()
No change for regular PCM, but DSD uses 0x69 now.
2016-07-05 18:01:29 +02:00
Max Kellermann
b8097eaf2e pcm/Volume: move silence pattern to Silence.cxx 2016-07-05 17:52:53 +02:00
Max Kellermann
5eb0cbc887 PlayerThread: make chunk allocation error non-fatal in SendSilence()
Fixes abort after seeking on fast machines.
2016-07-05 17:44:45 +02:00
Max Kellermann
ba8e579e9b pcm/Volume: use 0x69 to generate DSD silence 2016-07-01 21:22:21 +02:00
Max Kellermann
072e39c9cf filter/ReplayGain: skip PcmVolume if a mixer is set
Previously, volume was applied twice: once by PcmVolume, and again by
the hardware mixer.
2016-07-01 21:17:52 +02:00
Max Kellermann
8dc3f3b21a configure.ac: prepare for 0.19.17 2016-07-01 21:16:14 +02:00
Max Kellermann
faf0c950fe release v0.19.16 2016-06-13 18:59:07 +02:00
Max Kellermann
4ecd325371 decoder/flac: log seek errors 2016-06-13 18:37:45 +02:00
Max Kellermann
5771d67202 player/Thread: cancel outputs before seeking
.. instead of doing it after seeking.  After seeking, the command had
no effect, because CheckDecoderStartup() waits for all outputs to
finish.  This caused a very long delay while seeking and switching
songs (https://bugs.musicpd.org/view.php?id=4534).
2016-06-13 09:13:56 +02:00
Max Kellermann
75c8aecffa NEWS: add missing lines 2016-05-11 17:09:46 +02:00
Max Kellermann
aa5d05eaa4 configure.ac: don't suppress GLib warnings by changing -I to -isystem
This is a kludge which may break system include path order, see
https://bugs.musicpd.org/view.php?id=4524
2016-05-02 22:05:21 +02:00
Max Kellermann
15735552f4 Makefile.am: include doc/include/tags.xml in tarball
See https://bugs.musicpd.org/view.php?id=4523
2016-05-02 09:03:54 +02:00
Max Kellermann
d6d9dc9d95 Makefile.am: include scripts/*.rb in tarball
Fix out-of-tree build by prepending $(srcdir)/, and change *.sh to
*.rb.
2016-05-02 08:58:17 +02:00
Max Kellermann
dc57966dc3 configure.ac: prepare for 0.19.16 2016-05-02 08:57:53 +02:00
Max Kellermann
04ed50fb0f release v0.19.15 2016-04-30 14:21:52 +02:00
Max Kellermann
c9553411bb encoder/wave: add constant WAVE_FORMAT_PCM 2016-04-30 13:57:09 +02:00
Max Kellermann
62221adf55 encoder/wave: fix indent 2016-04-30 13:45:52 +02:00
Max Kellermann
a6bf4746c6 test/test_byte_reverse: use gcc_alignas() for gcc<4.8 compatibility 2016-04-30 13:41:24 +02:00
Michael Cree
72637d00e8 Bug#822848: mpd FTBFS on Alpha; misaligned arrays in the test suite
Source: mpd
Version: 0.19.14-2
Severity: important
Justification: fails to build form source (but built in the past)
Tags: patch
User: debian-alpha@lists.debian.org
Usertags: alpha

mpd FTBFS on Alpha with a failure in the test suite [1]:

FAIL: test/test_byte_reverse
============================

.F...

!!!FAILURES!!!
Test Results:
Run:  4   Failures: 1   Errors: 0

1) test: ByteReverseTest::TestByteReverse2 (F) line: 58 test/test_byte_reverse.cxx
assertion failed
- Expression: strcmp(result, (const char *)dest) == 0

This occurs because the test suite (in test/test_byte_reversal.cxx)
allocates static char arrays and passes the char arrays to functions
whose respective arguments were declared to be uint16_t *, etc., in
the main code.

This is in the realm of undefined behaviour on architectures with
strict memory alignment requirements.  Although the test only fails
on Alpha (because Alpha has a particular CPU load instruction that
gcc likes to use to add bugs ..., ahem,  optimise the code on the
assumption of alignment) it is potentially a latent bug for other
architectures with strict alignment requirements.

Since the code is compiled with the c++11 standard I attach a patch
that modifies the test suite to align the non-compliant strings with
the alignas() attribute.  The test suite now passes on Alpha with
that patch.

Cheers
Michael

[1] https://buildd.debian.org/status/fetch.php?pkg=mpd&arch=alpha&ver=0.19.14-2&stamp=1461542099
2016-04-28 13:29:41 +02:00
Florian Schlichting
27d4b15925 DecoderBuffer: add missing include
> In file included from src/decoder/DecoderBuffer.cxx:21:0:
> src/decoder/DecoderBuffer.hxx:41:20: error: 'uint8_t' was not declared in this scope
>   DynamicFifoBuffer<uint8_t> buffer;
>                     ^
> src/decoder/DecoderBuffer.hxx:41:27: error: template argument 1 is invalid
>   DynamicFifoBuffer<uint8_t> buffer;
>                            ^
> src/decoder/DecoderBuffer.hxx: In member function 'void DecoderBuffer::Clear()':
> src/decoder/DecoderBuffer.hxx:61:10: error: request for member 'Clear' in '((DecoderBuffer*)this)->DecoderBuffer::buffer', which is of non-class type 'int'
>    buffer.Clear();
>           ^
> src/decoder/DecoderBuffer.hxx: In member function 'size_t DecoderBuffer::GetAvailable() const':
> src/decoder/DecoderBuffer.hxx:78:17: error: request for member 'GetAvailable' in '((const DecoderBuffer*)this)->DecoderBuffer::buffer', which is of non-class type 'const int'
>    return buffer.GetAvailable();
>                  ^
> src/decoder/DecoderBuffer.hxx: In member function 'ConstBuffer<void> DecoderBuffer::Read() const':
> src/decoder/DecoderBuffer.hxx:87:19: error: request for member 'Read' in '((const DecoderBuffer*)this)->DecoderBuffer::buffer', which is of non-class type 'const int'
>    auto r = buffer.Read();
>                    ^
> src/decoder/DecoderBuffer.hxx:88:27: error: could not convert '{<expression error>, <expression error>}' from '<brace-enclosed initializer list>' to 'ConstBuffer<void>'
>    return { r.data, r.size };
>                            ^
> src/decoder/DecoderBuffer.hxx: In member function 'void DecoderBuffer::Consume(size_t)':
> src/decoder/DecoderBuffer.hxx:105:10: error: request for member 'Consume' in '((DecoderBuffer*)this)->DecoderBuffer::buffer', which is of non-class type 'int'
>    buffer.Consume(nbytes);
>           ^

This seems to be caused by a lacking include, fixed by the below patch.

I'm unsure what made this appear now, though, compiler and toolchain
libraries seem to be the same upstream versions that built 0.19.14-1
just fine in late March.
2016-04-25 08:30:27 +02:00
Max Kellermann
7a77767e66 doc/mpd.conf.5: move metadata_to_use to the user manual 2016-04-22 10:48:12 +02:00
Max Kellermann
1b26621860 doc/{user,protocol}: add a list of supported tags
A complete list which replaces the incomplete list in the mpd.conf
manpage.
2016-04-22 10:48:12 +02:00
Max Kellermann
3db5f4d0aa doc/mpd.conf.5: remove obsolete metadata_to_use sentence 2016-04-22 10:38:24 +02:00
Max Kellermann
b2a6e327bf doc: migrate to DocBook 4.5 2016-04-22 10:04:29 +02:00
Florian Schlichting
9aec5fe907 doc/user: fix typo 2016-04-22 09:25:17 +02:00
Max Kellermann
c731a82b71 decoder/opus: limit the number of packets in _scan_stream() 2016-04-19 13:08:07 +02:00
Max Kellermann
e6fad97edc decoder/opus: support bigger OpusTags packets
Required for OpusTags packets which contain artwork.

See https://bugs.musicpd.org/view.php?id=4520
2016-04-19 13:05:42 +02:00
Max Kellermann
70495aada1 decoder/ffmpeg: don't copy the AVPacket in ffmpeg_send_packet()
Reduce some overhead.  It is not necessary to copy the object.
2016-04-13 09:04:51 +02:00
Max Kellermann
f243f615ef decoder/ffmpeg: convert pointers to references 2016-04-13 09:01:54 +02:00
Max Kellermann
807c72b2f1 decoder/ffmpeg: use av_packet_unref() instead of av_free_packet()
av_free_packet() was deprecated in FFmpeg 3.0.
2016-04-12 21:15:05 +02:00
Max Kellermann
74dbaade6f decoder/Thread: use "ffmpeg" as fallback instead of "mad"
Adds support for stream codecs which havn't been explicitly listed in
ffmpeg_mime_types.
2016-03-30 00:58:48 +02:00
Max Kellermann
53677172f2 notify: use "constexpr" only with glibc
The Mutex and Cond constructors are only "constexpr" with glibc, and
this is what this #ifdef is about.

Backport of commit 459a812a

See http://bugs.musicpd.org/view.php?id=4511
2016-03-30 00:31:01 +02:00
Max Kellermann
bef0ccf42a configure.ac: prepare for 0.19.15 2016-03-30 00:30:39 +02:00
Max Kellermann
ff35aa07dc release v0.19.14 2016-03-18 18:26:58 +01:00
Max Kellermann
a3afd5178c tag/TagPool: optimize _dup_item()
When a reference counter is at its limit, don't allocate a new
TagPoolSlot - that would result in many TagPoolSlot instances with
ref==1.  This in turn would make the linked list very very large,
which means quadratic runtime for many operations.
2016-03-14 13:08:04 +01:00
Max Kellermann
f1285a6dfd tag/TagPool: add constexpr MAX_REF 2016-03-14 08:07:22 +01:00
Max Kellermann
cf7c1afb93 tag/TagPool: use prime number for NUM_SLOTS 2016-03-14 08:04:51 +01:00
Max Kellermann
e140a28073 archive/iso9660: check path buffer bounds 2016-03-07 14:21:01 +01:00
Max Kellermann
de61c3b962 archive/iso9660: use a single path buffer for Visit()
Avoid wasting 4 kB stack per directory level.
2016-03-07 14:01:52 +01:00
Max Kellermann
c46fc4531b archive/iso9660: move the "." and ".." checks up 2016-03-07 14:01:40 +01:00
Max Kellermann
065a9ed10f archive/iso9660: add local variable "filename" 2016-03-07 13:57:07 +01:00
Max Kellermann
e44c0254f7 archive/iso9660: make variables more local 2016-03-07 13:15:07 +01:00
Max Kellermann
13f9f0315f util/HugeAllocator: fix division by zero due to inverted check
There were two ways this could fail:

1. division by zero when sysconf(_SC_PAGESIZE)==0

2. mmap() failure because the size parameter is not aligned to page
   size

Neither ever happened: sysconf() never fails, and the only caller
passes a size that is already aligned.  Phew.
2016-03-06 23:53:41 +01:00
Max Kellermann
1532ffe215 protocol/ArgParser: fix range check
The old check

 unsigned(value) > std::numeric_limits<unsigned>::max()

.. cannot ever fail.
2016-03-06 23:41:08 +01:00
Max Kellermann
b24cbc68ba decoder/dsdiff: fix off-by-one buffer overflow 2016-03-06 23:28:29 +01:00
Max Kellermann
976fdd76c1 decoder/opus: limit tag size to 64 kB 2016-03-06 23:26:48 +01:00
Max Kellermann
bbda335e02 mixer/pulse: fix integer division rounding 2016-03-06 23:23:30 +01:00
Max Kellermann
d2dd6f7c70 thread/Posix{Mutex,Cond}: use "constexpr" only with glibc
Apparently all other C libraries are not compatible with "constexpr".
Those which are not will get a performance penalty, but at least they
work at all.
2016-03-01 21:23:59 +01:00
Max Kellermann
e9a544fa98 configure.ac: prepare for 0.19.14 2016-03-01 21:22:42 +01:00
91 changed files with 2449 additions and 979 deletions
.gitignoreINSTALLMakefile.amNEWSconfigure.ac
doc
src
systemd
test

2
.gitignore vendored

@@ -35,7 +35,7 @@ tags
/mkinstalldirs
/build
/src/mpd
/systemd/mpd.service
/systemd/system/mpd.service
/stamp-h1
/src/dsd2pcm/dsd2pcm

@@ -12,7 +12,7 @@ install MPD. If more information is desired, read the user manual:
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/
Any other C++11 compliant compiler should also work.

@@ -359,6 +359,7 @@ libutil_a_SOURCES = \
src/util/Clamp.hxx \
src/util/Alloc.cxx src/util/Alloc.hxx \
src/util/VarSize.hxx \
src/util/ScopeExit.hxx \
src/util/Error.cxx src/util/Error.hxx \
src/util/Domain.hxx \
src/util/ReusableArray.hxx \
@@ -462,11 +463,13 @@ ICU_LDADD = libicu.a $(ICU_LIBS)
libpcm_a_SOURCES = \
src/pcm/Domain.cxx src/pcm/Domain.hxx \
src/pcm/Traits.hxx \
src/pcm/Interleave.cxx src/pcm/Interleave.hxx \
src/pcm/PcmBuffer.cxx src/pcm/PcmBuffer.hxx \
src/pcm/PcmExport.cxx src/pcm/PcmExport.hxx \
src/pcm/PcmConvert.cxx src/pcm/PcmConvert.hxx \
src/pcm/PcmDop.cxx src/pcm/PcmDop.hxx \
src/pcm/Volume.cxx src/pcm/Volume.hxx \
src/pcm/Silence.cxx src/pcm/Silence.hxx \
src/pcm/PcmMix.cxx src/pcm/PcmMix.hxx \
src/pcm/PcmChannels.cxx src/pcm/PcmChannels.hxx \
src/pcm/PcmPack.cxx src/pcm/PcmPack.hxx \
@@ -527,7 +530,7 @@ libfs_a_SOURCES = \
src/fs/Traits.cxx src/fs/Traits.hxx \
src/fs/Config.cxx src/fs/Config.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/FileSystem.cxx src/fs/FileSystem.hxx \
src/fs/StandardDirectory.cxx src/fs/StandardDirectory.hxx \
@@ -798,6 +801,11 @@ endif
if HAVE_FFMPEG
noinst_LIBRARIES += libffmpeg.a
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/Domain.cxx src/lib/ffmpeg/Domain.hxx
libffmpeg_a_CPPFLAGS = $(AM_CPPFLAGS) \
@@ -979,6 +987,8 @@ endif
if HAVE_FFMPEG
libdecoder_a_SOURCES += \
src/decoder/plugins/FfmpegIo.cxx \
src/decoder/plugins/FfmpegIo.hxx \
src/decoder/plugins/FfmpegMetaData.cxx \
src/decoder/plugins/FfmpegMetaData.hxx \
src/decoder/plugins/FfmpegDecoderPlugin.cxx \
@@ -1439,8 +1449,13 @@ FILTER_LIBS = \
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
systemd/mpd.socket \
systemd/mpd.service
systemd/system/mpd.socket \
systemd/system/mpd.service
endif
if HAVE_SYSTEMD_USER
systemduserunit_DATA = \
systemd/user/mpd.service
endif
@@ -1866,6 +1881,7 @@ test_run_convert_SOURCES = test/run_convert.cxx \
src/AudioParser.cxx
test_run_convert_LDADD = \
$(PCM_LIBS) \
libconf.a \
libutil.a \
$(GLIB_LIBS)
@@ -2109,7 +2125,9 @@ developer_DATA = $(wildcard doc/developer/*.html)
DOCBOOK_HTML = $(patsubst %.xml,%/index.html,$(DOCBOOK_FILES))
$(DOCBOOK_HTML): %/index.html: %.xml
DOCBOOK_INCLUDES = $(wildcard $(srcdir)/doc/include/*.xml)
$(DOCBOOK_HTML): %/index.html: %.xml $(DOCBOOK_INCLUDES)
$(XMLTO) -o $(@D) --stringparam chunker.output.encoding=utf-8 html --stringparam use.id.as.filename=1 $<
doc/api/html/index.html: doc/doxygen.conf
@@ -2150,9 +2168,10 @@ EXTRA_DIST = $(doc_DATA) autogen.sh \
test/test_archive_bzip2.sh \
test/test_archive_iso9660.sh \
test/test_archive_zzip.sh \
$(wildcard scripts/*.sh) \
$(wildcard $(srcdir)/scripts/*.rb) \
$(man_MANS) $(DOCBOOK_FILES) doc/mpdconf.example doc/doxygen.conf \
systemd/mpd.socket \
$(wildcard $(srcdir)/doc/include/*.xml) \
systemd/system/mpd.socket \
android/AndroidManifest.xml \
android/build.py \
android/custom_rules.xml \

75
NEWS

@@ -1,3 +1,78 @@
ver 0.19.21 (2016/12/13)
* decoder
- ffmpeg: fix crash bug
* fix unit test failure after recent "setprio" change
* systemd: add user unit
ver 0.19.20 (2016/12/09)
* protocol
- "setprio" re-enqueues old song if priority has been raised
* decoder
- ffmpeg: ignore empty packets
- pcm: fix corruption bug with partial frames (after short read)
- sidplay: fix playback speed with libsidplayfp
* output
- winmm: fix 8 bit playback
* fix gcc 7.0 -Wimplicit-fallthrough
* systemd: paranoid security settings
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)
* decoder
- flac: fix assertion failure while seeking
- flac: fix stream duration indicator
- fix seek problems in several plugins
* fix spurious seek error "Failed to allocate silence buffer"
* replay gain: fix "replay_gain_handler mixer" setting
* DSD: use 0x69 as silence pattern
* fix use-after-free bug on "close" and "kill"
ver 0.19.16 (2016/06/13)
* faster seeking
* fix system include path order
* add missing DocBook file to tarball
ver 0.19.15 (2016/04/30)
* decoder
- ffmpeg: support FFmpeg 3.0
- ffmpeg: use as fallback instead of "mad" if no plugin matches
- opus: support bigger OpusTags packets
* fix more build failures on non-glibc builds due to constexpr Mutex
* fix build failure due to missing include
* fix unit test on Alpha
ver 0.19.14 (2016/03/18)
* decoder
- dsdiff: fix off-by-one buffer overflow
- opus: limit tag size to 64 kB
* archive
- iso9660: fix buffer overflow
* fix quadratic runtime bug in the tag pool
* fix build failures on non-glibc builds due to constexpr Mutex
ver 0.19.13 (2016/02/23)
* tags
- aiff, riff: fix ID3 chunk padding

@@ -1,10 +1,10 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.19.13, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.19.21, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
VERSION_MINOR=19
VERSION_REVISION=13
VERSION_REVISION=21
VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx])
@@ -52,6 +52,22 @@ if test "x$with_systemdsystemunitdir" != xno; then
fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ])
AC_ARG_WITH([systemduserunitdir],
AS_HELP_STRING([--with-systemduserunitdir=DIR], [Directory for systemd service files]),
[], [with_systemduserunitdir=no])
if test "x$with_systemduserunitdir" = xyes; then
AC_MSG_CHECKING(for systemd)
with_systemduserunitdir=$($PKG_CONFIG --variable=systemduserunitdir systemd)
if test -z "$with_systemduserunitdir"; then
AC_MSG_ERROR([Failed to detect systemd])
fi
AC_MSG_RESULT([$with_systemduserunitdir])
fi
if test "x$with_systemduserunitdir" != xno; then
AC_SUBST([systemduserunitdir], [$with_systemduserunitdir])
fi
AM_CONDITIONAL(HAVE_SYSTEMD_USER, [test -n "$with_systemduserunitdir" -a "x$with_systemduserunitdir" != xno ])
dnl ---------------------------------------------------------------------------
dnl Declare Variables
dnl ---------------------------------------------------------------------------
@@ -542,8 +558,6 @@ AC_ARG_ENABLE(sidplay,
AS_HELP_STRING([--enable-sidplay],
[enable C64 SID support via libsidplay2]),,
enable_sidplay=auto)
MPD_DEPENDS([enable_sidplay], [enable_glib],
[Cannot use --enable-sidplay with --disable-glib])
AC_ARG_ENABLE(shine-encoder,
AS_HELP_STRING([--enable-shine-encoder],
@@ -703,12 +717,6 @@ AC_ARG_ENABLE(glib,
if test x$enable_glib = xyes; then
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28 gthread-2.0],,
[AC_MSG_ERROR([GLib 2.28 is required])])
if test x$GCC = xyes; then
# suppress warnings in the GLib headers
GLIB_CFLAGS=`echo $GLIB_CFLAGS |sed -e 's,-I/,-isystem /,g'`
fi
AC_DEFINE(HAVE_GLIB, 1, [Define if GLib is used])
fi
AM_CONDITIONAL(HAVE_GLIB, test x$enable_glib = xyes)
@@ -1341,31 +1349,36 @@ AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enab
dnl --------------------------------- sidplay ---------------------------------
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],[])
dnl Check for libsidplayfp first
PKG_CHECK_MODULES([SIDPLAY], [libsidplayfp libsidutils],
[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],
[libsidplay2 not found])
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],
[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])
[libresid-builder not found])
fi
if test x$enable_sidplay = xyes; then
AC_SUBST(SIDPLAY_LIBS,"-lsidplay2 -lresid-builder -lsidutils")
AC_SUBST(SIDPLAY_CFLAGS,)
SIDPLAY_LIBS="$SIDPLAY_LIBS -lresid-builder"
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
AM_CONDITIONAL(ENABLE_SIDPLAY, test x$enable_sidplay = xyes)
@@ -1908,7 +1921,8 @@ dnl Generate files
dnl ---------------------------------------------------------------------------
AC_CONFIG_FILES(Makefile)
AC_CONFIG_FILES(doc/doxygen.conf)
AC_CONFIG_FILES(systemd/mpd.service)
AC_CONFIG_FILES(systemd/system/mpd.service)
AC_CONFIG_FILES(systemd/user/mpd.service)
AC_OUTPUT
echo 'MPD is ready for compilation, type "make" to begin.'

@@ -1,6 +1,7 @@
<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"docbook/dtd/xml/4.2/docbookx.dtd">
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book>
<title>The Music Player Daemon - Developer's Manual</title>
@@ -40,7 +41,7 @@
<listitem>
<para>
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
</para>
</listitem>

154
doc/include/tags.xml Normal file

@@ -0,0 +1,154 @@
<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE itemizedlist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<itemizedlist>
<listitem>
<para>
<varname>artist</varname>: the artist name. Its meaning is not
well-defined; see <varname>composer</varname> and
<varname>performer</varname> for more specific tags.
</para>
</listitem>
<listitem>
<para>
<varname>artistsort</varname>: same as
<varname>artist</varname>, but for sorting. This usually omits
prefixes such as "The".
</para>
</listitem>
<listitem>
<para>
<varname>album</varname>: the album name.
</para>
</listitem>
<listitem>
<para>
<varname>albumsort</varname>: same as <varname>album</varname>,
but for sorting.
</para>
</listitem>
<listitem>
<para>
<varname>albumartist</varname>: on multi-artist albums, this is
the artist name which shall be used for the whole album. The
exact meaning of this tag is not well-defined.
</para>
</listitem>
<listitem>
<para>
<varname>albumartistsort</varname>: same as
<varname>albumartist</varname>, but for sorting.
</para>
</listitem>
<listitem>
<para>
<varname>title</varname>: the song title.
</para>
</listitem>
<listitem>
<para>
<varname>track</varname>: the track number within the album.
</para>
</listitem>
<listitem>
<para>
<varname>name</varname>: a name for this song. This is not the
song title. The exact meaning of this tag is not well-defined.
It is often used by badly configured internet radio stations
with broken tags to squeeze both the artist name and the song
title in one tag.
</para>
</listitem>
<listitem>
<para>
<varname>genre</varname>: the music genre.
</para>
</listitem>
<listitem>
<para>
<varname>date</varname>: the song's release date. This is
usually a 4-digit year.
</para>
</listitem>
<listitem>
<para>
<varname>composer</varname>: the artist who composed the song.
</para>
</listitem>
<listitem>
<para>
<varname>performer</varname>: the artist who performed the song.
</para>
</listitem>
<listitem>
<para>
<varname>comment</varname>: a human-readable comment about this
song. The exact meaning of this tag is not well-defined.
</para>
</listitem>
<listitem>
<para>
<varname>disc</varname>: the disc number in a multi-disc album.
</para>
</listitem>
<listitem>
<para>
<varname>musicbrainz_artistid</varname>: the artist id in the
<ulink
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
database.
</para>
</listitem>
<listitem>
<para>
<varname>musicbrainz_albumid</varname>: the album id in the
<ulink
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
database.
</para>
</listitem>
<listitem>
<para>
<varname>musicbrainz_albumartistid</varname>: the album artist
id in the <ulink
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
database.
</para>
</listitem>
<listitem>
<para>
<varname>musicbrainz_trackid</varname>: the track id in the
<ulink
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
database.
</para>
</listitem>
<listitem>
<para>
<varname>musicbrainz_releasetrackid</varname>: the release track
id in the <ulink
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
database.
</para>
</listitem>
</itemizedlist>

@@ -174,18 +174,6 @@ MP3 playback.
This specifies whether relative or absolute paths for song filenames are used
when saving playlists. The default is "no".
.TP
.B metadata_to_use <tags>
This specifies the tag types that will be scanned for and made available to
clients. Note that you must recreate (not update) your database for changes to
this parameter to take effect. Possible values are artist, album, title,
track, name, genre, date, composer, performer, comment, disc,
musicbrainz_artistid, musicbrainz_albumid, musicbrainz_albumartistid,
musicbrainz_releasetrackid, musicbrainz_trackid. Multiple tags may be specified
as a comma separated list.
An example value is "artist,album,title,track". The special value "none" may
be used alone to disable all metadata. The default is to use all known tag
types except for comments and those starting with "musicbrainz".
.TP
.B auto_update <yes or no>
This specifies the whether to support automatic update of music database when
files are changed in music_directory. The default is to disable autoupdate

@@ -115,7 +115,7 @@
#
# This setting defines a list of tag types that will be extracted during the
# audio file discovery process. The complete list of possible values can be
# found in the mpd.conf man page.
# found in the user manual.
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
#
# This setting enables automatic update of MPD's database when files in
@@ -164,7 +164,7 @@
# Permissions #################################################################
#
# If this setting is set, MPD will require password authorization. The password
# can setting can be specified multiple times for different password profiles.
# setting can be specified multiple times for different password profiles.
#
#password "password@read,add,control,admin"
#
@@ -231,7 +231,7 @@ input {
#
#audio_output {
# type "shout"
# encoding "ogg" # optional
# encoder "vorbis" # optional
# name "My Shout Stream"
# host "localhost"
# port "8000"

@@ -1,6 +1,7 @@
<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"docbook/dtd/xml/4.2/docbookx.dtd">
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book>
<title>The Music Player Daemon protocol</title>
@@ -65,8 +66,8 @@
<function>strcpy</function> just fine with UTF-8 encoded
strings. For example: <returnvalue>OK</returnvalue> encoded in
UTF-8 is simply <returnvalue>OK</returnvalue>. For more
information on UTF=8:
http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)
information on UTF-8:
<ulink url="http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8"/>)
</para>
</section>
@@ -201,6 +202,25 @@
omitted, then the maximum possible value is assumed.
</para>
</section>
<section id="tags">
<title>Tags</title>
<para>
The following tags are supported by
<application>MPD</application>:
</para>
<xi:include href="include/tags.xml"
xmlns:xi="http://www.w3.org/2001/XInclude"/>
<para>
There can be multiple values for some of these tags. For
example, <application>MPD</application> may return multiple
lines with a <varname>performer</varname> tag. A tag value is
a UTF-8 string.
</para>
</section>
</chapter>
<chapter id="recipes">

@@ -1,6 +1,7 @@
<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"docbook/dtd/xml/4.2/docbookx.dtd">
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book>
<title>The Music Player Daemon - User's Manual</title>
@@ -16,7 +17,7 @@
<application>MPD</application> (Music Player Daemon) is, as the
name suggests, a server software allowing you to remotely play
your music, handle playlists, deliver music (HTTP streams with
various sub-protocols) and organizze playlists.
various sub-protocols) and organize playlists.
</para>
<para>
@@ -150,7 +151,7 @@ apt-get install g++ \
<application>systemd</application> unit files: a "service"
unit and a "socket" unit. These will only be installed when
<application>MPD</application> was configured with
<parameter>--with-systemdsystemunitdir=/lib/systemd</parameter>.
<parameter>--with-systemdsystemunitdir=/lib/systemd/system</parameter>.
</para>
<para>
@@ -166,6 +167,33 @@ systemctl start mpd.socket</programlisting>
<varname>port</varname> settings.
</para>
</section>
<section id="systemd_user">
<title><filename>systemd</filename> user unit</title>
<para>
You can launch <application>MPD</application> as a
<filename>systemd</filename> user unit. The service file will
only be installed when <application>MPD</application> was
configured with
<parameter>--with-systemduserunitdir=/usr/lib/systemd/user</parameter>
or
<parameter>--with-systemduserunitdir=$HOME/.local/share/systemd/user</parameter>.
</para>
<para>
Once the user unit is installed, you can start and stop
<application>MPD</application> like any other service:
</para>
<programlisting>systemctl --user start mpd</programlisting>
<para>
To auto-start <application>MPD</application> upon login, type:
</para>
<programlisting>systemctl --user enable mpd</programlisting>
</section>
</chapter>
<chapter id="config">
@@ -944,6 +972,33 @@ systemctl start mpd.socket</programlisting>
<section id="config_other">
<title>Other Settings</title>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>metadata_to_use</varname>
<parameter>TAG1,TAG2,...</parameter>
</entry>
<entry>
Use only the specified tags, and ignore the others.
This setting can reduce the database size and
<application>MPD</application>'s memory usage by
omitting unused tags. By default, all tags but
<varname>comment</varname> are enabled. The special
value "none" disables all tags.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<section>
<title>The State File</title>
@@ -1297,6 +1352,19 @@ database {
</para>
</section>
<section id="tags">
<title>Metadata</title>
<para>
When scanning or playing a song,
<application>MPD</application> parses its metadata. The
following tags are supported:
</para>
<xi:include href="include/tags.xml"
xmlns:xi="http://www.w3.org/2001/XInclude"/>
</section>
<section id="queue">
<title>The queue</title>

@@ -26,7 +26,7 @@
#include <utility>
#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 */
#define chrono_constexpr gcc_pure
#else

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

@@ -51,7 +51,7 @@ static constexpr Domain log_domain("log");
#ifndef ANDROID
static int out_fd;
static int out_fd = -1;
static AllocatedPath out_path = AllocatedPath::Null();
static void redirect_logs(int fd)

@@ -25,6 +25,7 @@
#include "MusicPipe.hxx"
#include "MusicBuffer.hxx"
#include "MusicChunk.hxx"
#include "pcm/Silence.hxx"
#include "DetachedSong.hxx"
#include "system/FatalError.hxx"
#include "CrossFade.hxx"
@@ -486,8 +487,12 @@ Player::SendSilence()
MusicChunk *chunk = buffer.Allocate();
if (chunk == nullptr) {
LogError(player_domain, "Failed to allocate silence buffer");
return false;
/* this is non-fatal, because this means that the
decoder has filled to buffer completely meanwhile;
by ignoring the error, we work around this race
condition */
LogDebug(player_domain, "Failed to allocate silence buffer");
return true;
}
#ifndef NDEBUG
@@ -501,7 +506,7 @@ Player::SendSilence()
chunk->time = SignedSongTime::Negative(); /* undefined time stamp */
chunk->length = num_frames * frame_size;
memset(chunk->data, 0, chunk->length);
PcmSilence({chunk->data, chunk->length}, play_audio_format.format);
Error error;
if (!pc.outputs.Play(chunk, error)) {
@@ -518,6 +523,8 @@ Player::SeekDecoder()
{
assert(pc.next_song != nullptr);
pc.outputs.Cancel();
const SongTime start_time = pc.next_song->GetStartTime();
if (!dc.LockIsCurrentSong(*pc.next_song)) {
@@ -583,8 +590,6 @@ Player::SeekDecoder()
/* re-fill the buffer after seeking */
buffering = true;
pc.outputs.Cancel();
return true;
}

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

@@ -66,7 +66,11 @@ public:
return iso9660_iso_seek_read(iso, ptr, start, i_size);
}
void Visit(const char *path, ArchiveVisitor &visitor);
/**
* @param capacity the path buffer size
*/
void Visit(char *path, size_t length, size_t capacity,
ArchiveVisitor &visitor);
virtual void Close() override {
Unref();
@@ -84,32 +88,36 @@ static constexpr Domain iso9660_domain("iso9660");
/* archive open && listing routine */
inline void
Iso9660ArchiveFile::Visit(const char *psz_path, ArchiveVisitor &visitor)
Iso9660ArchiveFile::Visit(char *path, size_t length, size_t capacity,
ArchiveVisitor &visitor)
{
CdioList_t *entlist;
CdioListNode_t *entnode;
iso9660_stat_t *statbuf;
char pathname[4096];
entlist = iso9660_ifs_readdir (iso, psz_path);
auto *entlist = iso9660_ifs_readdir(iso, path);
if (!entlist) {
return;
}
/* Iterate over the list of nodes that iso9660_ifs_readdir gives */
CdioListNode_t *entnode;
_CDIO_LIST_FOREACH (entnode, entlist) {
statbuf = (iso9660_stat_t *) _cdio_list_node_data (entnode);
auto *statbuf = (iso9660_stat_t *)
_cdio_list_node_data(entnode);
const char *filename = statbuf->filename;
if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
continue;
strcpy(pathname, psz_path);
strcat(pathname, statbuf->filename);
size_t filename_length = strlen(filename);
if (length + filename_length + 1 >= capacity)
/* file name is too long */
continue;
memcpy(path + length, filename, filename_length + 1);
size_t new_length = length + filename_length;
if (iso9660_stat_s::_STAT_DIR == statbuf->type ) {
if (strcmp(statbuf->filename, ".") && strcmp(statbuf->filename, "..")) {
strcat(pathname, "/");
Visit(pathname, visitor);
}
memcpy(path + new_length, "/", 2);
Visit(path, new_length + 1, capacity, visitor);
} else {
//remove leading /
visitor.VisitArchiveEntry(pathname + 1);
visitor.VisitArchiveEntry(path + 1);
}
}
_cdio_list_free (entlist, true);
@@ -133,7 +141,8 @@ iso9660_archive_open(Path pathname, Error &error)
void
Iso9660ArchiveFile::Visit(ArchiveVisitor &visitor)
{
Visit("/", visitor);
char path[4096] = "/";
Visit(path, 1, sizeof(path), visitor);
}
/* single archive handling */

@@ -52,8 +52,8 @@ Client::OnSocketInput(void *data, size_t length)
break;
case CommandResult::KILL:
Close();
partition.instance.event_loop->Break();
Close();
return InputResult::CLOSED;
case CommandResult::FINISH:

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

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

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

@@ -301,7 +301,8 @@ decoder_check_cancel_read(const Decoder *decoder)
/* ignore the SEEK command during initialization, the plugin
should handle that after it has initialized successfully */
if (dc.command == DecoderCommand::SEEK &&
(dc.state == DecoderState::START || decoder->seeking))
(dc.state == DecoderState::START || decoder->seeking ||
decoder->initial_seek_running))
return false;
return true;

@@ -25,6 +25,7 @@
#include "util/ConstBuffer.hxx"
#include <stddef.h>
#include <stdint.h>
struct Decoder;
class InputStream;

@@ -98,6 +98,7 @@ decoder_input_stream_open(DecoderControl &dc, const char *uri)
if (!is->Check(error)) {
dc.Unlock();
delete is;
LogError(error);
return nullptr;
@@ -255,7 +256,11 @@ decoder_run_stream_fallback(Decoder &decoder, InputStream &is)
{
const struct DecoderPlugin *plugin;
#ifdef HAVE_FFMPEG
plugin = decoder_plugin_from_name("ffmpeg");
#else
plugin = decoder_plugin_from_name("mad");
#endif
return plugin != nullptr && plugin->stream_decode != nullptr &&
decoder_stream_decode(*plugin, decoder, is);
}

@@ -205,7 +205,7 @@ dsdiff_handle_native_tag(InputStream &is,
if (length == 0 || length > 60)
return;
char string[length];
char string[length + 1];
char *label;
label = string;

File diff suppressed because it is too large Load Diff

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

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

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

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

@@ -33,7 +33,7 @@ flac_data::flac_data(Decoder &_decoder,
InputStream &_input_stream)
:FlacInput(_input_stream, &_decoder),
initialized(false), unsupported(false),
total_frames(0), first_frame(0), next_frame(0), position(0),
position(0),
decoder(_decoder), input_stream(_input_stream)
{
}
@@ -59,6 +59,38 @@ flac_sample_format(unsigned bits_per_sample)
}
}
bool
flac_data::Initialize(unsigned sample_rate, unsigned bits_per_sample,
unsigned channels, FLAC__uint64 total_frames)
{
assert(!initialized);
assert(!unsupported);
::Error error;
if (!audio_format_init_checked(audio_format,
sample_rate,
flac_sample_format(bits_per_sample),
channels, error)) {
LogError(error);
unsupported = true;
return false;
}
frame_size = audio_format.GetFrameSize();
const auto duration = total_frames > 0
? SignedSongTime::FromScale<uint64_t>(total_frames,
audio_format.sample_rate)
: SignedSongTime::Negative();
decoder_initialized(decoder, audio_format,
input_stream.IsSeekable(),
duration);
initialized = true;
return true;
}
static void
flac_got_stream_info(struct flac_data *data,
const FLAC__StreamMetadata_StreamInfo *stream_info)
@@ -66,22 +98,10 @@ flac_got_stream_info(struct flac_data *data,
if (data->initialized || data->unsupported)
return;
Error error;
if (!audio_format_init_checked(data->audio_format,
stream_info->sample_rate,
flac_sample_format(stream_info->bits_per_sample),
stream_info->channels, error)) {
LogError(error);
data->unsupported = true;
return;
}
data->frame_size = data->audio_format.GetFrameSize();
if (data->total_frames == 0)
data->total_frames = stream_info->total_samples;
data->initialized = true;
data->Initialize(stream_info->sample_rate,
stream_info->bits_per_sample,
stream_info->channels,
stream_info->total_samples);
}
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
@@ -125,28 +145,11 @@ flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header)
if (data->unsupported)
return false;
Error error;
if (!audio_format_init_checked(data->audio_format,
header->sample_rate,
flac_sample_format(header->bits_per_sample),
header->channels, error)) {
LogError(error);
data->unsupported = true;
return false;
}
data->frame_size = data->audio_format.GetFrameSize();
const auto duration = SongTime::FromScale<uint64_t>(data->total_frames,
data->audio_format.sample_rate);
decoder_initialized(data->decoder, data->audio_format,
data->input_stream.IsSeekable(),
duration);
data->initialized = true;
return true;
return data->Initialize(header->sample_rate,
header->bits_per_sample,
header->channels,
/* unknown duration */
0);
}
FLAC__StreamDecoderWriteStatus
@@ -155,7 +158,6 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
FLAC__uint64 nbytes)
{
void *buffer;
unsigned bit_rate;
if (!data->initialized && !flac_got_first_frame(data, &frame->header))
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
@@ -167,16 +169,12 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
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;
unsigned bit_rate = nbytes * 8 * frame->header.sample_rate /
(1000 * frame->header.blocksize);
auto cmd = decoder_data(data->decoder, data->input_stream,
buffer, buffer_size,
bit_rate);
data->next_frame += frame->header.blocksize;
switch (cmd) {
case DecoderCommand::NONE:
case DecoderCommand::START:

@@ -55,23 +55,9 @@ struct flac_data : public FlacInput {
AudioFormat 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).
* End of last frame's position within the stream. This is
* used for bit rate calculations.
*/
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;
Decoder &decoder;
@@ -80,6 +66,12 @@ struct flac_data : public FlacInput {
Tag tag;
flac_data(Decoder &decoder, InputStream &input_stream);
/**
* Wrapper for decoder_initialized().
*/
bool Initialize(unsigned sample_rate, unsigned bits_per_sample,
unsigned channels, FLAC__uint64 total_frames);
};
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,

@@ -132,26 +132,16 @@ flac_decoder_new(void)
}
static bool
flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd,
FLAC__uint64 duration)
flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd)
{
data->total_frames = duration;
if (!FLAC__stream_decoder_process_until_end_of_metadata(sd)) {
LogWarning(flac_domain, "problem reading metadata");
if (FLAC__stream_decoder_get_state(sd) != FLAC__STREAM_DECODER_END_OF_STREAM)
LogWarning(flac_domain, "problem reading metadata");
return false;
}
if (data->initialized) {
/* done */
const auto duration2 =
SongTime::FromScale<uint64_t>(data->total_frames,
data->audio_format.sample_rate);
decoder_initialized(data->decoder, data->audio_format,
data->input_stream.IsSeekable(),
duration2);
return true;
}
@@ -167,13 +157,10 @@ flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd,
}
static void
flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
FLAC__uint64 t_start, FLAC__uint64 t_end)
flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec)
{
Decoder &decoder = data->decoder;
data->first_frame = t_start;
while (true) {
DecoderCommand cmd;
if (!data->tag.IsEmpty()) {
@@ -184,24 +171,49 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
cmd = decoder_get_command(decoder);
if (cmd == DecoderCommand::SEEK) {
FLAC__uint64 seek_sample = t_start +
FLAC__uint64 seek_sample =
decoder_seek_where_frame(decoder);
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;
if (FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) {
data->position = 0;
decoder_command_finished(decoder);
} else
decoder_seek_error(decoder);
} else if (cmd == DecoderCommand::STOP ||
FLAC__stream_decoder_get_state(flac_dec) == FLAC__STREAM_DECODER_END_OF_STREAM)
} else if (cmd == DecoderCommand::STOP)
break;
if (t_end != 0 && data->next_frame >= t_end)
/* end of this sub track */
switch (FLAC__stream_decoder_get_state(flac_dec)) {
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:
/* continue decoding */
break;
case FLAC__STREAM_DECODER_END_OF_STREAM:
/* regular end of stream */
return;
case FLAC__STREAM_DECODER_SEEK_ERROR:
/* try to recover from seek error */
if (!FLAC__stream_decoder_flush(flac_dec)) {
LogError(flac_domain, "FLAC__stream_decoder_flush() failed");
return;
}
break;
case FLAC__STREAM_DECODER_OGG_ERROR:
case FLAC__STREAM_DECODER_ABORTED:
case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
/* an error, fatal enough for us to abort the
decoder */
return;
case FLAC__STREAM_DECODER_UNINITIALIZED:
/* we shouldn't see this, ever - bail out */
return;
}
if (!FLAC__stream_decoder_process_single(flac_dec) &&
decoder_get_command(decoder) == DecoderCommand::NONE) {
/* a failure that was not triggered by a
@@ -250,6 +262,24 @@ stream_init(FLAC__StreamDecoder *flac_dec, struct flac_data *data, bool is_ogg)
: stream_init_flac(flac_dec, data);
}
static bool
FlacInitAndDecode(struct flac_data &data, FLAC__StreamDecoder *sd, bool is_ogg)
{
auto init_status = stream_init(sd, &data, is_ogg);
if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
LogWarning(flac_domain,
FLAC__StreamDecoderInitStatusString[init_status]);
return false;
}
bool result = flac_decoder_initialize(&data, sd);
if (result)
flac_decoder_loop(&data, sd);
FLAC__stream_decoder_finish(sd);
return result;
}
static void
flac_decode_internal(Decoder &decoder,
InputStream &input_stream,
@@ -263,24 +293,8 @@ flac_decode_internal(Decoder &decoder,
struct flac_data data(decoder, input_stream);
FLAC__StreamDecoderInitStatus status =
stream_init(flac_dec, &data, is_ogg);
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
FLAC__stream_decoder_delete(flac_dec);
LogWarning(flac_domain,
FLAC__StreamDecoderInitStatusString[status]);
return;
}
FlacInitAndDecode(data, flac_dec, is_ogg);
if (!flac_decoder_initialize(&data, flac_dec, 0)) {
FLAC__stream_decoder_finish(flac_dec);
FLAC__stream_decoder_delete(flac_dec);
return;
}
flac_decoder_loop(&data, flac_dec, 0, 0);
FLAC__stream_decoder_finish(flac_dec);
FLAC__stream_decoder_delete(flac_dec);
}

@@ -20,6 +20,7 @@
#include "config.h"
#include "FlacIOHandle.hxx"
#include "util/Error.hxx"
#include "Log.hxx"
#include "Compiler.h"
#include <errno.h>
@@ -87,7 +88,13 @@ FlacIOSeek(FLAC__IOHandle handle, FLAC__int64 _offset, int whence)
return -1;
}
return is->LockSeek(offset, IgnoreError()) ? 0 : -1;
Error error;
if (!is->LockSeek(offset, error)) {
LogError(error);
return -1;
}
return 0;
}
static FLAC__int64

@@ -441,13 +441,15 @@ mpd_opus_scan_stream(InputStream &is,
if (!oy.ExpectFirstPage(os))
return false;
/* read at most two more pages */
unsigned remaining_pages = 2;
/* read at most 64 more pages */
unsigned remaining_pages = 64;
unsigned remaining_packets = 4;
bool result = false;
ogg_packet packet;
while (true) {
while (remaining_packets > 0) {
int r = ogg_stream_packetout(&os, &packet);
if (r < 0) {
result = false;
@@ -466,6 +468,8 @@ mpd_opus_scan_stream(InputStream &is,
continue;
}
--remaining_packets;
if (packet.b_o_s) {
if (!IsOpusHead(packet))
break;

@@ -85,7 +85,7 @@ public:
char *ReadString() {
uint32_t length;
if (!ReadWord(length))
if (!ReadWord(length) || length >= 65536)
return nullptr;
const char *src = (const char *)Read(length);

@@ -23,10 +23,28 @@
#include "input/InputStream.hxx"
#include "util/Error.hxx"
#include "util/ByteReverse.hxx"
#include "util/StaticFifoBuffer.hxx"
#include "Log.hxx"
#include <assert.h>
#include <string.h>
template<typename B>
static bool
FillBuffer(Decoder &decoder, InputStream &is, B &buffer)
{
buffer.Shift();
auto w = buffer.Write();
assert(!w.IsEmpty());
size_t nbytes = decoder_read(decoder, is, w.data, w.size);
if (nbytes == 0 && is.LockIsEOF())
return false;
buffer.Append(nbytes);
return true;
}
static void
pcm_stream_decode(Decoder &decoder, InputStream &is)
{
@@ -50,25 +68,27 @@ pcm_stream_decode(Decoder &decoder, InputStream &is)
decoder_initialized(decoder, audio_format,
is.IsSeekable(), total_time);
StaticFifoBuffer<uint8_t, 4096> buffer;
DecoderCommand cmd;
do {
char buffer[4096];
size_t nbytes = decoder_read(decoder, is,
buffer, sizeof(buffer));
if (nbytes == 0 && is.LockIsEOF())
if (!FillBuffer(decoder, is, buffer))
break;
auto r = buffer.Read();
/* round down to the nearest frame size, because we
must not pass partial frames to decoder_data() */
r.size -= r.size % frame_size;
buffer.Consume(r.size);
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));
reverse_bytes_16((uint16_t *)r.data,
(uint16_t *)r.data,
(uint16_t *)(r.data + r.size));
cmd = nbytes > 0
? decoder_data(decoder, is,
buffer, nbytes, 0)
cmd = !r.IsEmpty()
? decoder_data(decoder, is, r.data, r.size, 0)
: decoder_get_command(decoder);
if (cmd == DecoderCommand::SEEK) {
uint64_t frame = decoder_seek_where_frame(decoder);
@@ -76,6 +96,7 @@ pcm_stream_decode(Decoder &decoder, InputStream &is)
Error error;
if (is.LockSeek(offset, error)) {
buffer.Clear();
decoder_command_finished(decoder);
} else {
LogError(error);

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

@@ -65,6 +65,24 @@ wildmidi_finish(void)
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
wildmidi_file_decode(Decoder &decoder, Path path_fs)
{
@@ -94,18 +112,11 @@ wildmidi_file_decode(Decoder &decoder, Path path_fs)
DecoderCommand cmd;
do {
char buffer[4096];
int len;
info = WildMidi_GetInfo(wm);
if (info == nullptr)
break;
len = WildMidi_GetOutput(wm, buffer, sizeof(buffer));
if (len <= 0)
break;
cmd = decoder_data(decoder, nullptr, buffer, len, 0);
cmd = wildmidi_output(decoder, wm);
if (cmd == DecoderCommand::SEEK) {
unsigned long seek_where =

@@ -27,6 +27,8 @@
#include <assert.h>
#include <string.h>
static constexpr uint16_t WAVE_FORMAT_PCM = 1;
struct WaveEncoder {
Encoder encoder;
unsigned bits;
@@ -64,15 +66,15 @@ fill_wave_header(struct wave_header *header, int channels, int bits,
header->id_fmt = ToLE32(0x20746d66);
header->id_data = ToLE32(0x61746164);
/* wave format */
header->format = ToLE16(1); // PCM_FORMAT
/* wave format */
header->format = ToLE16(WAVE_FORMAT_PCM);
header->channels = ToLE16(channels);
header->bits = ToLE16(bits);
header->freq = ToLE32(freq);
header->blocksize = ToLE16(block_size);
header->byterate = ToLE32(freq * block_size);
/* chunk sizes (fake data length) */
/* chunk sizes (fake data length) */
header->fmt_size = ToLE32(16);
header->data_size = ToLE32(data_size);
header->riff_size = ToLE32(4 + (8 + 16) + (8 + data_size));

@@ -118,9 +118,15 @@ BufferedSocket::OnSocketReady(unsigned flags)
if (flags & READ) {
assert(!input.IsFull());
if (!ReadToBuffer() || !ResumeInput())
if (!ReadToBuffer())
return false;
if (!ResumeInput())
/* we must return "true" here or
SocketMonitor::Dispatch() will call
Cancel() on a freed object */
return true;
if (!input.IsFull())
ScheduleRead();
}

@@ -134,8 +134,6 @@ ReplayGainFilter::Update()
volume = pcm_float_to_volume(scale);
}
pv.SetVolume(volume);
if (mixer != nullptr) {
/* update the hardware mixer volume */
@@ -146,7 +144,8 @@ ReplayGainFilter::Update()
Error error;
if (!mixer_set_volume(mixer, _volume, error))
LogError(error, "Failed to update hardware mixer");
}
} else
pv.SetVolume(volume);
}
static Filter *
@@ -174,7 +173,9 @@ ReplayGainFilter::Close()
ConstBuffer<void>
ReplayGainFilter::FilterPCM(ConstBuffer<void> src, gcc_unused Error &error)
{
return pv.Apply(src);
return mixer != nullptr
? src
: pv.Apply(src);
}
const struct filter_plugin replay_gain_filter_plugin = {

@@ -47,9 +47,11 @@
#include "filter/FilterInternal.hxx"
#include "filter/FilterRegistry.hxx"
#include "pcm/PcmBuffer.hxx"
#include "pcm/Silence.hxx"
#include "util/StringUtil.hxx"
#include "util/Error.hxx"
#include "util/ConstBuffer.hxx"
#include "util/WritableBuffer.hxx"
#include <algorithm>
@@ -266,9 +268,8 @@ RouteFilter::FilterPCM(ConstBuffer<void> src, gcc_unused Error &error)
(unsigned)sources[c] >= input_format.channels) {
// No source for this destination output,
// give it zeroes as input
memset(chan_destination,
0x00,
bytes_per_frame_per_channel);
PcmSilence({chan_destination, bytes_per_frame_per_channel},
input_format.format);
} else {
// Get the data from channel sources[c]
// and copy it to the output

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

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

@@ -29,6 +29,8 @@
#include <assert.h>
#include <string.h>
class AllocatedPath;
/**
* A path name in the native file system character set.
*
@@ -128,6 +130,22 @@ public:
gcc_pure
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
* object, not including the directory separator. Returns an
@@ -140,7 +158,7 @@ public:
}
gcc_pure
bool IsAbsolute() {
bool IsAbsolute() const {
return PathTraitsFS::IsAbsolute(c_str());
}
};

28
src/fs/Path2.cxx Normal 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()));
}

@@ -247,6 +247,10 @@ AlsaInputStream::Recover(int err)
case -EPIPE:
LogDebug(alsa_input_domain, "Buffer Overrun");
// drop through
#if GCC_CHECK_VERSION(7,0)
[[fallthrough]];
#endif
case -ESTRPIPE:
case -EINTR:
err = snd_pcm_recover(capture_handle, err, 1);

72
src/lib/ffmpeg/Buffer.hxx Normal 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

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

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

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

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

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

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

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

@@ -59,6 +59,18 @@ NfsManager::Compare::operator()(const ManagedConnection &a,
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()
{
assert(GetEventLoop().IsInside());

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

@@ -218,7 +218,7 @@ PulseMixer::SetVolume(unsigned new_volume, Error &error)
struct pa_cvolume cvolume;
pa_cvolume_set(&cvolume, volume.channels,
(pa_volume_t)new_volume * PA_VOLUME_NORM / 100 + 0.5);
(new_volume * PA_VOLUME_NORM + 50) / 100);
bool success = pulse_output_set_volume(output, &cvolume, error);
if (success)
volume = cvolume;

@@ -216,7 +216,7 @@ SmbclientNeighborExplorer::Run()
prev = i;
} else {
/* 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);
#else
/* the forward_list::splice_after() lvalue

@@ -28,7 +28,7 @@ struct notify {
Cond cond;
bool pending;
#if !defined(WIN32) && !defined(__NetBSD__) && !defined(__BIONIC__)
#ifdef __GLIBC__
constexpr
#endif
notify():pending(false) {}

@@ -760,6 +760,9 @@ alsa_recover(AlsaOutput *ad, int err)
if (err == -EAGAIN)
return 0;
/* fall-through to snd_pcm_prepare: */
#if GCC_CHECK_VERSION(7,0)
[[fallthrough]];
#endif
case SND_PCM_STATE_SETUP:
case SND_PCM_STATE_XRUN:
ad->period_position = 0;

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

@@ -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);
if (encoder_plugin == nullptr) {
error.Format(config_domain,
@@ -247,7 +249,6 @@ ShoutOutput::Configure(const config_param &param, Error &error)
{
char temp[11];
memset(temp, 0, sizeof(temp));
snprintf(temp, sizeof(temp), "%u", audio_format.channels);
shout_set_audio_info(shout_conn, SHOUT_AI_CHANNELS, temp);

@@ -148,10 +148,10 @@ winmm_output_open(AudioOutput *ao, AudioFormat &audio_format,
}
switch (audio_format.format) {
case SampleFormat::S8:
case SampleFormat::S16:
break;
case SampleFormat::S8:
case SampleFormat::S24_P32:
case SampleFormat::S32:
case SampleFormat::FLOAT:

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

47
src/pcm/Interleave.cxx Normal 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

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

35
src/pcm/Silence.cxx Normal file

@@ -0,0 +1,35 @@
/*
* 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.
*/
#include "config.h"
#include "Silence.hxx"
#include "AudioFormat.hxx"
#include "util/WritableBuffer.hxx"
#include <string.h>
void
PcmSilence(WritableBuffer<void> dest, SampleFormat format)
{
uint8_t pattern = 0;
if (format == SampleFormat::DSD)
pattern = 0x69;
memset(dest.data, pattern, dest.size);
}

36
src/pcm/Silence.hxx Normal file

@@ -0,0 +1,36 @@
/*
* 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_PCM_SILENCE_HXX
#define MPD_PCM_SILENCE_HXX
#include "check.h"
#include <stdint.h>
template<typename T> struct WritableBuffer;
enum class SampleFormat : uint8_t;
/**
* Fill the given buffer with the format-specific silence pattern.
*/
void
PcmSilence(WritableBuffer<void> dest, SampleFormat format);
#endif

@@ -19,10 +19,12 @@
#include "config.h"
#include "Volume.hxx"
#include "Silence.hxx"
#include "Domain.hxx"
#include "PcmUtils.hxx"
#include "Traits.hxx"
#include "util/ConstBuffer.hxx"
#include "util/WritableBuffer.hxx"
#include "util/Error.hxx"
#include "PcmDither.cxx" // including the .cxx file to get inlined templates
@@ -134,9 +136,7 @@ PcmVolume::Apply(ConstBuffer<void> src)
if (volume == 0) {
/* optimized special case: 0% volume = memset(0) */
/* TODO: is this valid for all sample formats? What
about floating point? */
memset(data, 0, src.size);
PcmSilence({data, src.size}, format);
return { data, src.size };
}

@@ -92,7 +92,7 @@ check_range(Client &client, unsigned *value_r1, unsigned *value_r2,
return false;
}
if (unsigned(value) > std::numeric_limits<unsigned>::max()) {
if (value > std::numeric_limits<int>::max()) {
command_error(client, ACK_ERROR_ARG,
"Number too large: %s", s);
return false;
@@ -117,7 +117,7 @@ check_range(Client &client, unsigned *value_r1, unsigned *value_r2,
return false;
}
if (unsigned(value) > std::numeric_limits<unsigned>::max()) {
if (value > std::numeric_limits<int>::max()) {
command_error(client, ACK_ERROR_ARG,
"Number too large: %s", s);
return false;

@@ -426,14 +426,15 @@ Queue::SetPriority(unsigned position, uint8_t priority, int after_order)
if (_order < (unsigned)after_order) {
/* the specified song has been played already
- enqueue it only if its priority has just
become bigger than the current one's */
- enqueue it only if its priority has been
increased and is now bigger than the
current one's */
const unsigned after_position =
OrderToPosition(after_order);
const Item *after_item =
&items[after_position];
if (old_priority > after_item->priority ||
if (priority <= old_priority ||
priority <= after_item->priority)
/* priority hasn't become bigger */
return true;

@@ -137,7 +137,7 @@ CompositeStorage::Directory::Make(const char *uri)
Directory *directory = this;
while (*uri != 0) {
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),
Directory());
#else
@@ -265,22 +265,13 @@ CompositeStorage::FindStorage(const char *uri) const
return result;
}
CompositeStorage::FindResult
CompositeStorage::FindStorage(const char *uri, Error &error) const
{
auto result = FindStorage(uri);
if (result.directory == nullptr)
error.Set(composite_domain, "No such directory");
return result;
}
bool
CompositeStorage::GetInfo(const char *uri, bool follow, FileInfo &info,
Error &error)
{
const ScopeLock protect(mutex);
auto f = FindStorage(uri, error);
auto f = FindStorage(uri);
if (f.directory->storage != nullptr &&
f.directory->storage->GetInfo(f.uri, follow, info, error))
return true;
@@ -295,6 +286,8 @@ CompositeStorage::GetInfo(const char *uri, bool follow, FileInfo &info,
return true;
}
if (!error.IsDefined())
error.Set(composite_domain, "No such directory");
return false;
}
@@ -304,13 +297,15 @@ CompositeStorage::OpenDirectory(const char *uri,
{
const ScopeLock protect(mutex);
auto f = FindStorage(uri, error);
auto f = FindStorage(uri);
const Directory *directory = f.directory->Find(f.uri);
if (directory == nullptr || directory->children.empty()) {
/* no virtual directories here */
if (f.directory->storage == nullptr)
if (f.directory->storage == nullptr) {
error.Set(composite_domain, "No such directory");
return nullptr;
}
return f.directory->storage->OpenDirectory(f.uri, error);
}

@@ -45,7 +45,7 @@ class CompositeStorage final : public Storage {
*/
struct Directory {
/**
* The #Storage mounted n this virtual directory. All
* The #Storage mounted in this virtual directory. All
* "leaf" Directory instances must have a #Storage.
* Other Directory instances may have one, and child
* mounts will be "mixed" in.
@@ -155,9 +155,16 @@ private:
}
}
/**
* Follow the given URI path, and find the outermost directory
* which is a #Storage mount point. If there are no mounts,
* it returns the root directory (with a nullptr "storage"
* attribute, of course). FindResult::uri contains the
* remaining unused part of the URI (may be empty if all of
* the URI was used).
*/
gcc_pure
FindResult FindStorage(const char *uri) const;
FindResult FindStorage(const char *uri, Error &error) const;
const char *MapToRelativeUTF8(const Directory &directory,
const char *uri) const;

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

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

@@ -21,7 +21,6 @@
#define MPD_TAG_ITEM_HXX
#include "TagType.h"
#include "Compiler.h"
/**
* One tag value. It is a mapping of #TagType to am arbitrary string
@@ -35,11 +34,14 @@ struct TagItem {
/**
* the value of this tag; this is a variable length string
*/
char value[sizeof(long) - sizeof(type)];
char value[1];
TagItem() = default;
TagItem(const TagItem &other) = delete;
TagItem &operator=(const TagItem &other) = delete;
} gcc_packed;
};
static_assert(sizeof(TagItem) == 2, "Unexpected size");
static_assert(alignof(TagItem) == 1, "Unexpected alignment");
#endif

@@ -23,19 +23,23 @@
#include "util/Cast.hxx"
#include "util/VarSize.hxx"
#include <limits>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
Mutex tag_pool_lock;
static constexpr size_t NUM_SLOTS = 4096;
static constexpr size_t NUM_SLOTS = 4093;
struct TagPoolSlot {
TagPoolSlot *next;
unsigned char ref;
TagItem item;
static constexpr unsigned MAX_REF = std::numeric_limits<decltype(ref)>::max();
TagPoolSlot(TagPoolSlot *_next, TagType type,
const char *value, size_t length)
:next(_next), ref(1) {
@@ -46,7 +50,7 @@ struct TagPoolSlot {
static TagPoolSlot *Create(TagPoolSlot *_next, TagType type,
const char *value, size_t length);
} gcc_packed;
};
TagPoolSlot *
TagPoolSlot::Create(TagPoolSlot *_next, TagType type,
@@ -87,7 +91,7 @@ calc_hash(TagType type, const char *p)
return hash ^ type;
}
#if defined(__clang__) || GCC_CHECK_VERSION(4,7)
#if CLANG_OR_GCC_VERSION(4,7)
constexpr
#endif
static inline TagPoolSlot *
@@ -116,7 +120,7 @@ tag_pool_get_item(TagType type, const char *value, size_t length)
if (slot->item.type == type &&
length == strlen(slot->item.value) &&
memcmp(value, slot->item.value, length) == 0 &&
slot->ref < 0xff) {
slot->ref < TagPoolSlot::MAX_REF) {
assert(slot->ref > 0);
++slot->ref;
return &slot->item;
@@ -135,19 +139,15 @@ tag_pool_dup_item(TagItem *item)
assert(slot->ref > 0);
if (slot->ref < 0xff) {
if (slot->ref < TagPoolSlot::MAX_REF) {
++slot->ref;
return item;
} else {
/* the reference counter overflows above 0xff;
duplicate the item, and start with 1 */
/* the reference counter overflows above MAX_REF;
obtain a reference to a different TagPoolSlot which
isn't yet "full" */
size_t length = strlen(item->value);
auto slot_p = tag_value_slot_p(item->type,
item->value, length);
slot = TagPoolSlot::Create(*slot_p, item->type,
item->value, strlen(item->value));
*slot_p = slot;
return &slot->item;
return tag_pool_get_item(item->type, item->value, length);
}
}

@@ -41,9 +41,13 @@ class PosixCond {
pthread_cond_t cond;
public:
#if defined(__NetBSD__) || defined(__BIONIC__)
/* NetBSD's PTHREAD_COND_INITIALIZER is not compatible with
"constexpr" */
#ifdef __GLIBC__
/* optimized constexpr constructor for pthread implementations
that support it */
constexpr PosixCond():cond(PTHREAD_COND_INITIALIZER) {}
#else
/* slow fallback for pthread implementations that are not
compatible with "constexpr" */
PosixCond() {
pthread_cond_init(&cond, nullptr);
}
@@ -51,10 +55,6 @@ public:
~PosixCond() {
pthread_cond_destroy(&cond);
}
#else
/* optimized constexpr constructor for sane POSIX
implementations */
constexpr PosixCond():cond(PTHREAD_COND_INITIALIZER) {}
#endif
PosixCond(const PosixCond &other) = delete;

@@ -41,9 +41,13 @@ class PosixMutex {
pthread_mutex_t mutex;
public:
#if defined(__NetBSD__) || defined(__BIONIC__)
/* NetBSD's PTHREAD_MUTEX_INITIALIZER is not compatible with
"constexpr" */
#ifdef __GLIBC__
/* optimized constexpr constructor for pthread implementations
that support it */
constexpr PosixMutex():mutex(PTHREAD_MUTEX_INITIALIZER) {}
#else
/* slow fallback for pthread implementations that are not
compatible with "constexpr" */
PosixMutex() {
pthread_mutex_init(&mutex, nullptr);
}
@@ -51,10 +55,6 @@ public:
~PosixMutex() {
pthread_mutex_destroy(&mutex);
}
#else
/* optimized constexpr constructor for sane POSIX
implementations */
constexpr PosixMutex():mutex(PTHREAD_MUTEX_INITIALIZER) {}
#endif
PosixMutex(const PosixMutex &other) = delete;

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

@@ -46,7 +46,7 @@ static size_t
AlignToPageSize(size_t size)
{
static const long page_size = sysconf(_SC_PAGESIZE);
if (page_size > 0)
if (page_size == 0)
return size;
size_t ps(page_size);

@@ -41,7 +41,7 @@
#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 ignored "-Wstrict-aliasing"
#endif
@@ -114,7 +114,7 @@ public:
}
};
#if defined(__clang__) || GCC_VERSION >= 40700
#if CLANG_OR_GCC_VERSION(4,7)
#pragma GCC diagnostic pop
#endif

89
src/util/ScopeExit.hxx Normal 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

@@ -1,13 +0,0 @@
[Unit]
Description=Music Player Daemon
After=network.target sound.target
[Service]
ExecStart=@prefix@/bin/mpd --no-daemon
# allow MPD to use real-time priority 50
LimitRTPRIO=50
LimitRTTIME=infinity
[Install]
WantedBy=multi-user.target

@@ -0,0 +1,26 @@
[Unit]
Description=Music Player Daemon
After=network.target sound.target
[Service]
ExecStart=@prefix@/bin/mpd --no-daemon
# allow MPD to use real-time priority 50
LimitRTPRIO=50
LimitRTTIME=infinity
# disallow writing to /usr, /bin, /sbin, ...
ProtectSystem=yes
# more paranoid security settings
NoNewPrivileges=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
ProtectKernelModules=yes
# AF_NETLINK is required by libsmbclient, or it will exit() .. *sigh*
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
RestrictNamespaces=yes
[Install]
WantedBy=multi-user.target
Also=mpd.socket

@@ -0,0 +1,29 @@
[Unit]
Description=Music Player Daemon
After=network.target sound.target
[Service]
ExecStart=@prefix@/bin/mpd --no-daemon
# allow MPD to use real-time priority 50
LimitRTPRIO=50
LimitRTTIME=infinity
# disallow writing to /usr, /bin, /sbin, ...
ProtectSystem=yes
# more paranoid security settings
NoNewPrivileges=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
# AF_NETLINK is required by libsmbclient, or it will exit() .. *sigh*
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
RestrictNamespaces=yes
# Note that "ProtectKernelModules=yes" is missing in the user unit
# because systemd 232 is unable to reduce its own capabilities
# ("Failed at step CAPABILITIES spawning /usr/bin/mpd: Operation not
# permitted")
[Install]
WantedBy=default.target

@@ -49,9 +49,9 @@ CPPUNIT_TEST_SUITE_REGISTRATION(ByteReverseTest);
void
ByteReverseTest::TestByteReverse2()
{
static const char src[] = "123456";
static const char src[] gcc_alignas(uint16_t, 2) = "123456";
static const char result[] = "214365";
static uint8_t dest[ARRAY_SIZE(src)];
static uint8_t dest[ARRAY_SIZE(src)] gcc_alignas(uint16_t, 2);
reverse_bytes(dest, (const uint8_t *)src,
(const uint8_t *)(src + ARRAY_SIZE(src) - 1), 2);
@@ -73,9 +73,9 @@ ByteReverseTest::TestByteReverse3()
void
ByteReverseTest::TestByteReverse4()
{
static const char src[] = "12345678";
static const char src[] gcc_alignas(uint32_t, 4) = "12345678";
static const char result[] = "43218765";
static uint8_t dest[ARRAY_SIZE(src)];
static uint8_t dest[ARRAY_SIZE(src)] gcc_alignas(uint32_t, 4);
reverse_bytes(dest, (const uint8_t *)src,
(const uint8_t *)(src + ARRAY_SIZE(src) - 1), 4);

@@ -164,21 +164,6 @@ QueuePriorityTest::TestPriority()
check_descending_priority(&queue, current_order + 1);
/* priority=60 for the old prio50 item; must not be moved,
because it's before the current song, and it's status
hasn't changed (it was already higher before) */
unsigned c_order = 0;
unsigned c_position = queue.OrderToPosition(c_order);
CPPUNIT_ASSERT_EQUAL(50u, unsigned(queue.items[c_position].priority));
queue.SetPriority(c_position, 60, current_order);
current_order = queue.PositionToOrder(current_position);
CPPUNIT_ASSERT_EQUAL(3u, current_order);
c_order = queue.PositionToOrder(c_position);
CPPUNIT_ASSERT_EQUAL(0u, c_order);
/* move the prio=20 item back */
a_order = queue.PositionToOrder(a_position);