Compare commits

...

169 Commits

Author SHA1 Message Date
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
86 changed files with 2347 additions and 945 deletions

View File

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

View File

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

69
NEWS
View File

@@ -1,3 +1,72 @@
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) ver 0.19.13 (2016/02/23)
* tags * tags
- aiff, riff: fix ID3 chunk padding - aiff, riff: fix ID3 chunk padding

View File

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

View File

@@ -1,6 +1,7 @@
<?xml version='1.0' encoding="utf-8"?> <?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"docbook/dtd/xml/4.2/docbookx.dtd"> "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book> <book>
<title>The Music Player Daemon - Developer's Manual</title> <title>The Music Player Daemon - Developer's Manual</title>
@@ -40,7 +41,7 @@
<listitem> <listitem>
<para> <para>
the code should be C++11 compliant, and must compile with the code should be C++11 compliant, and must compile with
<application>GCC</application> 4.6 and <application>GCC</application> 4.7 and
<application>clang</application> 3.2 <application>clang</application> 3.2
</para> </para>
</listitem> </listitem>

154
doc/include/tags.xml Normal file
View 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>

View File

@@ -174,18 +174,6 @@ MP3 playback.
This specifies whether relative or absolute paths for song filenames are used This specifies whether relative or absolute paths for song filenames are used
when saving playlists. The default is "no". when saving playlists. The default is "no".
.TP .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> .B auto_update <yes or no>
This specifies the whether to support automatic update of music database when This specifies the whether to support automatic update of music database when
files are changed in music_directory. The default is to disable autoupdate files are changed in music_directory. The default is to disable autoupdate

View File

@@ -115,7 +115,7 @@
# #
# This setting defines a list of tag types that will be extracted during the # 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 # 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" #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 # This setting enables automatic update of MPD's database when files in
@@ -164,7 +164,7 @@
# Permissions ################################################################# # Permissions #################################################################
# #
# If this setting is set, MPD will require password authorization. The password # 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" #password "password@read,add,control,admin"
# #
@@ -231,7 +231,7 @@ input {
# #
#audio_output { #audio_output {
# type "shout" # type "shout"
# encoding "ogg" # optional # encoder "vorbis" # optional
# name "My Shout Stream" # name "My Shout Stream"
# host "localhost" # host "localhost"
# port "8000" # port "8000"

View File

@@ -1,6 +1,7 @@
<?xml version='1.0' encoding="utf-8"?> <?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"docbook/dtd/xml/4.2/docbookx.dtd"> "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book> <book>
<title>The Music Player Daemon protocol</title> <title>The Music Player Daemon protocol</title>
@@ -65,8 +66,8 @@
<function>strcpy</function> just fine with UTF-8 encoded <function>strcpy</function> just fine with UTF-8 encoded
strings. For example: <returnvalue>OK</returnvalue> encoded in strings. For example: <returnvalue>OK</returnvalue> encoded in
UTF-8 is simply <returnvalue>OK</returnvalue>. For more UTF-8 is simply <returnvalue>OK</returnvalue>. For more
information on UTF=8: information on UTF-8:
http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8) <ulink url="http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8"/>)
</para> </para>
</section> </section>
@@ -201,6 +202,25 @@
omitted, then the maximum possible value is assumed. omitted, then the maximum possible value is assumed.
</para> </para>
</section> </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>
<chapter id="recipes"> <chapter id="recipes">

View File

@@ -1,6 +1,7 @@
<?xml version='1.0' encoding="utf-8"?> <?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"docbook/dtd/xml/4.2/docbookx.dtd"> "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book> <book>
<title>The Music Player Daemon - User's Manual</title> <title>The Music Player Daemon - User's Manual</title>
@@ -16,7 +17,7 @@
<application>MPD</application> (Music Player Daemon) is, as the <application>MPD</application> (Music Player Daemon) is, as the
name suggests, a server software allowing you to remotely play name suggests, a server software allowing you to remotely play
your music, handle playlists, deliver music (HTTP streams with your music, handle playlists, deliver music (HTTP streams with
various sub-protocols) and organizze playlists. various sub-protocols) and organize playlists.
</para> </para>
<para> <para>
@@ -944,6 +945,33 @@ systemctl start mpd.socket</programlisting>
<section id="config_other"> <section id="config_other">
<title>Other Settings</title> <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> <section>
<title>The State File</title> <title>The State File</title>
@@ -1297,6 +1325,19 @@ database {
</para> </para>
</section> </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"> <section id="queue">
<title>The queue</title> <title>The queue</title>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2003-2016 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* necessary because libavutil/common.h uses UINT64_C */
#define __STDC_CONSTANT_MACROS
#include "config.h"
#include "FfmpegIo.hxx"
#include "../DecoderAPI.hxx"
#include "input/InputStream.hxx"
#include "util/Error.hxx"
AvioStream::~AvioStream()
{
if (io != nullptr) {
av_free(io->buffer);
av_free(io);
}
}
static int
mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size)
{
AvioStream *stream = (AvioStream *)opaque;
return decoder_read(stream->decoder, stream->input,
(void *)buf, size);
}
static int64_t
mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
{
AvioStream *stream = (AvioStream *)opaque;
switch (whence) {
case SEEK_SET:
break;
case SEEK_CUR:
pos += stream->input.GetOffset();
break;
case SEEK_END:
if (!stream->input.KnownSize())
return -1;
pos += stream->input.GetSize();
break;
case AVSEEK_SIZE:
if (!stream->input.KnownSize())
return -1;
return stream->input.GetSize();
default:
return -1;
}
if (!stream->input.LockSeek(pos, IgnoreError()))
return -1;
return stream->input.GetOffset();
}
bool
AvioStream::Open()
{
constexpr size_t BUFFER_SIZE = 8192;
auto buffer = (unsigned char *)av_malloc(BUFFER_SIZE);
if (buffer == nullptr)
return false;
io = avio_alloc_context(buffer, BUFFER_SIZE,
false, this,
mpd_ffmpeg_stream_read, nullptr,
input.IsSeekable()
? mpd_ffmpeg_stream_seek : nullptr);
/* If avio_alloc_context() fails, who frees the buffer? The
libavformat API documentation does not specify this, it
only says that AVIOContext.buffer must be freed in the end,
however no AVIOContext exists in that failure code path. */
return io != nullptr;
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2003-2016 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_FFMPEG_IO_HXX
#define MPD_FFMPEG_IO_HXX
#include "check.h"
extern "C" {
#include "libavformat/avio.h"
}
#include <stdint.h>
class InputStream;
struct Decoder;
struct AvioStream {
Decoder *const decoder;
InputStream &input;
AVIOContext *io;
AvioStream(Decoder *_decoder, InputStream &_input)
:decoder(_decoder), input(_input), io(nullptr) {}
~AvioStream();
bool Open();
};
#endif

View File

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

View File

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

View File

@@ -33,7 +33,7 @@ flac_data::flac_data(Decoder &_decoder,
InputStream &_input_stream) InputStream &_input_stream)
:FlacInput(_input_stream, &_decoder), :FlacInput(_input_stream, &_decoder),
initialized(false), unsupported(false), initialized(false), unsupported(false),
total_frames(0), first_frame(0), next_frame(0), position(0), position(0),
decoder(_decoder), input_stream(_input_stream) 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 static void
flac_got_stream_info(struct flac_data *data, flac_got_stream_info(struct flac_data *data,
const FLAC__StreamMetadata_StreamInfo *stream_info) const FLAC__StreamMetadata_StreamInfo *stream_info)
@@ -66,22 +98,10 @@ flac_got_stream_info(struct flac_data *data,
if (data->initialized || data->unsupported) if (data->initialized || data->unsupported)
return; return;
Error error; data->Initialize(stream_info->sample_rate,
if (!audio_format_init_checked(data->audio_format, stream_info->bits_per_sample,
stream_info->sample_rate, stream_info->channels,
flac_sample_format(stream_info->bits_per_sample), stream_info->total_samples);
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;
} }
void flac_metadata_common_cb(const FLAC__StreamMetadata * block, 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) if (data->unsupported)
return false; return false;
Error error; return data->Initialize(header->sample_rate,
if (!audio_format_init_checked(data->audio_format, header->bits_per_sample,
header->sample_rate, header->channels,
flac_sample_format(header->bits_per_sample), /* unknown duration */
header->channels, error)) { 0);
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;
} }
FLAC__StreamDecoderWriteStatus FLAC__StreamDecoderWriteStatus
@@ -155,7 +158,6 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
FLAC__uint64 nbytes) FLAC__uint64 nbytes)
{ {
void *buffer; void *buffer;
unsigned bit_rate;
if (!data->initialized && !flac_got_first_frame(data, &frame->header)) if (!data->initialized && !flac_got_first_frame(data, &frame->header))
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; 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, data->audio_format.format, buf,
0, frame->header.blocksize); 0, frame->header.blocksize);
if (nbytes > 0) unsigned bit_rate = nbytes * 8 * frame->header.sample_rate /
bit_rate = nbytes * 8 * frame->header.sample_rate / (1000 * frame->header.blocksize);
(1000 * frame->header.blocksize);
else
bit_rate = 0;
auto cmd = decoder_data(data->decoder, data->input_stream, auto cmd = decoder_data(data->decoder, data->input_stream,
buffer, buffer_size, buffer, buffer_size,
bit_rate); bit_rate);
data->next_frame += frame->header.blocksize;
switch (cmd) { switch (cmd) {
case DecoderCommand::NONE: case DecoderCommand::NONE:
case DecoderCommand::START: case DecoderCommand::START:

View File

@@ -55,23 +55,9 @@ struct flac_data : public FlacInput {
AudioFormat audio_format; AudioFormat audio_format;
/** /**
* The total number of frames in this song. The decoder * End of last frame's position within the stream. This is
* plugin may initialize this attribute to override the value * used for bit rate calculations.
* provided by libFLAC (e.g. for sub songs from a CUE sheet).
*/ */
FLAC__uint64 total_frames;
/**
* The number of the first frame in this song. This is only
* non-zero if playing sub songs from a CUE sheet.
*/
FLAC__uint64 first_frame;
/**
* The number of the next frame which is going to be decoded.
*/
FLAC__uint64 next_frame;
FLAC__uint64 position; FLAC__uint64 position;
Decoder &decoder; Decoder &decoder;
@@ -80,6 +66,12 @@ struct flac_data : public FlacInput {
Tag tag; Tag tag;
flac_data(Decoder &decoder, InputStream &input_stream); 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, void flac_metadata_common_cb(const FLAC__StreamMetadata * block,

View File

@@ -132,26 +132,16 @@ flac_decoder_new(void)
} }
static bool static bool
flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd, flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd)
FLAC__uint64 duration)
{ {
data->total_frames = duration;
if (!FLAC__stream_decoder_process_until_end_of_metadata(sd)) { 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; return false;
} }
if (data->initialized) { if (data->initialized) {
/* done */ /* 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; return true;
} }
@@ -167,13 +157,10 @@ flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd,
} }
static void static void
flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec, flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec)
FLAC__uint64 t_start, FLAC__uint64 t_end)
{ {
Decoder &decoder = data->decoder; Decoder &decoder = data->decoder;
data->first_frame = t_start;
while (true) { while (true) {
DecoderCommand cmd; DecoderCommand cmd;
if (!data->tag.IsEmpty()) { if (!data->tag.IsEmpty()) {
@@ -184,24 +171,49 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
cmd = decoder_get_command(decoder); cmd = decoder_get_command(decoder);
if (cmd == DecoderCommand::SEEK) { if (cmd == DecoderCommand::SEEK) {
FLAC__uint64 seek_sample = t_start + FLAC__uint64 seek_sample =
decoder_seek_where_frame(decoder); decoder_seek_where_frame(decoder);
if (seek_sample >= t_start && if (FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) {
(t_end == 0 || seek_sample <= t_end) &&
FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) {
data->next_frame = seek_sample;
data->position = 0; data->position = 0;
decoder_command_finished(decoder); decoder_command_finished(decoder);
} else } else
decoder_seek_error(decoder); decoder_seek_error(decoder);
} else if (cmd == DecoderCommand::STOP || } else if (cmd == DecoderCommand::STOP)
FLAC__stream_decoder_get_state(flac_dec) == FLAC__STREAM_DECODER_END_OF_STREAM)
break; break;
if (t_end != 0 && data->next_frame >= t_end) switch (FLAC__stream_decoder_get_state(flac_dec)) {
/* end of this sub track */ 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; 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) && if (!FLAC__stream_decoder_process_single(flac_dec) &&
decoder_get_command(decoder) == DecoderCommand::NONE) { decoder_get_command(decoder) == DecoderCommand::NONE) {
/* a failure that was not triggered by a /* 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); : 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 static void
flac_decode_internal(Decoder &decoder, flac_decode_internal(Decoder &decoder,
InputStream &input_stream, InputStream &input_stream,
@@ -263,24 +293,8 @@ flac_decode_internal(Decoder &decoder,
struct flac_data data(decoder, input_stream); struct flac_data data(decoder, input_stream);
FLAC__StreamDecoderInitStatus status = FlacInitAndDecode(data, flac_dec, is_ogg);
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;
}
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); FLAC__stream_decoder_delete(flac_dec);
} }

View File

@@ -20,6 +20,7 @@
#include "config.h" #include "config.h"
#include "FlacIOHandle.hxx" #include "FlacIOHandle.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "Log.hxx"
#include "Compiler.h" #include "Compiler.h"
#include <errno.h> #include <errno.h>
@@ -87,7 +88,13 @@ FlacIOSeek(FLAC__IOHandle handle, FLAC__int64 _offset, int whence)
return -1; 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 static FLAC__int64

View File

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

View File

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

View File

@@ -23,10 +23,28 @@
#include "input/InputStream.hxx" #include "input/InputStream.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/ByteReverse.hxx" #include "util/ByteReverse.hxx"
#include "util/StaticFifoBuffer.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <assert.h>
#include <string.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 static void
pcm_stream_decode(Decoder &decoder, InputStream &is) pcm_stream_decode(Decoder &decoder, InputStream &is)
{ {
@@ -50,25 +68,27 @@ pcm_stream_decode(Decoder &decoder, InputStream &is)
decoder_initialized(decoder, audio_format, decoder_initialized(decoder, audio_format,
is.IsSeekable(), total_time); is.IsSeekable(), total_time);
StaticFifoBuffer<uint8_t, 4096> buffer;
DecoderCommand cmd; DecoderCommand cmd;
do { do {
char buffer[4096]; if (!FillBuffer(decoder, is, buffer))
size_t nbytes = decoder_read(decoder, is,
buffer, sizeof(buffer));
if (nbytes == 0 && is.LockIsEOF())
break; 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) if (reverse_endian)
/* make sure we deliver samples in host byte order */ /* make sure we deliver samples in host byte order */
reverse_bytes_16((uint16_t *)buffer, reverse_bytes_16((uint16_t *)r.data,
(uint16_t *)buffer, (uint16_t *)r.data,
(uint16_t *)(buffer + nbytes)); (uint16_t *)(r.data + r.size));
cmd = nbytes > 0 cmd = !r.IsEmpty()
? decoder_data(decoder, is, ? decoder_data(decoder, is, r.data, r.size, 0)
buffer, nbytes, 0)
: decoder_get_command(decoder); : decoder_get_command(decoder);
if (cmd == DecoderCommand::SEEK) { if (cmd == DecoderCommand::SEEK) {
uint64_t frame = decoder_seek_where_frame(decoder); uint64_t frame = decoder_seek_where_frame(decoder);
@@ -76,6 +96,7 @@ pcm_stream_decode(Decoder &decoder, InputStream &is)
Error error; Error error;
if (is.LockSeek(offset, error)) { if (is.LockSeek(offset, error)) {
buffer.Clear();
decoder_command_finished(decoder); decoder_command_finished(decoder);
} else { } else {
LogError(error); LogError(error);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "Path.hxx"
#include "AllocatedPath.hxx"
AllocatedPath
Path::GetDirectoryName() const
{
return AllocatedPath::FromFS(PathTraitsFS::GetParent(c_str()));
}

View File

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

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

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_FFMPEG_BUFFER_HXX
#define MPD_FFMPEG_BUFFER_HXX
extern "C" {
#include <libavutil/mem.h>
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 18, 0)
#define HAVE_AV_FAST_MALLOC
#else
#include <libavcodec/avcodec.h>
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 25, 0)
#define HAVE_AV_FAST_MALLOC
#endif
#endif
}
#include <stddef.h>
/* suppress the ffmpeg compatibility macro */
#ifdef SampleFormat
#undef SampleFormat
#endif
class FfmpegBuffer {
void *data;
unsigned size;
public:
FfmpegBuffer():data(nullptr), size(0) {}
~FfmpegBuffer() {
av_free(data);
}
void *Get(size_t min_size) {
#ifdef HAVE_AV_FAST_MALLOC
av_fast_malloc(&data, &size, min_size);
#else
void *new_data = av_fast_realloc(data, &size, min_size);
if (new_data == nullptr)
return AVERROR(ENOMEM);
data = new_data;
#endif
return data;
}
template<typename T>
T *GetT(size_t n) {
return (T *)Get(n * sizeof(T));
}
};
#endif

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

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* necessary because libavutil/common.h uses UINT64_C */
#define __STDC_CONSTANT_MACROS
#include "config.h"
#include "Init.hxx"
#include "LogCallback.hxx"
extern "C" {
#include <libavformat/avformat.h>
}
void
FfmpegInit()
{
av_log_set_callback(FfmpegLogCallback);
av_register_all();
}

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

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_FFMPEG_INIT_HXX
#define MPD_FFMPEG_INIT_HXX
void
FfmpegInit();
#endif

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* necessary because libavutil/common.h uses UINT64_C */
#define __STDC_CONSTANT_MACROS
#include "config.h"
#include "LogCallback.hxx"
#include "Domain.hxx"
#include "LogV.hxx"
#include "util/Domain.hxx"
extern "C" {
#include <libavutil/log.h>
}
#include <stdio.h>
gcc_const
static LogLevel
FfmpegImportLogLevel(int level)
{
if (level <= AV_LOG_FATAL)
return LogLevel::ERROR;
if (level <= AV_LOG_WARNING)
return LogLevel::WARNING;
if (level <= AV_LOG_INFO)
return LogLevel::INFO;
return LogLevel::DEBUG;
}
void
FfmpegLogCallback(gcc_unused void *ptr, int level, const char *fmt, va_list vl)
{
const AVClass * cls = nullptr;
if (ptr != nullptr)
cls = *(const AVClass *const*)ptr;
if (cls != nullptr) {
char domain[64];
snprintf(domain, sizeof(domain), "%s/%s",
ffmpeg_domain.GetName(), cls->item_name(ptr));
const Domain d(domain);
LogFormatV(d, FfmpegImportLogLevel(level), fmt, vl);
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_FFMPEG_LOG_CALLBACK_HXX
#define MPD_FFMPEG_LOG_CALLBACK_HXX
#include "check.h"
#include <stdarg.h>
void
FfmpegLogCallback(void *ptr, int level, const char *fmt, va_list vl);
#endif

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "LogError.hxx"
#include "Domain.hxx"
#include "Log.hxx"
#include <cstdint> /* needed due to libavutil bug */
extern "C" {
#include <libavutil/error.h>
}
void
LogFfmpegError(int errnum)
{
char msg[256];
av_strerror(errnum, msg, sizeof(msg));
LogError(ffmpeg_domain, msg);
}
void
LogFfmpegError(int errnum, const char *prefix)
{
char msg[256];
av_strerror(errnum, msg, sizeof(msg));
FormatError(ffmpeg_domain, "%s: %s", prefix, msg);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_FFMPEG_LOG_ERROR_HXX
#define MPD_FFMPEG_LOG_ERROR_HXX
void
LogFfmpegError(int errnum);
void
LogFfmpegError(int errnum, const char *prefix);
#endif

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

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_FFMPEG_TIME_HXX
#define MPD_FFMPEG_TIME_HXX
#include "Chrono.hxx"
#include "Compiler.h"
extern "C" {
#include <libavutil/avutil.h>
#include <libavutil/mathematics.h>
}
#include <assert.h>
#include <stdint.h>
/* suppress the ffmpeg compatibility macro */
#ifdef SampleFormat
#undef SampleFormat
#endif
gcc_const
static inline double
FfmpegTimeToDouble(int64_t t, const AVRational time_base)
{
assert(t != (int64_t)AV_NOPTS_VALUE);
return (double)av_rescale_q(t, time_base, (AVRational){1, 1024})
/ (double)1024;
}
template<typename Ratio>
static inline constexpr AVRational
RatioToAVRational()
{
return { Ratio::num, Ratio::den };
}
/**
* Convert a FFmpeg time stamp to a #SongTime.
*/
gcc_const
static inline SongTime
FromFfmpegTime(int64_t t, const AVRational time_base)
{
assert(t != (int64_t)AV_NOPTS_VALUE);
return SongTime::FromMS(av_rescale_q(t, time_base,
(AVRational){1, 1000}));
}
/**
* Convert a FFmpeg time stamp to a #SignedSongTime.
*/
gcc_const
static inline SignedSongTime
FromFfmpegTimeChecked(int64_t t, const AVRational time_base)
{
return t != (int64_t)AV_NOPTS_VALUE
? SignedSongTime(FromFfmpegTime(t, time_base))
: SignedSongTime::Negative();
}
/**
* Convert a #SongTime to a FFmpeg time stamp with the given base.
*/
gcc_const
static inline int64_t
ToFfmpegTime(SongTime t, const AVRational time_base)
{
return av_rescale_q(t.count(),
RatioToAVRational<SongTime::period>(),
time_base);
}
/**
* Replace #AV_NOPTS_VALUE with the given fallback.
*/
static constexpr int64_t
FfmpegTimestampFallback(int64_t t, int64_t fallback)
{
return gcc_likely(t != int64_t(AV_NOPTS_VALUE))
? t
: fallback;
}
#endif

View File

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

View File

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

View File

@@ -218,7 +218,7 @@ PulseMixer::SetVolume(unsigned new_volume, Error &error)
struct pa_cvolume cvolume; struct pa_cvolume cvolume;
pa_cvolume_set(&cvolume, volume.channels, 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); bool success = pulse_output_set_volume(output, &cvolume, error);
if (success) if (success)
volume = cvolume; volume = cvolume;

View File

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

View File

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

View File

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

View File

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

View File

@@ -164,7 +164,9 @@ ShoutOutput::Configure(const config_param &param, Error &error)
} }
} }
const char *encoding = param.GetBlockValue("encoding", "ogg"); const char *encoding = param.GetBlockValue("encoder", nullptr);
if (encoding == nullptr)
encoding = param.GetBlockValue("encoding", "vorbis");
const auto encoder_plugin = shout_encoder_plugin_get(encoding); const auto encoder_plugin = shout_encoder_plugin_get(encoding);
if (encoder_plugin == nullptr) { if (encoder_plugin == nullptr) {
error.Format(config_domain, error.Format(config_domain,
@@ -247,7 +249,6 @@ ShoutOutput::Configure(const config_param &param, Error &error)
{ {
char temp[11]; char temp[11];
memset(temp, 0, sizeof(temp));
snprintf(temp, sizeof(temp), "%u", audio_format.channels); snprintf(temp, sizeof(temp), "%u", audio_format.channels);
shout_set_audio_info(shout_conn, SHOUT_AI_CHANNELS, temp); shout_set_audio_info(shout_conn, SHOUT_AI_CHANNELS, temp);

View File

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

View File

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

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

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "Interleave.hxx"
#include <stdint.h>
#include <string.h>
static void
GenericPcmInterleave(uint8_t *dest, ConstBuffer<const uint8_t *> src,
size_t n_frames, size_t sample_size)
{
for (size_t frame = 0; frame < n_frames; ++frame) {
for (size_t channel = 0; channel < src.size; ++channel) {
memcpy(dest, src[channel] + frame * sample_size,
sample_size);
dest += sample_size;
}
}
}
void
PcmInterleave(void *dest, ConstBuffer<const void *> src,
size_t n_frames, size_t sample_size)
{
GenericPcmInterleave((uint8_t *)dest,
ConstBuffer<const uint8_t *>((const uint8_t *const*)src.data,
src.size),
n_frames, sample_size);
}

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

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_PCM_INTERLEAVE_HXX
#define MPD_PCM_INTERLEAVE_HXX
#include "check.h"
#include "util/ConstBuffer.hxx"
/**
* Interleave planar PCM samples from #src to #dest.
*/
void
PcmInterleave(void *dest, ConstBuffer<const void *> src,
size_t n_frames, size_t sample_size);
#endif

35
src/pcm/Silence.cxx Normal file
View 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
View 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

View File

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

View File

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

View File

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

View File

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

View File

@@ -45,7 +45,7 @@ class CompositeStorage final : public Storage {
*/ */
struct Directory { 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. * "leaf" Directory instances must have a #Storage.
* Other Directory instances may have one, and child * Other Directory instances may have one, and child
* mounts will be "mixed" in. * 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 gcc_pure
FindResult FindStorage(const char *uri) const; FindResult FindStorage(const char *uri) const;
FindResult FindStorage(const char *uri, Error &error) const;
const char *MapToRelativeUTF8(const Directory &directory, const char *MapToRelativeUTF8(const Directory &directory,
const char *uri) const; const char *uri) const;

View File

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

View File

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

View File

@@ -21,7 +21,6 @@
#define MPD_TAG_ITEM_HXX #define MPD_TAG_ITEM_HXX
#include "TagType.h" #include "TagType.h"
#include "Compiler.h"
/** /**
* One tag value. It is a mapping of #TagType to am arbitrary string * 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 * the value of this tag; this is a variable length string
*/ */
char value[sizeof(long) - sizeof(type)]; char value[1];
TagItem() = default; TagItem() = default;
TagItem(const TagItem &other) = delete; TagItem(const TagItem &other) = delete;
TagItem &operator=(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 #endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

@@ -9,5 +9,18 @@ ExecStart=@prefix@/bin/mpd --no-daemon
LimitRTPRIO=50 LimitRTPRIO=50
LimitRTTIME=infinity 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] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
Also=mpd.socket

View File

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