Compare commits

...

67 Commits

Author SHA1 Message Date
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
39 changed files with 602 additions and 249 deletions

@@ -467,6 +467,7 @@ libpcm_a_SOURCES = \
src/pcm/PcmConvert.cxx src/pcm/PcmConvert.hxx \
src/pcm/PcmDop.cxx src/pcm/PcmDop.hxx \
src/pcm/Volume.cxx src/pcm/Volume.hxx \
src/pcm/Silence.cxx src/pcm/Silence.hxx \
src/pcm/PcmMix.cxx src/pcm/PcmMix.hxx \
src/pcm/PcmChannels.cxx src/pcm/PcmChannels.hxx \
src/pcm/PcmPack.cxx src/pcm/PcmPack.hxx \
@@ -2109,7 +2110,9 @@ developer_DATA = $(wildcard doc/developer/*.html)
DOCBOOK_HTML = $(patsubst %.xml,%/index.html,$(DOCBOOK_FILES))
$(DOCBOOK_HTML): %/index.html: %.xml
DOCBOOK_INCLUDES = $(wildcard $(srcdir)/doc/include/*.xml)
$(DOCBOOK_HTML): %/index.html: %.xml $(DOCBOOK_INCLUDES)
$(XMLTO) -o $(@D) --stringparam chunker.output.encoding=utf-8 html --stringparam use.id.as.filename=1 $<
doc/api/html/index.html: doc/doxygen.conf
@@ -2150,8 +2153,9 @@ EXTRA_DIST = $(doc_DATA) autogen.sh \
test/test_archive_bzip2.sh \
test/test_archive_iso9660.sh \
test/test_archive_zzip.sh \
$(wildcard scripts/*.sh) \
$(wildcard $(srcdir)/scripts/*.rb) \
$(man_MANS) $(DOCBOOK_FILES) doc/mpdconf.example doc/doxygen.conf \
$(wildcard $(srcdir)/doc/include/*.xml) \
systemd/mpd.socket \
android/AndroidManifest.xml \
android/build.py \

33
NEWS

@@ -1,3 +1,36 @@
ver 0.19.17 (2016/07/09)
* decoder
- flac: fix assertion failure while seeking
- flac: fix stream duration indicator
- fix seek problems in several plugins
* fix spurious seek error "Failed to allocate silence buffer"
* replay gain: fix "replay_gain_handler mixer" setting
* DSD: use 0x69 as silence pattern
* fix use-after-free bug on "close" and "kill"
ver 0.19.16 (2016/06/13)
* faster seeking
* fix system include path order
* add missing DocBook file to tarball
ver 0.19.15 (2016/04/30)
* decoder
- ffmpeg: support FFmpeg 3.0
- ffmpeg: use as fallback instead of "mad" if no plugin matches
- opus: support bigger OpusTags packets
* fix more build failures on non-glibc builds due to constexpr Mutex
* fix build failure due to missing include
* fix unit test on Alpha
ver 0.19.14 (2016/03/18)
* decoder
- dsdiff: fix off-by-one buffer overflow
- opus: limit tag size to 64 kB
* archive
- iso9660: fix buffer overflow
* fix quadratic runtime bug in the tag pool
* fix build failures on non-glibc builds due to constexpr Mutex
ver 0.19.13 (2016/02/23)
* tags
- aiff, riff: fix ID3 chunk padding

@@ -1,10 +1,10 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.19.13, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.19.17, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
VERSION_MINOR=19
VERSION_REVISION=13
VERSION_REVISION=17
VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx])
@@ -703,12 +703,6 @@ AC_ARG_ENABLE(glib,
if test x$enable_glib = xyes; then
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28 gthread-2.0],,
[AC_MSG_ERROR([GLib 2.28 is required])])
if test x$GCC = xyes; then
# suppress warnings in the GLib headers
GLIB_CFLAGS=`echo $GLIB_CFLAGS |sed -e 's,-I/,-isystem /,g'`
fi
AC_DEFINE(HAVE_GLIB, 1, [Define if GLib is used])
fi
AM_CONDITIONAL(HAVE_GLIB, test x$enable_glib = xyes)

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

154
doc/include/tags.xml Normal file

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

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

@@ -115,7 +115,7 @@
#
# This setting defines a list of tag types that will be extracted during the
# audio file discovery process. The complete list of possible values can be
# found in the mpd.conf man page.
# found in the user manual.
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
#
# This setting enables automatic update of MPD's database when files in

@@ -1,6 +1,7 @@
<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"docbook/dtd/xml/4.2/docbookx.dtd">
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book>
<title>The Music Player Daemon protocol</title>
@@ -201,6 +202,25 @@
omitted, then the maximum possible value is assumed.
</para>
</section>
<section id="tags">
<title>Tags</title>
<para>
The following tags are supported by
<application>MPD</application>:
</para>
<xi:include href="include/tags.xml"
xmlns:xi="http://www.w3.org/2001/XInclude"/>
<para>
There can be multiple values for some of these tags. For
example, <application>MPD</application> may return multiple
lines with a <varname>performer</varname> tag. A tag value is
a UTF-8 string.
</para>
</section>
</chapter>
<chapter id="recipes">

@@ -1,6 +1,7 @@
<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"docbook/dtd/xml/4.2/docbookx.dtd">
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book>
<title>The Music Player Daemon - User's Manual</title>
@@ -16,7 +17,7 @@
<application>MPD</application> (Music Player Daemon) is, as the
name suggests, a server software allowing you to remotely play
your music, handle playlists, deliver music (HTTP streams with
various sub-protocols) and organizze playlists.
various sub-protocols) and organize playlists.
</para>
<para>
@@ -944,6 +945,33 @@ systemctl start mpd.socket</programlisting>
<section id="config_other">
<title>Other Settings</title>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>metadata_to_use</varname>
<parameter>TAG1,TAG2,...</parameter>
</entry>
<entry>
Use only the specified tags, and ignore the others.
This setting can reduce the database size and
<application>MPD</application>'s memory usage by
omitting unused tags. By default, all tags but
<varname>comment</varname> are enabled. The special
value "none" disables all tags.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<section>
<title>The State File</title>
@@ -1297,6 +1325,19 @@ database {
</para>
</section>
<section id="tags">
<title>Metadata</title>
<para>
When scanning or playing a song,
<application>MPD</application> parses its metadata. The
following tags are supported:
</para>
<xi:include href="include/tags.xml"
xmlns:xi="http://www.w3.org/2001/XInclude"/>
</section>
<section id="queue">
<title>The queue</title>

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

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

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

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

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

@@ -255,7 +255,11 @@ decoder_run_stream_fallback(Decoder &decoder, InputStream &is)
{
const struct DecoderPlugin *plugin;
#ifdef HAVE_FFMPEG
plugin = decoder_plugin_from_name("ffmpeg");
#else
plugin = decoder_plugin_from_name("mad");
#endif
return plugin != nullptr && plugin->stream_decode != nullptr &&
decoder_stream_decode(*plugin, decoder, is);
}

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

@@ -199,10 +199,10 @@ ffmpeg_init(gcc_unused const config_param &param)
}
static int
ffmpeg_find_audio_stream(const AVFormatContext *format_context)
ffmpeg_find_audio_stream(const AVFormatContext &format_context)
{
for (unsigned i = 0; i < format_context->nb_streams; ++i)
if (format_context->streams[i]->codec->codec_type ==
for (unsigned i = 0; i < format_context.nb_streams; ++i)
if (format_context.streams[i]->codec->codec_type ==
AVMEDIA_TYPE_AUDIO)
return i;
@@ -276,22 +276,22 @@ copy_interleave_frame2(uint8_t *dest, uint8_t **src,
* Copy PCM data from a AVFrame to an interleaved buffer.
*/
static int
copy_interleave_frame(const AVCodecContext *codec_context,
const AVFrame *frame,
copy_interleave_frame(const AVCodecContext &codec_context,
const AVFrame &frame,
uint8_t **output_buffer,
uint8_t **global_buffer, int *global_buffer_size)
{
int plane_size;
const int data_size =
av_samples_get_buffer_size(&plane_size,
codec_context->channels,
frame->nb_samples,
codec_context->sample_fmt, 1);
codec_context.channels,
frame.nb_samples,
codec_context.sample_fmt, 1);
if (data_size <= 0)
return data_size;
if (av_sample_fmt_is_planar(codec_context->sample_fmt) &&
codec_context->channels > 1) {
if (av_sample_fmt_is_planar(codec_context.sample_fmt) &&
codec_context.channels > 1) {
if(*global_buffer_size < data_size) {
av_freep(global_buffer);
@@ -303,12 +303,12 @@ copy_interleave_frame(const AVCodecContext *codec_context,
*global_buffer_size = data_size;
}
*output_buffer = *global_buffer;
copy_interleave_frame2(*output_buffer, frame->extended_data,
frame->nb_samples,
codec_context->channels,
av_get_bytes_per_sample(codec_context->sample_fmt));
copy_interleave_frame2(*output_buffer, frame.extended_data,
frame.nb_samples,
codec_context.channels,
av_get_bytes_per_sample(codec_context.sample_fmt));
} else {
*output_buffer = frame->extended_data[0];
*output_buffer = frame.extended_data[0];
}
return data_size;
@@ -349,41 +349,39 @@ PtsToPcmFrame(uint64_t pts, const AVStream &stream,
*/
static DecoderCommand
ffmpeg_send_packet(Decoder &decoder, InputStream &is,
const AVPacket *packet,
AVCodecContext *codec_context,
const AVStream *stream,
AVPacket &&packet,
AVCodecContext &codec_context,
const AVStream &stream,
AVFrame *frame,
uint64_t min_frame, size_t pcm_frame_size,
uint8_t **buffer, int *buffer_size)
{
size_t skip_bytes = 0;
const auto pts = StreamRelativePts(*packet, *stream);
const auto pts = StreamRelativePts(packet, stream);
if (pts >= 0) {
if (min_frame > 0) {
auto cur_frame = PtsToPcmFrame(pts, *stream,
*codec_context);
auto cur_frame = PtsToPcmFrame(pts, stream,
codec_context);
if (cur_frame < min_frame)
skip_bytes = pcm_frame_size * (min_frame - cur_frame);
} else
decoder_timestamp(decoder,
time_from_ffmpeg(pts, stream->time_base));
time_from_ffmpeg(pts, stream.time_base));
}
AVPacket packet2 = *packet;
uint8_t *output_buffer;
DecoderCommand cmd = DecoderCommand::NONE;
while (packet2.size > 0 && cmd == DecoderCommand::NONE) {
while (packet.size > 0 && cmd == DecoderCommand::NONE) {
int audio_size = 0;
int got_frame = 0;
int len = avcodec_decode_audio4(codec_context,
int len = avcodec_decode_audio4(&codec_context,
frame, &got_frame,
&packet2);
&packet);
if (len >= 0 && got_frame) {
audio_size = copy_interleave_frame(codec_context,
frame,
*frame,
&output_buffer,
buffer, buffer_size);
if (audio_size < 0)
@@ -397,8 +395,8 @@ ffmpeg_send_packet(Decoder &decoder, InputStream &is,
break;
}
packet2.data += len;
packet2.size -= len;
packet.data += len;
packet.size -= len;
if (audio_size <= 0)
continue;
@@ -417,7 +415,7 @@ ffmpeg_send_packet(Decoder &decoder, InputStream &is,
cmd = decoder_data(decoder, is,
data, audio_size,
codec_context->bit_rate / 1000);
codec_context.bit_rate / 1000);
}
return cmd;
}
@@ -535,7 +533,7 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
return;
}
int audio_stream = ffmpeg_find_audio_stream(format_context);
int audio_stream = ffmpeg_find_audio_stream(*format_context);
if (audio_stream == -1) {
LogError(ffmpeg_domain, "No audio stream inside");
avformat_close_input(&format_context);
@@ -631,8 +629,9 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
if (packet.stream_index == audio_stream) {
cmd = ffmpeg_send_packet(decoder, input,
&packet, codec_context,
av_stream,
std::move(packet),
*codec_context,
*av_stream,
frame,
min_frame, audio_format.GetFrameSize(),
&interleaved_buffer, &interleaved_buffer_size);
@@ -640,7 +639,11 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
} else
cmd = decoder_get_command(decoder);
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 25, 100)
av_packet_unref(&packet);
#else
av_free_packet(&packet);
#endif
if (cmd == DecoderCommand::SEEK) {
int64_t where =
@@ -709,7 +712,7 @@ ffmpeg_scan_stream(InputStream &is,
}
ffmpeg_scan_dictionary(f->metadata, handler, handler_ctx);
int idx = ffmpeg_find_audio_stream(f);
int idx = ffmpeg_find_audio_stream(*f);
if (idx >= 0)
ffmpeg_scan_dictionary(f->streams[idx]->metadata,
handler, handler_ctx);

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

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

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

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

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

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

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

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

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

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

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

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

@@ -247,7 +247,6 @@ ShoutOutput::Configure(const config_param &param, Error &error)
{
char temp[11];
memset(temp, 0, sizeof(temp));
snprintf(temp, sizeof(temp), "%u", audio_format.channels);
shout_set_audio_info(shout_conn, SHOUT_AI_CHANNELS, temp);

35
src/pcm/Silence.cxx Normal file

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

36
src/pcm/Silence.hxx Normal file

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2003-2016 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_PCM_SILENCE_HXX
#define MPD_PCM_SILENCE_HXX
#include "check.h"
#include <stdint.h>
template<typename T> struct WritableBuffer;
enum class SampleFormat : uint8_t;
/**
* Fill the given buffer with the format-specific silence pattern.
*/
void
PcmSilence(WritableBuffer<void> dest, SampleFormat format);
#endif

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

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

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

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

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

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

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