Compare commits

...

247 Commits

Author SHA1 Message Date
Max Kellermann
ab197b6d43 release v0.20.20 2018-05-22 12:40:18 +02:00
Max Kellermann
16b0e53a36 android/AndroidManifest.xml: increment version number to 0.20.20 2018-05-22 12:40:11 +02:00
Max Kellermann
bc14a6038e Makefile.am: invoke javac with source/target 1.6
Fixes:

```
error: Source option 5 is no longer supported. Use 6 or later.
error: Target option 1.5 is no longer supported. Use 1.6 or later.
```
2018-05-22 12:37:01 +02:00
Max Kellermann
626329a1cc python/build/meson.py: create build_path if it does not exist 2018-05-22 12:31:37 +02:00
Max Kellermann
8bf250c228 python/build/libs: upgrade CURL to 7.60.0 2018-05-22 11:17:19 +02:00
Max Kellermann
62127bbb12 python/build/libs.py: add libmpdclient 2018-05-12 16:24:24 +02:00
Max Kellermann
786ac87b76 python/build: add support for Meson/ninja based projects 2018-05-12 15:00:17 +02:00
Max Kellermann
c76f4ac89b player/Thread: pause all outputs in single mode
This mostly affects the Pulse output plugin which needs to "cork" the
stream (closes ).
2018-05-12 14:44:07 +02:00
Michal Koutenský
d495ec71a8 decoder/opus: add support for R128_ALBUM_GAIN tag 2018-05-07 10:57:03 +02:00
Max Kellermann
b763852f57 decoder/dsd: allow 4 MB ID3 tags
Closes 
2018-05-07 10:53:48 +02:00
Max Kellermann
6522d2f722 decoder/{dsdiff,dsf}: support more MIME types
These are used by DSD-streaming servers.  For example, MiniDLNA uses
"audio/x-dsd".
2018-05-03 12:02:11 +02:00
Max Kellermann
ac61fd1d78 {input,output}/alsa: work around -Wswitch due to SND_PCM_STATE_PRIVATE1 2018-05-03 11:59:18 +02:00
Max Kellermann
c44d1566fa SongFilter: fix "modified-since" filter
Error message sent to client was "basic_string::_M_construct null not
valid" due to passing nullptr to the std::string constructor.

Regression caused by commit 386688b87a
2018-04-30 20:34:25 +02:00
Max Kellermann
80dc7c2f74 increment version number to 0.20.20 2018-04-30 20:32:36 +02:00
Max Kellermann
7b94f0e36b release v0.20.19 2018-04-26 19:57:04 +02:00
Max Kellermann
504e8d564a android/AndroidManifest.xml: increment version number to 0.20.19 2018-04-26 19:56:39 +02:00
Max Kellermann
ac395429c3 db/proxy: implement the group_mask parameter in VisitUniqueTags()
Closes 
2018-04-26 19:43:33 +02:00
Max Kellermann
388768b3a6 db/proxy: call mpd_search_cancel() after search error
Fixes "search already in progress" errors.
2018-04-26 19:41:19 +02:00
Max Kellermann
5c4169e64e python/build/libs.py: upgrade FFmpeg to 4.0 2018-04-26 19:16:16 +02:00
Max Kellermann
d40e9de2d2 python/build/libs.py: upgrade libvorbis to 1.3.6 2018-04-26 19:14:26 +02:00
Max Kellermann
1e54297be8 lib/ffmpeg/Init: fix av_register_all() deprecation warning
av_register_all() was deprecated in
FFmpeg/FFmpeg@0694d87024
2018-04-25 21:35:33 +02:00
Max Kellermann
44b200240f player/Thread: never reuse decoder when switching radio streams
When switching to another song manually, the player checks if the
decoder is already decoding that song; if so, it will attempt to reuse
it by seeking it to the new position.  That however fails if the
decoder is not seekable (e.g. a radio stream) which leaves the user
unable to switch to that song with the bogus error message "Not
seekable".
2018-04-25 21:19:26 +02:00
Max Kellermann
a2340c313f pcm/PcmDop: round down to the nearest multiple of 4 DSD bytes
There was a discrepancy between what was written to the buffer and the
size returned by pcm_dsd_to_dop(): the "for" loop uses num_frames/2,
rounding down, while the return value is num_samples which is
num_frames*channels, without rounding.  This could cause undefined
data at the end of the destination buffer if the source buffer size
was not aligned to multiples of 8 bytes (4 DSD bytes per channel).

The latter however can occur in the 0.21 branch after commit
a06bf388d9

Closes 
2018-03-15 20:02:00 +01:00
Max Kellermann
37b07a5e7c pcm/PcmDop: use size_t 2018-03-15 20:00:14 +01:00
Max Kellermann
73013a3c04 input/thread: move code to Stop()
Fixes crash due to "pure virtual method called" in the "mms" input
plugin.  Closes 
2018-03-15 19:29:55 +01:00
Max Kellermann
e8099f01b5 python/build/libs: upgrade CURL to 7.59.0 2018-03-15 11:24:50 +01:00
Max Kellermann
672bdd3a56 doc/user.xml: clarify where mpd.conf is read from on Android
Closes 
2018-03-15 11:22:38 +01:00
Max Kellermann
c2c2c29658 input/thread: set InputStream::ready after Open() failure
Without setting the "ready" flag, the caller will wait in WaitReady()
forever, locking up MPD.  Closes 
2018-03-14 13:15:03 +01:00
Michal Smucr
c745e14f47 Bump minimum required version of Boost to 1.54.
lockfree library used by ALSA output plugin is part of Boost from version 1.53,
so this can be theoretically the lowest required version, however
there are issues which are resolved from 1.54 onwards.
2018-03-09 09:23:48 +01:00
Max Kellermann
e8f08cda53 AUTHORS: add various recent contributors 2018-03-05 19:23:36 +01:00
Max Kellermann
8266ab5588 android/build.py: support the x86 ABI
First commit for issue 
2018-03-04 20:46:46 +01:00
Max Kellermann
ea552208fc android/build.py: add ABI parameter 2018-03-04 20:43:59 +01:00
Max Kellermann
e86015a72a android/build.py: convert ndk_arch to local variable 2018-03-04 20:32:50 +01:00
Max Kellermann
cf7ec2c9d3 doc/user.xml: add section about compiling for Android 2018-03-04 20:19:22 +01:00
Max Kellermann
dadd3ca671 protocol/ArgParser: disallow negative seek times
Instead of stopping playback (due to seek time overflow), reject the
seek command.  Closes 

Relative negative values (with "seekcur") are still allowed, and MPD
will fix the resulting position if it turns out to be negative.  But
the "seek" and "seekid" commands use an unsigned time stamp which must
not be negative.
2018-03-04 11:46:11 +01:00
Christian Kröner
79535212c8 Get rid of GCD on macOS which breaks debug builds
With Grand Central Dispatch used in Main.cxx, debug builds on macOS
crash as the IsInside() assertion gets triggered in the event loop. As
a simple fix, usage of GCD is removed. Plugging and unplugging
headphones or changes of the default output device was tested without
issues. Whatever the original commit tried to fix by GCD probably does
not need fixing anymore.
2018-03-04 10:43:55 +01:00
Max Kellermann
ef5f96a193 increment version number to 0.20.19 2018-03-04 10:42:05 +01:00
Max Kellermann
418f71ec0f net/Init: work around -Werror=unused-variable 2018-02-24 23:17:36 +01:00
Max Kellermann
0ebeaa9ac2 release v0.20.18 2018-02-24 22:55:06 +01:00
Max Kellermann
25cd47b8dc win32/build.py: enable libnfs
Now that all build failures have been fixed, we can enable the
feature.
2018-02-24 22:44:42 +01:00
Max Kellermann
cd48d981b5 storage/nfs: use PathTraitsFS::const_pointer_type 2018-02-24 22:44:42 +01:00
Max Kellermann
774d26b982 storage/nfs: assume UTF-8 when accessing NFS from Windows
Fixes two build failures with libnfs on Windows.
2018-02-24 22:44:42 +01:00
Max Kellermann
f3e683bd6f test/run_storage: fallback for %F on Windows 2018-02-24 22:44:42 +01:00
Max Kellermann
50ce0c0d9d test/run_storage: initialize WinSock 2018-02-24 22:44:34 +01:00
Max Kellermann
5b80711d75 Main: move WinSock initialization to class ScopeNetInit 2018-02-24 22:44:27 +01:00
Max Kellermann
666e456551 win32/build.py: add -DWINVER=0x0600 -D_WIN32_WINNT=0x0600
configure.ac sets this, but this wasn't used for compiling third-party
libraries.  This setting however is important for libnfs, which adds
fallback definitions for POLLIN and POLLOUT with bogus values.
2018-02-24 22:44:11 +01:00
Max Kellermann
31794ac376 lib/nfs/FileReader: move sys/stat.h to header because "struct stat" may be macro
It indeed is a macro on Windows.
2018-02-24 22:03:38 +01:00
Max Kellermann
2141fdf06e lib/nfs/Connection: use winsock2.h instead of poll.h on Windows 2018-02-24 22:02:42 +01:00
tpoeiras
3f3e0739c4 Fix curl storage plugin failure if the authentication method is different than basic. 2018-02-24 21:59:13 +01:00
Max Kellermann
ebed7e2147 playlist/cue/Parser: parse tags after "INDEX 01"
Instead of setting state=IGNORE_TRACK, ignore only the following
"INDEX" lines.

Correction for commit 8461d71b52.  Closes 
2018-02-24 21:29:16 +01:00
Max Kellermann
53f5d4c710 android/build.py: disable libmad
Let FFmpeg do the MP3 decoding.  See commit
a4de96508d
2018-02-24 10:52:40 +01:00
Max Kellermann
139a4054c5 python/build/libs.py: remove duplicate FFmpeg option and fix typo
Closes 
2018-02-24 10:52:09 +01:00
Max Kellermann
a4de96508d python/build/libs.py: re-enable FFmpeg MP3 decoder
libmad has been unmaintained for a long time, and it fails to build on
Windows.  I could go and fix libmad's broken configure script, but I
prefer to just assign MP3 decoding to FFmpeg for now.

Closes 
2018-02-24 10:49:05 +01:00
Max Kellermann
a7582aaf15 python/build/libs.py: update FFmpeg to 3.4.2 2018-02-24 10:47:46 +01:00
Max Kellermann
c5c1c64a81 python/build/libs.py: add libnfs
Enable the NFS storage plugin on Android.

Closes 
2018-02-20 22:47:17 +01:00
Max Kellermann
992c52ce7f python/build/autotools.py: add autoreconf support 2018-02-20 22:46:54 +01:00
Max Kellermann
026aef7465 decoder/flac: move the SubmitData() call out of the callback
This addresses two problems:

1. the libFLAC write callback had to send an error status to its
caller when SubmitData() returned a command; this disrupted libFLAC
and the resulting command could not be used for anything;

2. the libFLAC function FLAC__stream_decoder_seek_absolute() also
calls the write callback, but its result cannot be used, because
seeking is still in progress, so we lose all data from one FLAC frame.
By moving the SubmitData() call until after CommandFinished(), we
avoid losing this data.  This fixes another part of 
2018-02-17 13:33:53 +01:00
Max Kellermann
b53a23b51b decoder/flac: call FlacSubmitToClient() again after seeking
See code comment.
2018-02-17 13:33:51 +01:00
Max Kellermann
2aad015392 decoder/flac: move code to FlacSubmitToClient() 2018-02-17 13:33:48 +01:00
Max Kellermann
986ec877b0 decoder/Bridge: truncate last chunk at the exact end_time
Instead of passing whole chunks to the MusicPipe and checking the
end_time after each chunk, truncate the last chunk if it would exceed
the end_time.  This requires keeping track of the absolute PCM frame
number.

This fixes a problem with gapless CUE song transitions: a small part
of the following song was always played twice.

Closes 
2018-02-17 13:10:00 +01:00
cathugger
c43ea74b30 encoder/opus: initialize granulepos to 0
it was uninitialized before
2018-02-17 01:22:17 +01:00
Max Kellermann
79981f3cda increment version number to 0.20.18 2018-02-17 01:21:46 +01:00
Max Kellermann
c2940a8385 release v0.20.17 2018-02-11 13:02:53 +01:00
Max Kellermann
bede564618 mixer/alsa: work around rounding error at volume 0
Due to rounding errors, a slightly negative value can be passed to
set_normalized_volume(), which will make the log10() call fail.
Actually, volume 0 is already failing because log10(0) is illegal.  So
let's fix this by implementing two corner cases: <=0 and >=100.

Closes 
2018-02-10 09:07:51 +01:00
Max Kellermann
e0ca4b865a android: require SDK version 14
Closes .
2018-02-10 00:03:23 +01:00
Max Kellermann
31c206bf80 android/build.py: add -mfpu=vfp, explicitly disabling NEON
Apparently, clang defaults to NEON when ARMv7 is used.  Not all ARMv7
CPUs we target have NEON, so we need to disable that.
2018-02-10 00:00:57 +01:00
Max Kellermann
9187a08106 lib/curl: remove .netrc support on Android
Not needed on Android, and the implementation uses getpwuid_r() which
is unavailable on old Android versions.
2018-02-09 23:14:29 +01:00
Max Kellermann
3859a50466 python/build/libs.py: convert CURL edit to quilt patch 2018-02-09 23:14:27 +01:00
Max Kellermann
927071e085 python/build/project.py: add quilt support 2018-02-09 22:59:12 +01:00
Max Kellermann
6ba918b203 input/file: don't use posix_fadvise() on Android
Requires Android API 21, but we want to support older versions as
well.
2018-02-09 22:54:22 +01:00
Max Kellermann
e8b70dbca4 SongSave, queue/PlaylistState, tag/ReplayGain: use portable atof() wrappers
For Android pre-5.0 compatibility ().
2018-02-09 22:54:22 +01:00
Max Kellermann
0f8d223c7f protocol/ArgParser: move strtof()/strtod() switch to util/NumberParser.hxx 2018-02-09 22:54:22 +01:00
Max Kellermann
19a2885fd5 protocol/ArgParser: use strtod() instead of strtof() on Android
For Android pre-5.0 compatibility ().
2018-02-09 22:54:22 +01:00
Max Kellermann
b8a094470b python/build/libs.py: build only the library 2018-02-09 22:54:22 +01:00
Max Kellermann
2988bb77e8 python/build/project: allow trailing digit after letter in version number
For version numbers such as OpenSSH's, e.g.: "7.2p2"
2018-02-09 22:54:22 +01:00
Max Kellermann
738317bf34 doc/user: document MPD on Android
Closes 
2018-02-09 19:11:39 +01:00
Max Kellermann
e46fbd0780 filter/convert: set the PcmConvert instance only if it was initialized
Fixes valgrind warning.
2018-02-09 19:05:45 +01:00
Max Kellermann
56b74ad990 filter/convert: add method IsActive() 2018-02-09 19:04:45 +01:00
Max Kellermann
6de92bb42b pcm/Order: fix size calculation with 8 channels
This was a buffer overflow bug which could cause MPD crahes when
playing back 8 channels with the ALSA output plugin.

Closes 
2018-02-09 19:01:12 +01:00
Max Kellermann
c801936e53 db/update/Service: set the update thread name 2018-02-09 18:48:14 +01:00
Max Kellermann
817656504d thread/Util: implement system call wrapper for sched_setscheduler()
There is a POSIX definition for sched_setscheduler(), but Linux does
not implement that; instead of changing the process's scheduler, it
only affects one thread.  This has caused some confusion among
application developers and C library developers.

While glibc implements Linux semantics, Musl has made their
sched_setscheduler() function an always-failing no-op, causing the
error message "sched_setscheduler failed: Function not implemented".

 http://git.musl-libc.org/cgit/musl/commit/src/sched/sched_setscheduler.c?id=1e21e78bf7a5c24c217446d8760be7b7188711c2

Instead of relying on the C library which may be unreliable here, we
now roll our own system call wrapper.

Closes 
2018-02-09 18:43:45 +01:00
Max Kellermann
6f00f97b66 thread/Util: rename ioprio_set() to linux_ioprio_set()
Juse in cas glibc gets a wrapper for the system call which would then
conflict with ours.
2018-02-09 18:43:45 +01:00
Max Kellermann
5acb978f8f increment version number to 0.20.17 2018-02-09 18:43:45 +01:00
Max Kellermann
975a4ae871 release v0.20.16 2018-02-03 19:55:07 +01:00
Max Kellermann
56aaf3c73e python/build/libs: upgrade CURL to 7.58.0 2018-02-03 19:46:31 +01:00
Max Kellermann
12fd1cad0c archive/iso9660: libcdio 2.0 compatibility
Closes 
2018-02-03 19:32:31 +01:00
Max Kellermann
e573cbf032 db/update/Queue: work around GCC7 -Wuninitialized 2018-02-01 19:53:42 +01:00
Max Kellermann
dead461542 lib/upnp/Init: enable IPv6 2018-01-31 18:15:46 +01:00
Max Kellermann
3d5da1ac73 lib/upnp/Init: use nullptr instead of 0 2018-01-31 18:14:26 +01:00
Max Kellermann
ec408ca6a6 output/pulse: fix crash during auto-detection
The PulseOutput needs to be "enabled" before WaitConnection() may be
called.

Closes 
2018-01-30 10:06:36 +01:00
Max Kellermann
ea66cdd6a5 test/read_mixer: another kludge to work around -Wnull-dereference 2018-01-23 16:42:25 +01:00
Max Kellermann
f762e8034f test/NullMixerListener: new class to fix -Wnull-dereference 2018-01-23 16:28:56 +01:00
Max Kellermann
bb1e369f30 playlist/SoundCloud: fix -Wunused-lambda-capture 2018-01-23 09:57:52 +01:00
Max Kellermann
8376578921 db/simple/Mount: drop mount point prefix from LOCATE_TAG_BASE_TYPE
Fixes search within mount points, resulting in error "No such
directory".

Closes 
2018-01-19 23:52:57 +01:00
Max Kellermann
ed2354cd9d SongFilter: allow copying items 2018-01-19 23:52:03 +01:00
Max Kellermann
386688b87a SongFilter: use std::string instead of AllocatedString 2018-01-19 23:51:42 +01:00
Max Kellermann
38d56dddf1 lib/icu/Compare: allow copying 2018-01-19 23:49:50 +01:00
Max Kellermann
e8975942ec Makefile.am: link libicu.a before libutil.a
libicu.a depends on libutil.a.
2018-01-19 23:38:24 +01:00
Max Kellermann
3ca80a7336 util/RefCount, db/simple/Mount: remove obsolete libc++ workarounds
No longer a problem with NDK r16.
2018-01-19 23:19:46 +01:00
Max Kellermann
d029dae7ad Makefile.am: use Android SDK build-tools 27.0.0 2018-01-19 23:04:54 +01:00
Max Kellermann
9e058732ee android/build.py: add -fpic
Android native code should be position-independent.

The NDK build scripts use "-fpic" instead of "-fPIC" for ARM, but that
doesn't work with FFmpeg's assembly code, because it requires
R_ARM_MOVW_ABS_NC which is unavailable with "-fpic".
2018-01-19 22:40:59 +01:00
Max Kellermann
cad5d11261 android/build.py: simplify libc++ flags
By telling clang which implementation to use, we avoid the dependency
on libstdc++.so.
2018-01-19 22:36:19 +01:00
Max Kellermann
fcaedec2ab {android,win32}/build.py: move "-O* -g" to common_flags 2018-01-19 12:33:28 +01:00
Max Kellermann
ead9d59e88 python/build/libs.py: build only libFLAC, no programs 2018-01-19 12:33:03 +01:00
Max Kellermann
34b8a17ccd python/build/autotools.py: add "subdir" parameter 2018-01-19 11:39:36 +01:00
Max Kellermann
a53d081c39 python/build/libs.py: disable libFLAC API documentation 2018-01-19 11:38:24 +01:00
Max Kellermann
823134e4ba python/build/libs.py: disable Opus documentation and extra programs 2018-01-19 11:32:40 +01:00
Max Kellermann
272167b4fc python/build/libs.py: update LAME to 3.100 2018-01-18 22:07:28 +01:00
Max Kellermann
92f09bba94 Makefile.am: rename JAVA_SOURCES to JAVA_SOURCE_PATHS
Work around automake warning:

    Makefile.am:310: warning: variable 'JAVA_SOURCES' is defined but no program or
    Makefile.am:310: library has 'JAVA' as canonical name (possible typo)

Closes 
2018-01-18 22:05:04 +01:00
Max Kellermann
1f50bdb230 event/Loop: use std::atomic_bool for the "quit" variable
Fixes thread sanitizer warnings.
2018-01-08 10:06:23 +01:00
Max Kellermann
2eef4e6716 thread/Thread: add debug attribute "inside_handle"
This attribute shall be used only for IsInside() to make this safe
against a race condition described in :

> There is no requirement on the implementation that the ID of the
> created thread be available before the newly created thread starts
> executing.

http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_create.html):

This means that on some pthread implementations (e.g. Haiku), the
assert(thread.IsInside()) could fail.

Closes 
2018-01-08 09:58:18 +01:00
Max Kellermann
d989dbfec4 thread/Thread: make IsInside() debug-only
This method is only used inside assert().
2018-01-08 09:56:39 +01:00
Max Kellermann
ca9fcec364 thread/Thread: fix indent 2018-01-08 09:49:08 +01:00
Max Kellermann
354104f9a9 thread/{Thread,Id}: use defaul-initialized pthread_t as "undefined" value
Use the "==" operator instead of pthread_equal().

This allows us to eliminate two boolean flags which are there to avoid
race conditions, and made the thing so fragile that I got tons of
(correct) thread sanitizer warnings.
2018-01-07 17:20:26 +01:00
Max Kellermann
8649ea3d6f thread/Thread: use BoundMethod 2018-01-07 17:20:26 +01:00
Max Kellermann
752ff12c37 thread/Thread: move code to Run() 2018-01-07 17:20:26 +01:00
Max Kellermann
4bb89b1755 MusicPipe: lock the mutex in Peek() and GetSize() 2018-01-07 17:20:22 +01:00
Max Kellermann
0ef553d30e increment version number to 0.20.16 2018-01-06 13:15:47 +01:00
Max Kellermann
43a62aef07 android: release 0.20.15 2018-01-05 18:09:56 +01:00
Max Kellermann
ed4d0aa909 release v0.20.15 2018-01-05 17:55:25 +01:00
Max Kellermann
023ce4e720 python/build/libs.py: disable even more FFmpeg modules 2018-01-05 17:41:58 +01:00
Max Kellermann
368d9359dd python/build/libs.py: update libogg to 1.3.3 2018-01-05 17:41:58 +01:00
Max Kellermann
d98c19d561 python/build/libs.py: disable more FFmpeg modules 2018-01-05 16:49:28 +01:00
Max Kellermann
cab77e35e0 queue/PlaylistControl: fix crash after seek failure
This completes the bug fix commit
2065e3290452377b2931f3129b230c8cc536cbc8; if we clear "queued" then we
must clear "queued_song" as well, or another variant of the assertion
fails.
2018-01-05 13:00:24 +01:00
Max Kellermann
e3e90b4b93 python/build/libs.py: disable libcurl SMB support
MPD doesn't use this feature anyway.
2018-01-05 12:00:29 +01:00
Max Kellermann
f8c69893e1 python/build/libs.py: disable lots of useless FFmpeg features 2018-01-05 11:11:20 +01:00
Max Kellermann
49678a0893 python/build/libs.py: suppress "visibility default" in libopus build 2018-01-05 10:33:53 +01:00
Max Kellermann
d667b5b48c python/build/libs.py: work around libid3tag CFLAGS bug 2018-01-05 10:25:16 +01:00
Max Kellermann
9cba55b39c python/build/project.py: add "edits" parameter to edit source files 2018-01-05 10:06:22 +01:00
Max Kellermann
c2cbb7b8ce output/haiku: remove unimplemented Cancel() method 2018-01-05 10:05:14 +01:00
Felix Hädicke
8217d75ca1 build/python: refactoring: introduce new class MakeProject
This introduces a the new class MakeProject, which is used as a base
class for all Makefile based thirdparty libraries.
2018-01-05 08:17:17 +01:00
Max Kellermann
1ca70d9759 build/python/autotools: add properties "ldflags", "libs", "install_target" 2018-01-05 08:17:15 +01:00
Felix Hädicke
4303aaa9b8 build/python: use "glibtoolize", not "libtoolize" when compiling on OS X
On OS X, the "libtoolize" command is some Apple tool. The libtoolize
we want is named "glibtoolize" in Homebrew.
2018-01-05 07:55:59 +01:00
Max Kellermann
7b56bae289 python/build/libs.py: pass --disable-debugging to libid3tag and libmad 2018-01-05 07:30:09 +01:00
Max Kellermann
4183416b3e python/build/libs.py: reindent arrays 2018-01-05 07:27:09 +01:00
Max Kellermann
a60dee57ce python/libs: upgrade Boost to 1.66.0 2018-01-05 07:16:38 +01:00
Max Kellermann
5724656acb android/build.py: enable function/data sections in static libraries
.. and make all library symbols hidden by default.

Saves big amounts of .text section size with --gc-sections, because
only this allows discarding unused functions from those (static)
third-party libraries.
2018-01-04 23:33:07 +01:00
Max Kellermann
329f9cd9fe thread/Util: no ioprio_set() on Android due to seccomp/SIGSYS 2018-01-04 19:33:14 +01:00
Felix Hädicke
fbdb8b406e Makefile.am: build Android APK package without Ant
In current Android SDK releases, Ant support was removed. Move the
necessary build steps from the former Ant build system to our Makefile,
and call the required build tools from the Android SDK (aapt and dx),
Java SDK (javac) and Info-ZIP (zip) directly.

[mk: copied from Felix's commit
e52b906dba971a1173f9e8f83d32b52ee9f89af3 in the XCSoar project)
2018-01-04 18:55:54 +01:00
Max Kellermann
85d0bbd957 Makefile.am: add variable ZIPALIGN 2018-01-04 18:55:45 +01:00
Max Kellermann
414f00d6ae Makefile.am: add variable ANDROID_SDK_PLATFORM 2018-01-04 18:47:52 +01:00
Max Kellermann
17b0add058 filter/Observer: pass Reset() to underlying Filter
Wohooooo, the method Filter::Reset() has been broken because no
implementation of it has ever been called for a loooong time.
And nobody ever noticed it.  WTF.
2018-01-02 22:13:14 +01:00
Max Kellermann
c68ed40661 pcm/SoxrResampler: implement method Reset() 2018-01-02 21:53:09 +01:00
Max Kellermann
ff624075a8 storage/State: check if a CompositeStorage exists; fixes nullptr dereference
Fixes another crash bug caused by commit
64d141f71e
2018-01-02 14:13:26 +01:00
Max Kellermann
08db28469d storage/State: make mount errors non-fatal
Fixes crash bug caused by commit
64d141f71e
2018-01-02 14:07:52 +01:00
Max Kellermann
a20b326807 storage/State: fix memory leak after database mount failure
Caused by commit 64d141f71e

This wasn't a serious memory leak, because after a mount failure, MPD
would abort anyway, which is subject to the next commit.
2018-01-02 14:05:07 +01:00
Max Kellermann
4db1b1b250 storage/State: remove useless #ifdef ENABLE_DATABASE
This source file isn't compiled when the database is disabled.
2018-01-02 13:48:16 +01:00
Max Kellermann
ff6b263b48 increment version number to 0.20.15 2018-01-02 13:46:03 +01:00
Max Kellermann
c0bf052fa9 release v0.20.14 2018-01-01 17:55:38 +01:00
loujine
5419cff925 [doc] Fix outdated MusicBrainz URLs (closes ) 2018-01-01 17:30:24 +01:00
Max Kellermann
eee10ad2ed input/curl: add missing mutex locks to OnEnd(), OnError() 2017-12-26 20:01:13 +01:00
Max Kellermann
98472a8104 pcm/SampleFormat: remove wrong "malloc" attribute 2017-12-23 08:38:22 +01:00
Max Kellermann
d094c168aa archive/{iso9660,zzip}: unlock the mutex during I/O
Similar to commit 31ab78ae8e
2017-12-22 16:09:03 +01:00
Max Kellermann
4b18460bc6 archive/bz2: unlock the archive mutex and lock the file mutex
Fixes deadlock because FileInputStream::Read() unlocks the mutex
(which was not locked) and then locks it, keeping it locked.  This can
result in a deadlock.  This happens because the archive and the file
mutex are different.
2017-12-22 16:02:23 +01:00
Max Kellermann
412c0a965c util/WStringAPI: fix indent 2017-12-21 18:45:26 +01:00
Ilya ilyxa Tyshchenko
2becf79223 correct action for compile on Solaris 11.3 X86 2017-12-21 18:42:36 +01:00
Max Kellermann
43ec96d4a0 command/Error: translate std::{length_error,out_of_range} to ACK_ERROR_ARG 2017-12-21 10:22:04 +01:00
Max Kellermann
3d1d779da7 storage/State: use std::set instead of sorting a std::list 2017-12-21 10:22:00 +01:00
Max Kellermann
c88056ba83 db/simple: fix file corruption in the presence of mount points
If a directory is a mount point, omit the "directory: " as well.

This bug is years old, but has become more visible now that mount
points are persistent in the state file.
2017-12-21 10:16:52 +01:00
Max Kellermann
e769751221 increment version number to 0.20.14 2017-12-21 10:15:16 +01:00
Max Kellermann
49b9a90c3f release v0.20.13 2017-12-18 23:41:56 +01:00
FlashSystems
64d141f71e Save and restore mountpoints within the state file.
Signed-off-by: FlashSystems <developer@flashsystems.de>
2017-12-18 23:39:01 +01:00
FlashSystems
c488d3123f Fix lsinfo and add for mounted databases.
If `SimpleDatabase::Visit` is called on a database that contains a mounted directry the URIs of the elements passed to the callbacks are not prefixed by the mountpoint path. This leads to lsinfo and add not working because they use the wrong URI. This pull request is using the `WalkMount` helper function to create prefixed versions of `VisitDirectory`, `VisitSong` and `VisitPlaylist` to add the correct prefix to the parameters of the callback functions.
2017-12-18 23:33:08 +01:00
Stefano Miccoli
967af60327 rounds alsa HW mixer volume towards ±∞ depending on sgn(∆ vol)
This alleviates a problem in which 'volume +1' cannot be undo by
'volume -1' when using alsa hw mixer.

Closes 
2017-12-18 21:29:03 +01:00
Yue Wang
f1ef9f9d31 OSXOutputPlugin: set the buffer time to be 100ms
[mk: the following text was copied from
https://github.com/MusicPlayerDaemon/MPD/pull/167]

For certain format (hi-res files) and normal buffer size hardware, The
hardware may at once consume most of the buffers. However, in Delay()
function, MPD is supposed to wait for 25 ms after the next try. it
will create a hiccup. The negative impact is much major than
increasing the latency.

I understand larger buffers come at a price. That's why in my earlier
commit last year I significantly reduced it. However, the buffer size
in CoreAudio is set according to the hardware, which is super small
latency. For instance, the system audio of 2015 generation of macbook
pro has maximum buffer size of 4096 samples, which is just 0.09s for
44.1k framerate, or 0.04s for 96k frames --- . compare to the 0.5 sec
latency alsa plugin has, even if we quadruple it, it's still super
tiny.
2017-12-12 10:56:42 +01:00
Max Kellermann
dfaf08743c *: check defined(_WIN32) instead of defined(WIN32)
Only _WIN32 is defined by the compiler, and WIN32 is not standardized
and may be missing.

Closes 
2017-12-12 10:22:20 +01:00
Max Kellermann
d9552d8a6d android/build.py: support NDK r16 2017-12-12 10:01:47 +01:00
Max Kellermann
7586a8ab2c python/build/libs.py: disable the FFmpeg HEVC decoder due to clang build failure 2017-12-12 10:01:47 +01:00
Max Kellermann
e1a942250b python/build/libs.py: disable more FFmpeg features 2017-12-12 09:57:40 +01:00
Max Kellermann
72be0185de python/libs: upgrade Boost to 1.65.1 2017-12-12 09:16:40 +01:00
Max Kellermann
7e4cbce06b python/build/libs: upgrade CURL to 7.57.0 2017-12-12 09:15:24 +01:00
Max Kellermann
177d62f431 python/build/libs: upgrade FFmpeg to 3.4.1 2017-12-11 19:00:22 +01:00
Uwe Kleine-König
5a11e03725 lib/upnp: use include path without upnp/ prefix and honor pkg-config CFLAGS
If libupnp is installed in a non-standard location we must rely on the
include path provided by $(pkg-config --cflags libupnp). Relative to the
path given from that command no prefix must be used to find the respective
files.
2017-12-11 18:58:09 +01:00
Max Kellermann
75d068b7cd Makefile.am: include Windows cross-build script in source tarball 2017-12-05 11:24:52 +01:00
FlashSystems
1208503888 Removing gcc_malloc attribute from Directory::CreateChild to fix
assignment of `mnt->mounted_database` in `SimpleDatabase::Mount`.
2017-12-03 12:34:08 +01:00
Max Kellermann
de90d401d2 MusicChunk: add magic value IGNORE_REPLAY_GAIN
This fixes spurious replay gain logs when the player inserts silence
chunks, because those silence chunks had no replay gain attached,
resetting the ReplayGainFilter state, flipping it forth and back.
2017-12-03 11:39:12 +01:00
Max Kellermann
396defaea9 MusicChunk: initialize replay_gain_serial on demand 2017-12-03 11:39:07 +01:00
Max Kellermann
18f350cd04 player/Thread: initialize MusicChunk::bit_rate in SendSilence()
This attribute is not particularly important, but it was
uninitialized.
2017-12-03 10:54:14 +01:00
Max Kellermann
478180ebe4 queue/PlaylistEdit: shuffle appended songs only within its priority group
Fixes .
2017-12-02 17:17:02 +01:00
Max Kellermann
4a3059f509 queue/PlaylistControl: don't skip highest priority song on "play"
When starting playback with a specific song which does not have the
highest priority, the previous highest priority song was skipped
completely because its order was "swapped".  This commit changes to a
more expensive operation which inserts the selected song into the
order list.

This fixes a small part of 
2017-12-02 16:25:32 +01:00
Max Kellermann
78728138a0 lib/upnp/Compat: disable the 1.8 API emulation with libupnp 1.6.24
libupnp 1.6.24 added a few badly designed macros which break the MPD
build:

 8177a4195a/

To work around this, we disable our emulation functions (from
714011c81e) on this libupnp version.

Closes 
2017-12-02 14:47:27 +01:00
FlashSystems
63fc98591d Fix for "Mount-Points are purged from database on update/rescan."
Signed-off-by: FlashSystems <developer@flashsystems.de>
2017-11-27 22:34:49 +01:00
Max Kellermann
53def9a682 increment version number to 0.20.13 2017-11-27 22:32:55 +01:00
Max Kellermann
323231d1dd release v0.20.12 2017-11-25 19:32:37 +01:00
Max Kellermann
714011c81e lib/upnp: adapt to libupnp 1.8 API changes
Closes 
2017-11-16 11:39:11 +01:00
Max Kellermann
952ff4207b lib/upnp/Callback: make "evp" parameter const 2017-11-16 11:37:58 +01:00
Max Kellermann
150b16ec2c lib/upnp/Discovery: make Upnp_Discovery pointers const 2017-11-16 11:37:04 +01:00
Max Kellermann
c98bc4a243 playlist/PlaylistRegistry: use LockRewind() instead of Rewind()
Fixes a deadlock caused by commit
31ab78ae8e.  That commit was not
actually bad - just these two calls have always been bad, which went
unnoticed for a long time.
2017-11-14 21:19:22 +01:00
Max Kellermann
014f8cd693 output/httpd: flush encoder after tag
Without the flush, ReadPage() may not return any data, or not all
data.  This may result in incomplete ddata the new "header" page,
corrupting streams with some encoders such as Vorbis.

Fixes 
2017-11-14 12:00:14 +01:00
Max Kellermann
aea37e46e3 encoder/vorbis: default to quality 3
Don't require a quality or bitrate setting.  If nothing is set, don't
fail startup - just go with a good default.  A quality setting of 3 is
what "oggenc" defaults to as well.
2017-11-14 11:30:28 +01:00
Max Kellermann
31ab78ae8e input/{cdio,ffmpeg,file,smbclient}: unlock the mutex during blocking I/O
InputStream::Read() and InputStream::Seek() are called with the mutex
locked.  That means the implementation must not block, or unlock the
mutex before calling into blocking code.

Previously, a slow CD drive could stall the whole MPD process,
including the main thread, due to this problem.

Closes 
2017-11-13 17:13:10 +01:00
Max Kellermann
f82e1453e4 input/smbclient: use std::lock_guard 2017-11-13 17:13:10 +01:00
Max Kellermann
a2b77c8813 decoder/ffmpeg, test/test_protocol: catch exceptions by reference
Work around -Werror=catch-value.
2017-11-12 18:54:29 +01:00
Max Kellermann
18add29472 configure.ac: disable -Wnoexcept-type
Workaround for .
2017-11-12 18:54:29 +01:00
cathugger
b111a8fe8d output/Thread: ensure pending tags are flushed in all cases
Fixes hanging playback with soxr resampler.

Closes , 
2017-11-05 17:42:32 +01:00
Marcin Jurkowski
3b23cf0258 decoder/vorbis: scale and clip tremor-decoded samples to 15 bits
Tremor decoder is unusable since commit 2ee43c4. Sound is distorted to
the point where it's nothing but noise.

The data from vorbis_synthesis_pcmout() needs to be scaled and
clipped for 16 bit sample size. For reference see
http://lists.xiph.org/pipermail/tremor/2010-April/001642.html and
http://lists.xiph.org/pipermail/vorbis/2006-October/026513.html.

Signed-off-by: Marcin Jurkowski <marcin1j@gmail.com>
2017-11-03 19:45:41 +01:00
Max Kellermann
28e864e096 player/Thread: log message when decoder is too slow 2017-10-25 20:26:09 +02:00
Max Kellermann
1de19b921a input/curl: call StartRequest() after setting CURLOPT_RANGE
It's not possible to set CURL options after curl_easy_perform(), and
thus the CURLOPT_RANGE had no effect.
2017-10-24 21:43:39 +02:00
Max Kellermann
ff162b5a03 input/curl: move code to StartRequest() 2017-10-24 21:41:17 +02:00
Max Kellermann
d8e4705dd4 input/curl: move the range buffer to the stack
From the CURLOPT_RANGE documentation: "The application does not have
to keep the string around after setting this option."
2017-10-24 21:38:35 +02:00
Max Kellermann
338e1f5926 increment version number to 0.20.12 2017-10-24 17:31:55 +02:00
Max Kellermann
a7fdfa08e1 release v0.20.11 2017-10-18 10:14:46 +02:00
Max Kellermann
9703a401c5 Playlist{File,Save}: always use UTF-8 in playlists on Windows
Turns out that using CP_ACP is a lousy idea, because only very few
Unicode characters can be represented by it.  Instead, switch to UTF-8
(which every sane person on other operating system already uses).

Closes 
2017-10-18 10:05:26 +02:00
Max Kellermann
753a2aa462 PlaylistSave: move code to playlist_print_path() 2017-10-18 09:51:04 +02:00
Max Kellermann
10990a0684 queue/Playlist: call MoveOrderToCurrent() in SeekSongOrder() on song change
Applies the improvements from the previous commit to the "seek"
commands, which are also capable of switching songs.

Closes 
2017-10-18 09:14:27 +02:00
Max Kellermann
91254e9211 queue/PlaylistControl: keep order list consistency in MoveOrderToCurrent()
Our previous use of Queue::SwapOrders() could cause surprising
results:

- sometimes, the old "current" song would be played again (if the
  newly selected song had not been played already)

- sometimes, the old "current" song would not be played again (if the
  newly selected song had already been played)

This is inconsistent, because it should not depend on whether the
newly selected song had already been played.

So instead of Queue::SwapOrders() we now use Queue::MoveOrderAfter()
and Queue::MoveOrderBefore(), which is more expensive, but also more
consistent.  It attempts to retain as much from the previous order
list as possible, and only moves the newly selected song around.
2017-10-18 09:05:47 +02:00
Max Kellermann
0f79287b04 queue/Playlist: move code to MoveOrderToCurrent() 2017-10-18 09:05:24 +02:00
Max Kellermann
f2fac77d8c queue/Queue: add methods MoveOrderBefore() and MoveOrderAfter() 2017-10-18 08:50:01 +02:00
Max Kellermann
81b7373637 queue/Queue: MoveOrder() returns to_order 2017-10-18 08:46:31 +02:00
Max Kellermann
fa67c2548a decoder/Thread: clear the command after catching an exception
If an early exception gets caught (e.g. from
AllocatedPath::FromUTF8Throw()) before
DecoderControl::CommandFinishedLocked() is called, the decoder thread
would go in an endless loop, because DecoderCommand::START is still
set.

Closes 
2017-09-27 17:08:16 +02:00
John Regan
ea80587ddb GME Plugin: fix track numbering
GME starts all track indexes at zero, but subtune prefixes
start at one. This fixes an off-by-one error during track
enumeration.
2017-09-27 11:18:03 +02:00
Max Kellermann
828f5f8384 lib/icu/CaseFold: disable broken strxfrm() callback 2017-09-20 23:55:14 +02:00
Max Kellermann
1295a1272a lib/icu/Compare: add fallback using strcasecmp() and strcasestr()
Our IcuCaseFold() fallback using strxfrm() is not actually case
insensitive.  This commit fixes the problem by switching to
strcasecmp().  That function is not guaranteed to support UTF-8, but
it's the best we can do in this sparse situation.

Closes 
2017-09-20 23:43:27 +02:00
Max Kellermann
66646d9276 SongFilter: use class IcuCompare 2017-09-20 23:43:26 +02:00
Max Kellermann
d0497dba92 lib/icu/Compare: OO wrapper for IcuCaseFold() 2017-09-20 23:32:55 +02:00
Max Kellermann
42914e8227 lib/icu/CaseFold: add "noexcept" 2017-09-20 23:32:54 +02:00
Max Kellermann
59b49b7881 db/Selection: add missing config.h 2017-09-20 23:32:54 +02:00
Max Kellermann
5620f16330 lib/icu/Collate: move IcuCaseFold() to CaseFold.cxx 2017-09-20 23:11:58 +02:00
Max Kellermann
be024d4ad7 lib/icu/Collate: remove unnecessary assert() 2017-09-20 23:05:31 +02:00
Max Kellermann
75c740fe2b output/sndio: fix indent 2017-09-19 18:50:35 +02:00
Max Kellermann
6c8d86bb90 output/sndio: rename the "sio_hdl" variable to avoid clash with struct name 2017-09-19 18:49:33 +02:00
Charlie Waters
b253a6b71e ffmpeg plugin: when decoded stream duration is unavailable, attempt fallback to container duration (fix ) 2017-09-18 10:39:27 +02:00
Max Kellermann
ca7b4df812 doc/user: document the Opus encoder 2017-09-07 14:21:40 +02:00
Max Kellermann
bc8dd57236 doc/protocol.xml: document status/volume=-1
Closes 
2017-09-04 08:15:41 +02:00
Max Kellermann
f4f461b8bb storage/curl: support Content-Type application/xml 2017-09-01 11:32:40 +02:00
Max Kellermann
cbb9b6957f storage/curl: use StringStartsWith() 2017-09-01 11:31:10 +02:00
Max Kellermann
f6b56c9317 storage/curl: move code to IsXmlContentType() 2017-09-01 11:30:30 +02:00
Max Kellermann
3717fb6c8d win32/build.py: add -march=pentium3 to fix 32 bit LAME build
Workaround for the following LAME build failure:

 error: inlining failed in call to always_inline '_mm_sqrt_ps': target
 specific option mismatch

This is because the LAME build scripts do not check whether SSE is
available; they only check for the presence of the "xmmintrin.h"
header.

Requiring a Pentium 3 CPU is reasonable enough, and it's the first CPU
to feature SSE support.
2017-08-31 19:48:59 +02:00
Max Kellermann
f6abbc01bd increment version number to 0.20.11 2017-08-31 19:48:59 +02:00
Max Kellermann
57a71c157d release v0.20.10 2017-08-24 09:15:43 +02:00
Max Kellermann
cc76aeb7bb python/build/libs: upgrade CURL to 7.55.1 2017-08-24 09:06:15 +02:00
Max Kellermann
811cabf8a9 python/libs: upgrade Opus to 1.2.1 2017-08-24 09:06:15 +02:00
Max Kellermann
bf8d2f93d2 python/build/libs: upgrade FFmpeg to 3.3.3 2017-08-24 09:06:15 +02:00
Max Kellermann
07d8259ad6 python/libs: upgrade Boost to 1.65 2017-08-23 17:46:25 +02:00
Max Kellermann
a00d412008 player/Thread: initialize play_audio_format, fixes assertion
This fixes an assertion failure caused by resuming playback before the
decoder has finished startup.
2017-08-23 17:43:49 +02:00
Matthew Leon
5fb39658f1 OSX mixer 2017-08-21 20:05:50 +01:00
Max Kellermann
b0703b92c3 util/FormatString: pass the allocated buffer to AllocatedString::Donate()
.. and not the stack buffer.  This made the AllocatedString destructor
crash.

Closes 
2017-08-03 00:25:30 +02:00
Max Kellermann
dd9fd3d8a7 tag/Aiff: the FORM chunk size is big-endian
Was broken by commit 8a86460b8f

Closes 
2017-07-31 13:46:09 +02:00
Max Kellermann
cf0c59864f doc/protocol.xml: clarify that idle events do not get lost 2017-07-21 09:51:43 +02:00
Matthew Leon
4c0404c70d Check for MusicBrainz id3v2 tags in ffmpeg.
Addresses .

Previously, the ffmpeg decoder only checked for the "generic"
MusicBrainz metadata keys used in other metadata container formats.
2017-07-20 08:28:14 +02:00
Matthew Leon
573a413ee1 move MusicBrainz id3v2 tags to separate file
We will reuse these tags elsewhere.
2017-07-20 08:26:29 +02:00
Max Kellermann
f633e6ca49 python/build/libs: add LAME
Enable it in the Windows build script, closes .

LAME currently doesn't support Android:

 checking host system type... Invalid configuration `arm-linux-androideabi': system `androideabi' not recognized
2017-07-19 20:53:52 +02:00
Max Kellermann
07b06d76be {android,win32}/build.py: concatenate variables from the command line 2017-07-19 20:53:52 +02:00
Max Kellermann
856fe2da15 python/build/libs: upgrade FFmpeg to 3.3.2, CURL to 7.54.1 2017-06-15 21:53:22 +02:00
Max Kellermann
f82aae65cd doc/user: add more Debian build dependencies 2017-06-15 21:37:07 +02:00
Max Kellermann
3fbd11a104 doc/user: update build dependencies for Debian Jessie 2017-06-15 21:37:05 +02:00
Max Kellermann
58a99f1907 increment version number to 0.20.10 2017-06-15 21:35:23 +02:00
237 changed files with 3048 additions and 899 deletions
AUTHORSMakefile.amNEWS
android
configure.ac
doc
python/build
src
CommandLine.cxxIOThread.cxxIOThread.hxxLocateUri.hxxLogBackend.cxxLogInit.cxxLogLevel.hxxMain.cxxMain.hxxMusicChunk.hxxMusicPipe.hxxPlaylistFile.cxxPlaylistSave.cxxSongFilter.cxxSongFilter.hxxSongSave.cxxStateFile.cxxStateFile.hxxStats.cxxTimePrint.cxx
archive
client
command
config
db
decoder
encoder
event
filter
fs
input
lib
mixer
neighbor
net
output
pcm
player
playlist
protocol
queue
storage
system
tag
thread
unix
util
win32
test
win32

@@ -30,3 +30,9 @@ The following people have contributed code to MPD:
Jurgen Kramer <gtmkramer@xs4all.nl>
Jean-Francois Dockes <jf@dockes.org>
Yue Wang <yuleopen@gmail.com>
Matthew Leon Grinshpun <ml@matthewleon.com>
Dimitris Papastamos <sin@2f30.org>
Florian Schlichting <fsfs@debian.org>
François Revol <revol@free.fr>
Jacob Vosmaer <contact@jacobvosmaer.nl>
Thomas Guillem <thomas@gllm.fr>

@@ -61,8 +61,8 @@ src_mpd_LDADD = \
libnet.a \
$(FS_LIBS) \
libsystem.a \
libutil.a \
$(ICU_LDADD) \
libutil.a \
$(SYSTEMD_DAEMON_LIBS)
src_mpd_SOURCES = \
@@ -243,6 +243,7 @@ CURL_SOURCES = \
src/lib/curl/Slist.hxx
UPNP_SOURCES = \
src/lib/upnp/Compat.hxx \
src/lib/upnp/Init.cxx src/lib/upnp/Init.hxx \
src/lib/upnp/ClientInit.cxx src/lib/upnp/ClientInit.hxx \
src/lib/upnp/Device.cxx src/lib/upnp/Device.hxx \
@@ -284,30 +285,62 @@ libmain_a_CPPFLAGS = $(AM_CPPFLAGS) -Iandroid/build/include
src_mpd_LDADD += libandroid.a libjava.a
all-local: android/build/bin/$(APK_NAME)-debug.apk
all-local: android/build/$(APK_NAME)-debug.apk
clean-local:
rm -rf android/build
libmpd.so: $(filter %.a,$(src_mpd_LDADD)) libmain.a
$(AM_V_CXXLD)$(CXXLD) -shared -Wl,--no-undefined,-shared,-Bsymbolic -llog -lz -o $@ $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) src/libmain_a-Main.o $(src_mpd_LDADD) $(LIBS)
android/build/build.xml: android/AndroidManifest.xml
rm -rf android/build
mkdir -p android/build/include android/build/res android/build/src/org
ln -s $(abs_srcdir)/android/AndroidManifest.xml $(abs_srcdir)/android/custom_rules.xml android/build
ln -s $(abs_srcdir)/android/src android/build/src/org/musicpd
ln -s $(abs_srcdir)/android/res/values $(abs_srcdir)/android/res/layout android/build/res
$(ANDROID_SDK)/tools/android update project --path android/build --target android-17 --name $(APK_NAME)
ANDROID_SDK_BUILD_TOOLS_VERSION = 27.0.0
ANDROID_SDK_PLATFORM = android-17
android/build/bin/classes/org/musicpd/Bridge.class: android/src/Bridge.java android/build/build.xml android/build/res/drawable/icon.png
cd android/build && ant compile-jni-classes
ANDROID_BUILD_TOOLS_DIR = $(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)
ANDROID_SDK_PLATFORM_DIR = $(ANDROID_SDK)/platforms/$(ANDROID_SDK_PLATFORM)
android/build/include/org_musicpd_Bridge.h: android/build/bin/classes/org/musicpd/Bridge.class
javah -classpath $(ANDROID_SDK)/platforms/android-17/android.jar:android/build/bin/classes -d $(@D) org.musicpd.Bridge
JAVAC = javac
AAPT = $(ANDROID_BUILD_TOOLS_DIR)/aapt
DX = $(ANDROID_BUILD_TOOLS_DIR)/dx
ZIPALIGN = $(ANDROID_BUILD_TOOLS_DIR)/zipalign
ANDROID_XML_RES := $(wildcard $(srcdir)/android/res/*/*.xml)
ANDROID_XML_RES_COPIES := $(patsubst $(srcdir)/android/%,android/build/%,$(ANDROID_XML_RES))
JAVA_SOURCE_NAMES = Bridge.java Loader.java Main.java
JAVA_SOURCE_PATHS = $(addprefix $(srcdir)/android/src/,$(JAVA_SOURCE_NAMES))
JAVA_CLASSFILES_DIR = android/build/classes
$(ANDROID_XML_RES_COPIES): $(ANDROID_XML_RES)
@$(MKDIR_P) $(dir $@)
cp $(patsubst android/build/%,$(srcdir)/android/%,$@) $@
android/build/resources.apk: $(ANDROID_XML_RES_COPIES) android/build/res/drawable/icon.png
@$(MKDIR_P) android/build/gen
$(AAPT) package -f -m --auto-add-overlay \
--custom-package org.musicpd \
-M $(srcdir)/android/AndroidManifest.xml \
-S android/build/res \
-J android/build/gen \
-I $(ANDROID_SDK_PLATFORM_DIR)/android.jar \
-F android/build/resources.apk
# R.java is generated by aapt, when resources.apk is generated
android/build/gen/org/musicpd/R.java: android/build/resources.apk
android/build/classes.dex: $(JAVA_SOURCE_PATHS) android/build/gen/org/musicpd/R.java
@$(MKDIR_P) $(JAVA_CLASSFILES_DIR)
$(JAVAC) -source 1.6 -target 1.6 -Xlint:-options \
-cp $(ANDROID_SDK_PLATFORM_DIR)/android.jar:$(JAVA_CLASSFILES_DIR) \
-d $(JAVA_CLASSFILES_DIR) $^
$(DX) --dex --output $@ $(JAVA_CLASSFILES_DIR)
android/build/include/org_musicpd_Bridge.h: android/build/classes.dex
javah -classpath $(ANDROID_SDK_PLATFORM_DIR)/android.jar:$(JAVA_CLASSFILES_DIR) -d $(@D) org.musicpd.Bridge
BUILT_SOURCES = android/build/include/org_musicpd_Bridge.h
android/build/libs/armeabi-v7a/libmpd.so: libmpd.so android/build/build.xml
android/build/lib/armeabi-v7a/libmpd.so: libmpd.so
mkdir -p $(@D)
rm -f $@
$(STRIP) -o $@ $<
@@ -316,24 +349,19 @@ android/build/res/drawable/icon.png: mpd.svg
mkdir -p $(@D)
rsvg-convert --width=48 --height=48 $< -o $@
APK_DEPS = android/build/res/drawable/icon.png \
android/build/libs/armeabi-v7a/libmpd.so \
$(wildcard $(srcdir)/android/src/*.java) \
android/build/build.xml
.DELETE_ON_ERROR: android/build/unsigned.apk
android/build/unsigned.apk: android/build/classes.dex android/build/resources.apk android/build/lib/armeabi-v7a/libmpd.so
cp android/build/resources.apk $@
cd $(dir $@) && zip -q -r $(notdir $@) classes.dex lib
android/build/bin/$(APK_NAME)-debug.apk: $(APK_DEPS)
cd android/build && ant nodeps debug
android/build/$(APK_NAME)-debug.apk: android/build/unsigned.apk
jarsigner -keystore $(HOME)/.android/debug.keystore -storepass android -signedjar $@ $< androiddebugkey
android/build/bin/$(APK_NAME)-release-unsigned.apk: $(APK_DEPS)
cd android/build && ant nodeps release
android/build/bin/$(APK_NAME)-release-unaligned.apk: android/build/bin/$(APK_NAME)-release-unsigned.apk
android/build/$(APK_NAME)-release-unaligned.apk: android/build/unsigned.apk
jarsigner -digestalg SHA1 -sigalg MD5withRSA -storepass:env ANDROID_KEYSTORE_PASS -keystore $(ANDROID_KEYSTORE) -signedjar $@ $< $(ANDROID_KEY_ALIAS)
ANDROID_SDK_BUILD_TOOLS_VERSION = 20.0.0
android/build/bin/$(APK_NAME).apk: android/build/bin/$(APK_NAME)-release-unaligned.apk
$(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)/zipalign -f 4 $< $@
android/build/$(APK_NAME).apk: android/build/$(APK_NAME)-release-unaligned.apk
$(ZIPALIGN) -f 4 $< $@
endif
@@ -469,6 +497,7 @@ libthread_a_SOURCES = \
libnet_a_SOURCES = \
src/net/Features.hxx \
src/net/Init.hxx \
src/net/ToString.cxx src/net/ToString.hxx \
src/net/Resolver.cxx src/net/Resolver.hxx \
src/net/StaticSocketAddress.cxx src/net/StaticSocketAddress.hxx \
@@ -518,6 +547,8 @@ libevent_a_SOURCES = \
# UTF-8 library
libicu_a_SOURCES = \
src/lib/icu/CaseFold.cxx src/lib/icu/CaseFold.hxx \
src/lib/icu/Compare.cxx src/lib/icu/Compare.hxx \
src/lib/icu/Collate.cxx src/lib/icu/Collate.hxx \
src/lib/icu/Converter.cxx src/lib/icu/Converter.hxx
@@ -699,6 +730,7 @@ libstorage_a_SOURCES = \
src/storage/MemoryDirectoryReader.cxx src/storage/MemoryDirectoryReader.hxx \
src/storage/Configured.cxx src/storage/Configured.hxx \
src/storage/plugins/LocalStorage.cxx src/storage/plugins/LocalStorage.hxx \
src/storage/StorageState.cxx src/storage/StorageState.hxx \
src/storage/FileInfo.hxx
libstorage_a_CPPFLAGS = $(AM_CPPFLAGS) \
@@ -751,6 +783,7 @@ libneighbor_a_SOURCES = \
src/neighbor/NeighborPlugin.hxx
libneighbor_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(UPNP_CFLAGS) \
$(SMBCLIENT_CFLAGS)
if ENABLE_SMBCLIENT
@@ -800,6 +833,8 @@ libdb_plugins_a_SOURCES = \
src/db/plugins/simple/PrefixedLightSong.hxx \
src/db/plugins/simple/SimpleDatabasePlugin.cxx \
src/db/plugins/simple/SimpleDatabasePlugin.hxx
libdb_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(UPNP_CFLAGS)
if ENABLE_LIBMPDCLIENT
libdb_plugins_a_SOURCES += \
@@ -923,6 +958,7 @@ libtag_a_SOURCES =\
src/tag/ReplayGain.cxx src/tag/ReplayGain.hxx \
src/tag/MixRamp.cxx src/tag/MixRamp.hxx \
src/tag/Generic.cxx src/tag/Generic.hxx \
src/tag/Id3MusicBrainz.cxx src/tag/Id3MusicBrainz.hxx \
src/tag/ApeLoader.cxx src/tag/ApeLoader.hxx \
src/tag/ApeReplayGain.cxx src/tag/ApeReplayGain.hxx \
src/tag/ApeTag.cxx src/tag/ApeTag.hxx
@@ -1489,6 +1525,8 @@ liboutput_plugins_a_SOURCES += \
src/output/plugins/OSXOutputPlugin.cxx \
src/output/plugins/OSXOutputPlugin.hxx
endif
libmixer_plugins_a_SOURCES += \
src/mixer/plugins/OSXMixerPlugin.cxx
if ENABLE_PULSE
liboutput_plugins_a_SOURCES += \
@@ -2122,6 +2160,7 @@ test_run_output_LDADD = $(MPD_LIBS) \
libutil.a
test_run_output_SOURCES = test/run_output.cxx \
test/ScopeIOThread.hxx \
test/NullMixerListener.hxx \
src/Log.cxx src/LogBackend.cxx \
src/IOThread.cxx \
src/output/Domain.cxx \
@@ -2145,6 +2184,7 @@ test_read_mixer_LDADD = \
libsystem.a \
libutil.a
test_read_mixer_SOURCES = test/read_mixer.cxx \
test/NullMixerListener.hxx \
src/Log.cxx src/LogBackend.cxx \
src/mixer/MixerControl.cxx \
src/filter/FilterPlugin.cxx \
@@ -2406,6 +2446,7 @@ EXTRA_DIST = $(doc_DATA) autogen.sh \
$(man_MANS) $(DOCBOOK_FILES) doc/mpdconf.example doc/doxygen.conf \
$(wildcard $(srcdir)/doc/include/*.xml) \
systemd/system/mpd.socket \
$(wildcard $(srcdir)/python/build/*.py) \
android/AndroidManifest.xml \
android/build.py \
android/custom_rules.xml \
@@ -2413,5 +2454,6 @@ EXTRA_DIST = $(doc_DATA) autogen.sh \
android/src/Bridge.java \
android/src/Loader.java \
android/src/Main.java \
win32/build.py \
win32/res/mpd.rc.in win32/res/mpd.ico \
src/haiku/App_MusicPD

127
NEWS

@@ -1,3 +1,130 @@
ver 0.20.20 (2018/05/22)
* protocol
- fix "modified-since" filter regression
* output
- pulse: cork stream when paused due to "single" mode
* decoder
- dsdiff, dsf: support more MIME types
- dsdiff, dsf: allow 4 MB ID3 tags
- opus: support R128_ALBUM_GAIN tag
* Android, Windows
- enable the "proxy" database plugin
ver 0.20.19 (2018/04/26)
* protocol
- validate absolute seek time, reject negative values
* database
- proxy: fix "search already in progress" errors
- proxy: implement "list ... group"
* input
- mms: fix lockup bug and a crash bug
* decoder
- ffmpeg: fix av_register_all() deprecation warning (FFmpeg 4.0)
* player
- fix spurious "Not seekable" error when switching radio streams
* macOS: fix crash bug
ver 0.20.18 (2018/02/24)
* input
- curl: allow authentication methods other than "Basic"
* decoder
- flac: improve seeking precision
* fix gapless CUE song transitions
* Android, Windows
- enable the NFS storage plugin
ver 0.20.17 (2018/02/11)
* output
- alsa: fix crash bug with 8 channels
* mixer
- alsa: fix rounding error at volume 0
* fix real-time and idle scheduling with Musl
* Android
- fix compatibility with Android 4.0
ver 0.20.16 (2018/02/03)
* output
- pulse: fix crash during auto-detection
* database
- simple: fix search within mount points
- upnp: enable IPv6
* archive
- iso9660: libcdio 2.0 compatibility
* fix crash in debug build on Haiku and other operating systems
ver 0.20.15 (2018/01/05)
* queue: fix crash after seek failure
* resampler
- soxr: clear internal state after manual song change
* state file
- make mount point restore errors non-fatal
- fix crash when restoring mounts with incompatible database plugin
* Android
- build without Ant
- fix for SIGSYS crash
ver 0.20.14 (2018/01/01)
* database
- simple: fix file corruption in the presence of mount points
* archive
- bz2: fix deadlock
- reduce lock contention, fixing lots of xrun problems
* fix Solaris build failure
ver 0.20.13 (2017/12/18)
* output
- osx: set up ring buffer to hold at least 100ms
* mixer
- alsa: fix rounding errors
* database
- simple: don't purge mount points on update/rescan
- simple: fix "mount" bug caused by bad compiler optimization
- simple: fix "lsinfo" into mount points
- upnp: work around libupnp 1.6.24 API breakage
* queue: fix spuriously misplaced prioritized songs
* save and restore mountpoints within the state file
* include Windows cross-build script in source tarball
* fix Windows build failures
ver 0.20.12 (2017/11/25)
* database
- upnp: adapt to libupnp 1.8 API changes
* input
- cdio_paranoia, ffmpeg, file, smbclient: reduce lock contention,
fixing lots of xrun problems
- curl: fix seeking
* decoder
- ffmpeg: fix GCC 8 warning
- vorbis: fix Tremor support
* player
- log message when decoder is too slow
* encoder
- vorbis: default to quality 3
* output
- fix hanging playback with soxr resampler
- httpd: flush encoder after tag; fixes corrupt Vorbis stream
ver 0.20.11 (2017/10/18)
* storage
- curl: support Content-Type application/xml
* decoder
- ffmpeg: more reliable song duration
- gme: fix track numbering
* improve random song order when switching songs manually
* fix case insensitive search without libicu
* fix Unicode file names in playlists on Windows
* fix endless loop when accessing malformed file names in ZIP files
ver 0.20.10 (2017/08/24)
* decoder
- ffmpeg: support MusicBrainz ID3v2 tags
* tags
- aiff: fix FORM chunk size endianess (is big-endian)
* mixer
- osx: add a mixer for OSX.
* fix crash when resuming playback before decoder is ready
* fix crash on Windows
ver 0.20.9 (2017/06/04)
* decoder
- ffmpeg: support *.adx

@@ -2,10 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="13"
android:versionName="0.19.9">
android:versionCode="19"
android:versionName="0.20.20">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17"/>
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17"/>
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Main"

@@ -3,13 +3,14 @@
import os, os.path
import sys, subprocess
if len(sys.argv) < 3:
print("Usage: build.py SDK_PATH NDK_PATH [configure_args...]", file=sys.stderr)
if len(sys.argv) < 4:
print("Usage: build.py SDK_PATH NDK_PATH ABI [configure_args...]", file=sys.stderr)
sys.exit(1)
sdk_path = sys.argv[1]
ndk_path = sys.argv[2]
configure_args = sys.argv[3:]
android_abi = sys.argv[3]
configure_args = sys.argv[4:]
if not os.path.isfile(os.path.join(sdk_path, 'tools', 'android')):
print("SDK not found in", ndk_path, file=sys.stderr)
@@ -19,8 +20,27 @@ if not os.path.isdir(ndk_path):
print("NDK not found in", ndk_path, file=sys.stderr)
sys.exit(1)
android_abis = {
'armeabi-v7a': {
'arch': 'arm-linux-androideabi',
'ndk_arch': 'arm',
'toolchain_arch': 'arm-linux-androideabi',
'llvm_triple': 'armv7-none-linux-androideabi',
'cflags': '-march=armv7-a -mfpu=vfp -mfloat-abi=softfp',
},
'x86': {
'arch': 'i686-linux-android',
'ndk_arch': 'x86',
'toolchain_arch': 'x86',
'llvm_triple': 'i686-none-linux-android',
'cflags': '-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
},
}
# select the NDK target
arch = 'arm-linux-androideabi'
abi_info = android_abis[android_abi]
arch = abi_info['arch']
# the path to the MPD sources
mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..'))
@@ -44,15 +64,15 @@ class AndroidNdkToolchain:
self.src_path = src_path
self.build_path = build_path
self.ndk_arch = 'arm'
android_abi = 'armeabi-v7a'
ndk_arch = abi_info['ndk_arch']
ndk_platform = 'android-14'
# select the NDK compiler
gcc_version = '4.9'
ndk_platform_path = os.path.join(ndk_path, 'platforms', ndk_platform)
sysroot = os.path.join(ndk_platform_path, 'arch-' + self.ndk_arch)
sysroot = os.path.join(ndk_path, 'sysroot')
target_root = os.path.join(ndk_platform_path, 'arch-' + ndk_arch)
install_prefix = os.path.join(arch_path, 'root')
@@ -60,11 +80,13 @@ class AndroidNdkToolchain:
self.install_prefix = install_prefix
self.sysroot = sysroot
toolchain_path = os.path.join(ndk_path, 'toolchains', arch + '-' + gcc_version, 'prebuilt', build_arch)
toolchain_path = os.path.join(ndk_path, 'toolchains', abi_info['toolchain_arch'] + '-' + gcc_version, 'prebuilt', build_arch)
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
llvm_triple = 'armv7-none-linux-androideabi'
llvm_triple = abi_info['llvm_triple']
common_flags = '-march=armv7-a -mfloat-abi=softfp'
common_flags = '-Os -g'
common_flags += ' -fPIC'
common_flags += ' ' + abi_info['cflags']
toolchain_bin = os.path.join(toolchain_path, 'bin')
llvm_bin = os.path.join(llvm_path, 'bin')
@@ -72,33 +94,40 @@ class AndroidNdkToolchain:
self.cxx = os.path.join(llvm_bin, 'clang++')
common_flags += ' -target ' + llvm_triple + ' -integrated-as -gcc-toolchain ' + toolchain_path
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
self.ar = os.path.join(toolchain_bin, arch + '-ar')
self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib')
self.nm = os.path.join(toolchain_bin, arch + '-nm')
self.strip = os.path.join(toolchain_bin, arch + '-strip')
self.cflags = '-Os -g ' + common_flags
self.cxxflags = '-Os -g ' + common_flags
self.cppflags = '--sysroot=' + self.sysroot + ' -isystem ' + os.path.join(install_prefix, 'include')
self.ldflags = '--sysroot=' + self.sysroot + ' ' + common_flags + ' -L' + os.path.join(install_prefix, 'lib')
self.cflags = common_flags
self.cxxflags = common_flags
self.cppflags = '--sysroot=' + sysroot + \
' -isystem ' + os.path.join(install_prefix, 'include') + \
' -isystem ' + os.path.join(sysroot, 'usr', 'include', arch) + \
' -D__ANDROID_API__=14'
self.ldflags = '--sysroot=' + sysroot + \
' -L' + os.path.join(install_prefix, 'lib') + \
' -L' + os.path.join(target_root, 'usr', 'lib') + \
' -B' + os.path.join(target_root, 'usr', 'lib') + \
' ' + common_flags
self.libs = ''
self.is_arm = self.ndk_arch == 'arm'
self.is_arm = ndk_arch == 'arm'
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
self.is_windows = False
libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++')
libcxx_libs_path = os.path.join(libcxx_path, 'libs', android_abi)
libstdcxx_cppflags = '-nostdinc++ -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include')
libstdcxx_ldadd = os.path.join(libcxx_libs_path, 'libc++_static.a') + ' ' + os.path.join(libcxx_libs_path, 'libc++abi.a')
if self.is_armv7:
libstdcxx_ldadd += ' ' + os.path.join(libcxx_libs_path, 'libunwind.a')
libstdcxx_flags = '-stdlib=libc++'
libstdcxx_cxxflags = libstdcxx_flags + ' -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include')
libstdcxx_ldflags = libstdcxx_flags + ' -static-libstdc++ -L' + libcxx_libs_path
if use_cxx:
self.libs += ' ' + libstdcxx_ldadd
self.cppflags += ' ' + libstdcxx_cppflags
self.cxxflags += ' ' + libstdcxx_cxxflags
self.ldflags += ' ' + libstdcxx_ldflags
self.env = dict(os.environ)
@@ -109,14 +138,15 @@ class AndroidNdkToolchain:
# a list of third-party libraries to be used by MPD on Android
from build.libs import *
thirdparty_libs = [
libmpdclient,
libogg,
libvorbis,
opus,
flac,
libid3tag,
libmad,
ffmpeg,
curl,
libnfs,
boost,
]
@@ -154,5 +184,9 @@ configure = [
] + configure_args
from build.cmdline import concatenate_cmdline_variables
configure = concatenate_cmdline_variables(configure,
set(('CFLAGS', 'CXXFLAGS', 'CPPFLAGS', 'LDFLAGS', 'LIBS')))
subprocess.check_call(configure, env=toolchain.env)
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], env=toolchain.env)

@@ -1,10 +1,10 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.20.9, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.20.20, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
VERSION_MINOR=20
VERSION_REVISION=9
VERSION_REVISION=20
VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx])
@@ -241,6 +241,7 @@ AC_CHECK_FUNCS(getpwnam_r getpwuid_r)
AC_CHECK_FUNCS(initgroups)
AC_CHECK_FUNCS(fnmatch)
AC_CHECK_FUNCS(strndup)
AC_CHECK_FUNCS(strcasestr)
if test x$host_is_linux = xyes; then
MPD_OPTIONAL_FUNC(eventfd, eventfd, USE_EVENTFD)
@@ -453,7 +454,7 @@ dnl ---------------------------------------------------------------------------
dnl Mandatory Libraries
dnl ---------------------------------------------------------------------------
AX_BOOST_BASE([1.46],, [AC_MSG_ERROR([Boost not found])])
AX_BOOST_BASE([1.54],, [AC_MSG_ERROR([Boost not found])])
AC_ARG_ENABLE(icu,
AS_HELP_STRING([--enable-icu],
@@ -491,7 +492,7 @@ if test x$enable_ipv6 = xyes; then
AC_EGREP_CPP([AP_maGiC_VALUE],
[
#include <sys/types.h>
#ifdef WIN32
#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
@@ -1384,6 +1385,11 @@ then
AX_APPEND_COMPILE_FLAGS([-Wcast-qual])
AX_APPEND_COMPILE_FLAGS([-Wwrite-strings])
AX_APPEND_COMPILE_FLAGS([-Wsign-compare])
dnl This GCC8 warning for C++17 ABI compatibility is of no
dnl interest for us, because we're not a shared library.
AX_APPEND_COMPILE_FLAGS([-Wno-noexcept-type])
AC_LANG_POP
fi

@@ -113,7 +113,7 @@
<para>
<varname>musicbrainz_artistid</varname>: the artist id in the
<ulink
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
database.
</para>
</listitem>
@@ -122,7 +122,7 @@
<para>
<varname>musicbrainz_albumid</varname>: the album id in the
<ulink
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
database.
</para>
</listitem>
@@ -131,7 +131,7 @@
<para>
<varname>musicbrainz_albumartistid</varname>: the album artist
id in the <ulink
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
database.
</para>
</listitem>
@@ -140,7 +140,7 @@
<para>
<varname>musicbrainz_trackid</varname>: the track id in the
<ulink
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
database.
</para>
</listitem>
@@ -149,7 +149,7 @@
<para>
<varname>musicbrainz_releasetrackid</varname>: the release track
id in the <ulink
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
database.
</para>
</listitem>

@@ -403,6 +403,15 @@
</para>
</listitem>
</itemizedlist>
<para>
Change events accumulate, even while the connection is
not in "idle" mode; no events gets lost while the client
is doing something else with the connection. If an
event had already occurred since the last call, the new
<command>idle</command> command will return immediately.
</para>
<para>
While a client is waiting for <command>idle</command>
results, the server disables timeouts, allowing a client
@@ -436,7 +445,9 @@
<listitem>
<para>
<varname>volume</varname>:
<returnvalue>0-100</returnvalue>
<returnvalue>0-100</returnvalue> or
<returnvalue>-1</returnvalue> if the volume cannot
be determined
</para>
</listitem>
<listitem>

@@ -66,6 +66,26 @@
</para>
</section>
<section id="install_android">
<title>Installing on Android</title>
<para>
An experimental Android build is available on <ulink
url="https://play.google.com/store/apps/details?id=org.musicpd">Google
Play</ulink>. After installing and launching it, MPD will
scan the music in your <filename>Music</filename> directory
and you can control it as usual with a MPD client.
</para>
<para>
If you need to tweak the configuration, you can create a file
called <filename>mpd.conf</filename> on the data partition
(the directory which is returned by Android's <ulink
url="https://developer.android.com/reference/android/os/Environment.html#getExternalStorageDirectory()">getExternalStorageDirectory()</ulink>
API function).
</para>
</section>
<section id="install_source">
<title>Compiling from source</title>
@@ -94,7 +114,7 @@ cd mpd-version</programlisting>
<listitem>
<para>
<ulink url="http://www.boost.org/">Boost 1.46</ulink>
<ulink url="http://www.boost.org/">Boost 1.54</ulink>
</para>
</listitem>
@@ -113,7 +133,7 @@ cd mpd-version</programlisting>
<para>
For example, the following installs a fairly complete list of
build dependencies on Debian Wheezy:
build dependencies on Debian Jessie:
</para>
<programlisting>
@@ -125,19 +145,20 @@ apt-get install g++ \
libmpcdec-dev libwavpack-dev libwildmidi-dev \
libsidplay2-dev libsidutils-dev libresid-builder-dev \
libavcodec-dev libavformat-dev \
libmp3lame-dev \
libmp3lame-dev libtwolame-dev libshine-dev \
libsamplerate0-dev libsoxr-dev \
libbz2-dev libcdio-paranoia-dev libiso9660-dev libmms-dev \
libzzip-dev \
libcurl4-gnutls-dev libyajl-dev libexpat-dev \
libasound2-dev libao-dev libjack-jackd2-dev libopenal-dev \
libpulse-dev libroar-dev libshout3-dev \
libsndio-dev \
libmpdclient-dev \
libnfs-dev libsmbclient-dev \
libupnp-dev \
libavahi-client-dev \
libsqlite3-dev \
libsystemd-daemon-dev libwrap0-dev \
libsystemd-dev libwrap0-dev \
libcppunit-dev xmlto \
libboost-dev \
libicu-dev
@@ -247,6 +268,59 @@ apt-get install g++ \
script.
</para>
</section>
<section id="android_build">
<title>Compiling for Android</title>
<para>
MPD can be compiled as an Android app. It can be installed
easily with <link linkend="install_android">Google
Play</link>, but if you want to build it from source, follow
this section.
</para>
<para>
You need:
</para>
<itemizedlist>
<listitem>
<para>
Android SDK
</para>
</listitem>
<listitem>
<para>
<ulink
url="https://developer.android.com/ndk/downloads/index.html">Android
NDK</ulink>
</para>
</listitem>
</itemizedlist>
<para>
Just like with the native build, unpack the
<application>MPD</application> source tarball and change
into the directory. Then, instead of
<command>./configure</command>, type:
</para>
<programlisting>./android/build.py SDK_PATH NDK_PATH ABI
make android/build/mpd-debug.apk</programlisting>
<para>
<varname>SDK_PATH</varname> is the absolute path where you
installed the Android SDK; <varname>NDK_PATH</varname> is
the Android NDK installation path; <varname>ABI</varname> is
the Android ABI to be built, e.g. "armeabi-v7a".
</para>
<para>
This downloads various library sources, and then configures
and builds <application>MPD</application>.
</para>
</section>
</section>
<section id="systemd_socket">
@@ -322,7 +396,9 @@ systemctl start mpd.socket</programlisting>
<application>MPD</application> as a user daemon (and not as a
system daemon), the configuration is read from
<filename>$XDG_CONFIG_HOME/mpd/mpd.conf</filename> (usually
<filename>~/.config/mpd/mpd.conf</filename>).
<filename>~/.config/mpd/mpd.conf</filename>). On Android,
<filename>mpd.conf</filename> will be loaded from the
top-level directory of the data partition.
</para>
<para>
@@ -2990,6 +3066,60 @@ run</programlisting>
</informaltable>
</section>
<section id="opus_encoder">
<title><varname>opus</varname></title>
<para>
Encodes into <ulink
url="http://www.opus-codec.org/">Ogg Opus</ulink>.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>bitrate</varname>
</entry>
<entry>
Sets the data rate in bit per second. The special
value "auto" lets <application>libopus</application>
choose a rate (which is the default), and "max" uses
the maximum possible data rate.
</entry>
</row>
<row>
<entry>
<varname>complexity</varname>
</entry>
<entry>
Sets the <ulink
url="https://wiki.xiph.org/OpusFAQ#What_is_the_complexity_of_Opus.3F">Opus
complexity</ulink>.
</entry>
</row>
<row>
<entry>
<varname>signal</varname>
</entry>
<entry>
Sets the Opus signal type. Valid values are "auto"
(the default), "voice" and "music".
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section id="vorbis_encoder">
<title><varname>vorbis</varname></title>
@@ -3013,8 +3143,8 @@ run</programlisting>
</entry>
<entry>
Sets the quality for VBR. -1 is the lowest quality,
10 is the highest quality. Cannot be used with
<varname>bitrate</varname>.
10 is the highest quality. Defaults to 3. Cannot
be used with <varname>bitrate</varname>.
</entry>
</row>
<row>

@@ -1,24 +1,37 @@
import os.path, subprocess
import os.path, subprocess, sys
from build.project import Project
from build.makeproject import MakeProject
class AutotoolsProject(Project):
class AutotoolsProject(MakeProject):
def __init__(self, url, md5, installed, configure_args=[],
autogen=False,
autoreconf=False,
cppflags='',
ldflags='',
libs='',
subdirs=None,
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
MakeProject.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args
self.autogen = autogen
self.autoreconf = autoreconf
self.cppflags = cppflags
self.ldflags = ldflags
self.libs = libs
self.subdirs = subdirs
def build(self, toolchain):
def configure(self, toolchain):
src = self.unpack(toolchain)
if self.autogen:
subprocess.check_call(['libtoolize', '--force'], cwd=src)
if sys.platform == 'darwin':
subprocess.check_call(['glibtoolize', '--force'], cwd=src)
else:
subprocess.check_call(['libtoolize', '--force'], cwd=src)
subprocess.check_call(['aclocal'], cwd=src)
subprocess.check_call(['automake', '--add-missing', '--force-missing', '--foreign'], cwd=src)
subprocess.check_call(['autoconf'], cwd=src)
if self.autoreconf:
subprocess.check_call(['autoreconf', '-vif'], cwd=src)
build = self.make_build_path(toolchain)
@@ -29,8 +42,8 @@ class AutotoolsProject(Project):
'CFLAGS=' + toolchain.cflags,
'CXXFLAGS=' + toolchain.cxxflags,
'CPPFLAGS=' + toolchain.cppflags + ' ' + self.cppflags,
'LDFLAGS=' + toolchain.ldflags,
'LIBS=' + toolchain.libs,
'LDFLAGS=' + toolchain.ldflags + ' ' + self.ldflags,
'LIBS=' + toolchain.libs + ' ' + self.libs,
'AR=' + toolchain.ar,
'RANLIB=' + toolchain.ranlib,
'STRIP=' + toolchain.strip,
@@ -40,7 +53,12 @@ class AutotoolsProject(Project):
] + self.configure_args
subprocess.check_call(configure, cwd=build, env=toolchain.env)
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'],
cwd=build, env=toolchain.env)
subprocess.check_call(['/usr/bin/make', '--quiet', 'install'],
cwd=build, env=toolchain.env)
return build
def build(self, toolchain):
build = self.configure(toolchain)
if self.subdirs is not None:
for subdir in self.subdirs:
MakeProject.build(self, toolchain, os.path.join(build, subdir))
else:
MakeProject.build(self, toolchain, build)

29
python/build/cmdline.py Normal file

@@ -0,0 +1,29 @@
def concatenate_cmdline_variables(src, names):
"""Find duplicate variable declarations on the given source list, and
concatenate the values of those in the 'names' list."""
# the result list being constructed
dest = []
# a map of variable name to destination list index
positions = {}
for item in src:
i = item.find('=')
if i > 0:
# it's a variable
name = item[:i]
if name in names:
# it's a known variable
if name in positions:
# already specified: concatenate instead of
# appending it
dest[positions[name]] += ' ' + item[i + 1:]
continue
else:
# not yet seen: append it and remember the list
# index
positions[name] = len(dest)
dest.append(item)
return dest

@@ -1,28 +1,54 @@
import re
from os.path import abspath
from build.project import Project
from build.zlib import ZlibProject
from build.meson import MesonProject
from build.autotools import AutotoolsProject
from build.ffmpeg import FfmpegProject
from build.boost import BoostProject
libmpdclient = MesonProject(
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.14.tar.xz',
'0a84e2791bfe3077cf22ee1784c805d5bb550803dffe56a39aa3690a38061372',
'lib/libmpdclient.a',
)
libogg = AutotoolsProject(
'http://downloads.xiph.org/releases/ogg/libogg-1.3.2.tar.xz',
'5c3a34309d8b98640827e5d0991a4015',
'http://downloads.xiph.org/releases/ogg/libogg-1.3.3.tar.xz',
'4f3fc6178a533d392064f14776b23c397ed4b9f48f5de297aba73b643f955c08',
'lib/libogg.a',
['--disable-shared', '--enable-static'],
[
'--disable-shared', '--enable-static',
],
)
libvorbis = AutotoolsProject(
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.xz',
'28cb28097c07a735d6af56e598e1c90f',
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.6.tar.xz',
'af00bb5a784e7c9e69f56823de4637c350643deedaf333d0fa86ecdba6fcb415',
'lib/libvorbis.a',
['--disable-shared', '--enable-static'],
[
'--disable-shared', '--enable-static',
],
edits={
# this option is not understood by clang
'configure': lambda data: data.replace('-mno-ieee-fp', ' '),
}
)
opus = AutotoolsProject(
'http://downloads.xiph.org/releases/opus/opus-1.1.4.tar.gz',
'9122b6b380081dd2665189f97bfd777f04f92dc3ab6698eea1dbb27ad59d8692',
'https://archive.mozilla.org/pub/opus/opus-1.2.1.tar.gz',
'cfafd339ccd9c5ef8d6ab15d7e1a412c054bf4cb4ecbbbcc78c12ef2def70732',
'lib/libopus.a',
['--disable-shared', '--enable-static'],
[
'--disable-shared', '--enable-static',
'--disable-doc',
'--disable-extra-programs',
],
# suppress "visibility default" from opus_defines.h
cppflags='-DOPUS_EXPORT=',
)
flac = AutotoolsProject(
@@ -32,7 +58,9 @@ flac = AutotoolsProject(
[
'--disable-shared', '--enable-static',
'--disable-xmms-plugin', '--disable-cpplibs',
'--disable-doxygen-docs',
],
subdirs=['include', 'src/libFLAC'],
)
zlib = ZlibProject(
@@ -45,21 +73,47 @@ libid3tag = AutotoolsProject(
'ftp://ftp.mars.org/pub/mpeg/libid3tag-0.15.1b.tar.gz',
'e5808ad997ba32c498803822078748c3',
'lib/libid3tag.a',
['--disable-shared', '--enable-static'],
[
'--disable-shared', '--enable-static',
# without this, libid3tag's configure.ac ignores -O* and -f*
'--disable-debugging',
],
autogen=True,
edits={
# fix bug in libid3tag's configure.ac which discards all but the last optimization flag
'configure.ac': lambda data: re.sub(r'optimize="\$1"', r'optimize="$optimize $1"', data, count=1),
}
)
libmad = AutotoolsProject(
'ftp://ftp.mars.org/pub/mpeg/libmad-0.15.1b.tar.gz',
'1be543bc30c56fb6bea1d7bf6a64e66c',
'lib/libmad.a',
['--disable-shared', '--enable-static'],
[
'--disable-shared', '--enable-static',
# without this, libmad's configure.ac ignores -O* and -f*
'--disable-debugging',
],
autogen=True,
)
liblame = AutotoolsProject(
'http://downloads.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz',
'ddfe36cab873794038ae2c1210557ad34857a4b6bdc515785d1da9e175b1da1e',
'lib/libmp3lame.a',
[
'--disable-shared', '--enable-static',
'--disable-gtktest', '--disable-analyzer-hooks',
'--disable-decoder', '--disable-frontend',
],
)
ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-3.3.1.tar.xz',
'b702a7fc656ac23e276b8c823a2f646e4e6f6309bb2788435a708e69bea98f2f',
'http://ffmpeg.org/releases/ffmpeg-4.0.tar.xz',
'ed945daf40b124e77a685893cc025d086f638bc703183460aff49508edb3a43f',
'lib/libavcodec.a',
[
'--disable-shared', '--enable-static',
@@ -73,17 +127,222 @@ ffmpeg = FfmpegProject(
'--disable-swscale',
'--disable-postproc',
'--disable-avfilter',
'--disable-lzo',
'--disable-faan',
'--disable-pixelutils',
'--disable-network',
'--disable-encoders',
'--disable-muxers',
'--disable-protocols',
'--disable-outdevs',
'--disable-devices',
'--disable-filters',
'--disable-v4l2_m2m',
'--disable-parser=bmp',
'--disable-parser=cavsvideo',
'--disable-parser=dvbsub',
'--disable-parser=dvdsub',
'--disable-parser=dvd_nav',
'--disable-parser=flac',
'--disable-parser=g729',
'--disable-parser=gsm',
'--disable-parser=h261',
'--disable-parser=h263',
'--disable-parser=h264',
'--disable-parser=hevc',
'--disable-parser=mjpeg',
'--disable-parser=mlp',
'--disable-parser=mpeg4video',
'--disable-parser=mpegvideo',
'--disable-parser=opus',
'--disable-parser=vc1',
'--disable-parser=vp3',
'--disable-parser=vp8',
'--disable-parser=vp9',
'--disable-parser=png',
'--disable-parser=pnm',
'--disable-parser=xma',
'--disable-demuxer=aqtitle',
'--disable-demuxer=ass',
'--disable-demuxer=bethsoftvid',
'--disable-demuxer=bink',
'--disable-demuxer=cavsvideo',
'--disable-demuxer=cdxl',
'--disable-demuxer=dvbsub',
'--disable-demuxer=dvbtxt',
'--disable-demuxer=h261',
'--disable-demuxer=h263',
'--disable-demuxer=h264',
'--disable-demuxer=ico',
'--disable-demuxer=image2',
'--disable-demuxer=jacosub',
'--disable-demuxer=lrc',
'--disable-demuxer=microdvd',
'--disable-demuxer=mjpeg',
'--disable-demuxer=mjpeg_2000',
'--disable-demuxer=mpegps',
'--disable-demuxer=mpegvideo',
'--disable-demuxer=mpl2',
'--disable-demuxer=mpsub',
'--disable-demuxer=pjs',
'--disable-demuxer=rawvideo',
'--disable-demuxer=realtext',
'--disable-demuxer=sami',
'--disable-demuxer=scc',
'--disable-demuxer=srt',
'--disable-demuxer=stl',
'--disable-demuxer=subviewer',
'--disable-demuxer=subviewer1',
'--disable-demuxer=swf',
'--disable-demuxer=tedcaptions',
'--disable-demuxer=vobsub',
'--disable-demuxer=vplayer',
'--disable-demuxer=webvtt',
'--disable-demuxer=yuv4mpegpipe',
# we don't need these decoders, because we have the dedicated
# libraries
'--disable-decoder=flac',
'--disable-decoder=opus',
'--disable-decoder=vorbis',
# audio codecs nobody uses
'--disable-decoder=atrac1',
'--disable-decoder=atrac3',
'--disable-decoder=atrac3al',
'--disable-decoder=atrac3p',
'--disable-decoder=atrac3pal',
'--disable-decoder=binkaudio_dct',
'--disable-decoder=binkaudio_rdft',
'--disable-decoder=bmv_audio',
'--disable-decoder=dsicinaudio',
'--disable-decoder=dvaudio',
'--disable-decoder=metasound',
'--disable-decoder=paf_audio',
'--disable-decoder=ra_144',
'--disable-decoder=ra_288',
'--disable-decoder=ralf',
'--disable-decoder=qdm2',
'--disable-decoder=qdmc',
# disable lots of image and video codecs
'--disable-decoder=ass',
'--disable-decoder=asv1',
'--disable-decoder=asv2',
'--disable-decoder=apng',
'--disable-decoder=avrn',
'--disable-decoder=avrp',
'--disable-decoder=bethsoftvid',
'--disable-decoder=bink',
'--disable-decoder=bmp',
'--disable-decoder=bmv_video',
'--disable-decoder=cavs',
'--disable-decoder=ccaption',
'--disable-decoder=cdgraphics',
'--disable-decoder=clearvideo',
'--disable-decoder=dirac',
'--disable-decoder=dsicinvideo',
'--disable-decoder=dvbsub',
'--disable-decoder=dvdsub',
'--disable-decoder=dvvideo',
'--disable-decoder=exr',
'--disable-decoder=ffv1',
'--disable-decoder=ffvhuff',
'--disable-decoder=ffwavesynth',
'--disable-decoder=flic',
'--disable-decoder=flv',
'--disable-decoder=fraps',
'--disable-decoder=gif',
'--disable-decoder=h261',
'--disable-decoder=h263',
'--disable-decoder=h263i',
'--disable-decoder=h263p',
'--disable-decoder=h264',
'--disable-decoder=hevc',
'--disable-decoder=hnm4_video',
'--disable-decoder=hq_hqa',
'--disable-decoder=hqx',
'--disable-decoder=idcin',
'--disable-decoder=iff_ilbm',
'--disable-decoder=indeo2',
'--disable-decoder=indeo3',
'--disable-decoder=indeo4',
'--disable-decoder=indeo5',
'--disable-decoder=interplay_video',
'--disable-decoder=jacosub',
'--disable-decoder=jpeg2000',
'--disable-decoder=jpegls',
'--disable-decoder=microdvd',
'--disable-decoder=mimic',
'--disable-decoder=mjpeg',
'--disable-decoder=mmvideo',
'--disable-decoder=mpl2',
'--disable-decoder=motionpixels',
'--disable-decoder=mpeg1video',
'--disable-decoder=mpeg2video',
'--disable-decoder=mpeg4',
'--disable-decoder=mpegvideo',
'--disable-decoder=mscc',
'--disable-decoder=msmpeg4_crystalhd',
'--disable-decoder=msmpeg4v1',
'--disable-decoder=msmpeg4v2',
'--disable-decoder=msmpeg4v3',
'--disable-decoder=msvideo1',
'--disable-decoder=mszh',
'--disable-decoder=mvc1',
'--disable-decoder=mvc2',
'--disable-decoder=on2avc',
'--disable-decoder=paf_video',
'--disable-decoder=png',
'--disable-decoder=qdraw',
'--disable-decoder=qpeg',
'--disable-decoder=rawvideo',
'--disable-decoder=realtext',
'--disable-decoder=roq',
'--disable-decoder=roq_dpcm',
'--disable-decoder=rscc',
'--disable-decoder=rv10',
'--disable-decoder=rv20',
'--disable-decoder=rv30',
'--disable-decoder=rv40',
'--disable-decoder=sami',
'--disable-decoder=sheervideo',
'--disable-decoder=snow',
'--disable-decoder=srt',
'--disable-decoder=stl',
'--disable-decoder=subrip',
'--disable-decoder=subviewer',
'--disable-decoder=subviewer1',
'--disable-decoder=svq1',
'--disable-decoder=svq3',
'--disable-decoder=tiff',
'--disable-decoder=tiertexseqvideo',
'--disable-decoder=truemotion1',
'--disable-decoder=truemotion2',
'--disable-decoder=truemotion2rt',
'--disable-decoder=twinvq',
'--disable-decoder=utvideo',
'--disable-decoder=vc1',
'--disable-decoder=vmdvideo',
'--disable-decoder=vp3',
'--disable-decoder=vp5',
'--disable-decoder=vp6',
'--disable-decoder=vp7',
'--disable-decoder=vp8',
'--disable-decoder=vp9',
'--disable-decoder=vqa',
'--disable-decoder=webvtt',
'--disable-decoder=wmv1',
'--disable-decoder=wmv2',
'--disable-decoder=wmv3',
'--disable-decoder=yuv4',
],
)
curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.54.0.tar.lzma',
'cd6aa6039f13e0b06e0a93e1b93754f6dc07f444812bb6c32be75a8f28c4070a',
'http://curl.haxx.se/download/curl-7.60.0.tar.xz',
'8736ff8ded89ddf7e926eec7b16f82597d029fc1469f3a551f1fafaac164e6a0',
'lib/libcurl.a',
[
'--disable-shared', '--enable-static',
@@ -94,16 +353,34 @@ curl = AutotoolsProject(
'--disable-ldap', '--disable-ldaps',
'--disable-rtsp', '--disable-proxy', '--disable-dict', '--disable-telnet',
'--disable-tftp', '--disable-pop3', '--disable-imap', '--disable-smtp',
'--disable-smb',
'--disable-gopher',
'--disable-manual',
'--disable-threaded-resolver', '--disable-verbose', '--disable-sspi',
'--disable-crypto-auth', '--disable-ntlm-wb', '--disable-tls-srp', '--disable-cookies',
'--without-ssl', '--without-gnutls', '--without-nss', '--without-libssh2',
],
patches='src/lib/curl/patches',
)
libnfs = AutotoolsProject(
'https://github.com/sahlberg/libnfs/archive/libnfs-2.0.0.tar.gz',
'7ea6cd8fa6c461d01091e584d424d28e137d23ff4b65b95d01a3fd0ef95d120e',
'lib/libnfs.a',
[
'--disable-shared', '--enable-static',
'--disable-debug',
# work around -Wtautological-compare
'--disable-werror',
],
base='libnfs-libnfs-2.0.0',
autoreconf=True,
)
boost = BoostProject(
'http://downloads.sourceforge.net/project/boost/boost/1.64.0/boost_1_64_0.tar.bz2',
'7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332',
'http://downloads.sourceforge.net/project/boost/boost/1.66.0/boost_1_66_0.tar.bz2',
'5721818253e6a0989583192f96782c4a98eb6204965316df9f5ad75819225ca9',
'include/boost/version.hpp',
)

@@ -0,0 +1,28 @@
import subprocess
from build.project import Project
class MakeProject(Project):
def __init__(self, url, md5, installed,
install_target='install',
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.install_target = install_target
def get_simultaneous_jobs(self):
return 12
def get_make_args(self, toolchain):
return ['--quiet', '-j' + str(self.get_simultaneous_jobs())]
def get_make_install_args(self, toolchain):
return ['--quiet', self.install_target]
def make(self, toolchain, wd, args):
subprocess.check_call(['/usr/bin/make'] + args,
cwd=wd, env=toolchain.env)
def build(self, toolchain, wd, install=True):
self.make(toolchain, wd, self.get_make_args(toolchain))
if install:
self.make(toolchain, wd, self.get_make_install_args(toolchain))

96
python/build/meson.py Normal file

@@ -0,0 +1,96 @@
import os.path, subprocess, sys
from build.project import Project
class MesonProject(Project):
def __init__(self, url, md5, installed, configure_args=[],
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args
def _make_cross_file(self, toolchain):
if toolchain.is_windows:
system = 'windows'
else:
system = 'linux'
if toolchain.is_arm:
cpu_family = 'arm'
if toolchain.is_armv7:
cpu = 'armv7'
else:
cpu = 'armv6'
else:
cpu_family = 'x86'
if 'x86_64' in toolchain.arch:
cpu = 'x86_64'
else:
cpu = 'i686'
# TODO: support more CPUs
endian = 'little'
# TODO: write pkg-config wrapper
path = os.path.join(toolchain.build_path, 'meson.cross')
os.makedirs(toolchain.build_path, exist_ok=True)
with open(path, 'w') as f:
f.write("""
[binaries]
c = '%s'
cpp = '%s'
ar = '%s'
strip = '%s'
[properties]
root = '%s'
c_args = %s
c_link_args = %s
cpp_args = %s
cpp_link_args = %s
[host_machine]
system = '%s'
cpu_family = '%s'
cpu = '%s'
endian = '%s'
""" % (toolchain.cc, toolchain.cxx, toolchain.ar, toolchain.strip,
toolchain.install_prefix,
repr((toolchain.cppflags + ' ' + toolchain.cflags).split()),
repr(toolchain.ldflags.split()),
repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split()),
repr(toolchain.ldflags.split()),
system, cpu_family, cpu, endian))
return path
def configure(self, toolchain):
src = self.unpack(toolchain)
cross_file = self._make_cross_file(toolchain)
build = self.make_build_path(toolchain)
configure = [
'meson',
src, build,
'--prefix', toolchain.install_prefix,
# this is necessary because Meson uses Debian's build machine
# MultiArch path (e.g. "lib/x86_64-linux-gnu") for cross
# builds, which is obviously wrong
'--libdir', 'lib',
'--buildtype', 'plain',
'--default-library=static',
'--cross-file', cross_file,
] + self.configure_args
subprocess.check_call(configure, env=toolchain.env)
return build
def build(self, toolchain):
build = self.configure(toolchain)
subprocess.check_call(['ninja', 'install'],
cwd=build, env=toolchain.env)

@@ -3,10 +3,13 @@ import re
from build.download import download_and_verify
from build.tar import untar
from build.quilt import push_all
class Project:
def __init__(self, url, md5, installed, name=None, version=None,
base=None,
patches=None,
edits=None,
use_cxx=False):
if base is None:
basename = os.path.basename(url)
@@ -17,7 +20,7 @@ class Project:
self.base = base
if name is None or version is None:
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?)$', self.base)
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*)$', self.base)
if name is None: name = m.group(1)
if version is None: version = m.group(2)
@@ -28,6 +31,11 @@ class Project:
self.md5 = md5
self.installed = installed
if patches is not None:
srcdir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
patches = os.path.join(srcdir, patches)
self.patches = patches
self.edits = edits
self.use_cxx = use_cxx
def download(self, toolchain):
@@ -47,7 +55,21 @@ class Project:
parent_path = toolchain.src_path
else:
parent_path = toolchain.build_path
return untar(self.download(toolchain), parent_path, self.base)
path = untar(self.download(toolchain), parent_path, self.base)
if self.patches is not None:
push_all(toolchain, path, self.patches)
if self.edits is not None:
for filename, function in self.edits.items():
with open(os.path.join(path, filename), 'r+t') as f:
old_data = f.read()
new_data = function(old_data)
f.seek(0)
f.truncate(0)
f.write(new_data)
return path
def make_build_path(self, toolchain):
path = os.path.join(toolchain.build_path, self.base)

9
python/build/quilt.py Normal file

@@ -0,0 +1,9 @@
import subprocess
def run_quilt(toolchain, cwd, patches_path, *args):
env = dict(toolchain.env)
env['QUILT_PATCHES'] = patches_path
subprocess.check_call(['quilt'] + list(args), cwd=cwd, env=env)
def push_all(toolchain, src_path, patches_path):
run_quilt(toolchain, src_path, patches_path, 'push', '-a')

@@ -67,7 +67,7 @@
#include <stdio.h>
#include <stdlib.h>
#ifdef WIN32
#ifdef _WIN32
#define CONFIG_FILE_LOCATION PATH_LITERAL("mpd\\mpd.conf")
#define APP_CONFIG_FILE_LOCATION PATH_LITERAL("conf\\mpd.conf")
#else
@@ -389,7 +389,7 @@ ParseCommandLine(int argc, char **argv, struct options *options)
ConfigLoader loader;
bool found =
#ifdef WIN32
#ifdef _WIN32
loader.TryFile(GetUserConfigDir(), CONFIG_FILE_LOCATION) ||
loader.TryFile(GetSystemConfigDir(), CONFIG_FILE_LOCATION) ||
loader.TryFile(GetAppBaseDir(), APP_CONFIG_FILE_LOCATION);

@@ -27,12 +27,17 @@
#include <assert.h>
static struct {
static struct IOThread {
Mutex mutex;
Cond cond;
EventLoop *loop;
Thread thread;
IOThread():thread(BIND_THIS_METHOD(Run)) {}
private:
void Run() noexcept;
} io;
void
@@ -44,15 +49,15 @@ io_thread_run(void)
io.loop->Run();
}
static void
io_thread_func(gcc_unused void *arg)
inline void
IOThread::Run() noexcept
{
SetThreadName("io");
/* lock+unlock to synchronize with io_thread_start(), to be
sure that io.thread is set */
io.mutex.lock();
io.mutex.unlock();
mutex.lock();
mutex.unlock();
io_thread_run();
}
@@ -73,7 +78,7 @@ io_thread_start()
assert(!io.thread.IsDefined());
const std::lock_guard<Mutex> protect(io.mutex);
io.thread.Start(io_thread_func, nullptr);
io.thread.Start();
}
void
@@ -103,8 +108,12 @@ io_thread_get() noexcept
return *io.loop;
}
#ifndef NDEBUG
bool
io_thread_inside() noexcept
{
return io.thread.IsInside();
}
#endif

@@ -20,6 +20,7 @@
#ifndef MPD_IO_THREAD_HXX
#define MPD_IO_THREAD_HXX
#include "check.h"
#include "Compiler.h"
class EventLoop;
@@ -53,6 +54,8 @@ gcc_const
EventLoop &
io_thread_get() noexcept;
#ifndef NDEBUG
/**
* Is the current thread the I/O thread?
*/
@@ -61,3 +64,5 @@ bool
io_thread_inside() noexcept;
#endif
#endif

@@ -24,7 +24,7 @@
#include "Compiler.h"
#include "fs/AllocatedPath.hxx"
#ifdef WIN32
#ifdef _WIN32
#include <windows.h>
/* damn you, windows.h! */
#ifdef ABSOLUTE

@@ -162,7 +162,7 @@ FileLog(const Domain &domain, const char *message)
domain.GetName(),
chomp_length(message), message);
#ifdef WIN32
#ifdef _WIN32
/* force-flush the log file, because setvbuf() does not seem
to have an effect on WIN32 */
fflush(stderr);

@@ -72,7 +72,7 @@ log_init_file(int line)
out_fd = open_log_file();
if (out_fd < 0) {
#ifdef WIN32
#ifdef _WIN32
const std::string out_path_utf8 = out_path.ToUTF8();
throw FormatRuntimeError("failed to open log file \"%s\" (config line %d)",
out_path_utf8.c_str(), line);
@@ -182,7 +182,7 @@ void setup_log_output()
fflush(nullptr);
if (out_fd < 0) {
#ifdef WIN32
#ifdef _WIN32
return;
#else
out_fd = open("/dev/null", O_WRONLY);

@@ -20,7 +20,7 @@
#ifndef MPD_LOG_LEVEL_HXX
#define MPD_LOG_LEVEL_HXX
#ifdef WIN32
#ifdef _WIN32
#include <windows.h>
/* damn you, windows.h! */
#ifdef ERROR

@@ -50,6 +50,7 @@
#include "unix/SignalHandlers.hxx"
#include "system/FatalError.hxx"
#include "thread/Slack.hxx"
#include "net/Init.hxx"
#include "lib/icu/Init.hxx"
#include "config/ConfigGlobal.hxx"
#include "config/Param.hxx"
@@ -106,15 +107,6 @@
#include <locale.h>
#endif
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#ifdef __BLOCKS__
#include <dispatch/dispatch.h>
#endif
#include <limits.h>
static constexpr size_t KILOBYTE = 1024;
@@ -284,25 +276,6 @@ glue_state_file_init()
instance->state_file->Read();
}
/**
* Windows-only initialization of the Winsock2 library.
*/
static void winsock_init(void)
{
#ifdef WIN32
WSADATA sockinfo;
int retval = WSAStartup(MAKEWORD(2, 2), &sockinfo);
if(retval != 0)
FormatFatalError("Attempt to open Winsock2 failed; error code %d",
retval);
if (LOBYTE(sockinfo.wVersion) != 2)
FatalError("We use Winsock2 but your version is either too new "
"or old; please install Winsock 2.x");
#endif
}
/**
* Initialize the decoder and player core, including the music pipe.
*/
@@ -418,7 +391,7 @@ Instance::OnIdle(unsigned flags)
int main(int argc, char *argv[])
{
#ifdef WIN32
#ifdef _WIN32
return win32_main(argc, argv);
#else
return mpd_main(argc, argv);
@@ -451,7 +424,8 @@ try {
IcuInit();
winsock_init();
const ScopeNetInit net_init;
io_thread_init();
config_global_init();
@@ -505,21 +479,8 @@ try {
daemonize_begin(options.daemon);
#endif
#ifdef __BLOCKS__
/* Runs the OS X native event loop in the main thread, and runs
the rest of mpd_main on a new thread. This lets CoreAudio receive
route change notifications (e.g. plugging or unplugging headphones).
All hardware output on OS X ultimately uses CoreAudio internally.
This must be run after forking; if dispatch is called before forking,
the child process will have a broken internal dispatch state. */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
exit(mpd_main_after_fork(config));
});
dispatch_main();
return EXIT_FAILURE; // unreachable, because dispatch_main never returns
#else
return mpd_main_after_fork(config);
#endif
} catch (const std::exception &e) {
LogError(e);
return EXIT_FAILURE;
@@ -615,7 +576,7 @@ try {
playlist_state_restore() */
instance->partition->pc.LockUpdateAudio();
#ifdef WIN32
#ifdef _WIN32
win32_app_started();
#endif
@@ -630,7 +591,7 @@ try {
/* run the main loop */
instance->event_loop.Run();
#ifdef WIN32
#ifdef _WIN32
win32_app_stopping();
#endif
@@ -702,10 +663,6 @@ try {
daemonize_finish();
#endif
#ifdef WIN32
WSACleanup();
#endif
IcuFinish();
log_deinit();

@@ -42,7 +42,7 @@ int mpd_main(int argc, char *argv[]);
#endif
#ifdef WIN32
#ifdef _WIN32
/**
* If program is run as windows service performs nessesary initialization

@@ -79,12 +79,20 @@ struct MusicChunk {
*/
ReplayGainInfo replay_gain_info;
/**
* A magic value for #replay_gain_serial which omits updating
* the #ReplayGainFilter. This is used by "silence" chunks
* (see PlayerThread::SendSilence()) so they don't affect the
* replay gain.
*/
static constexpr unsigned IGNORE_REPLAY_GAIN = ~0u;
/**
* A serial number for checking if replay gain info has
* changed since the last chunk. The magic value 0 indicates
* that there is no replay gain info available.
*/
unsigned replay_gain_serial = 0;
unsigned replay_gain_serial;
/** the data (probably PCM) */
uint8_t data[CHUNK_SIZE];

@@ -95,6 +95,7 @@ public:
*/
gcc_pure
const MusicChunk *Peek() const noexcept {
const std::lock_guard<Mutex> protect(mutex);
return head;
}
@@ -120,6 +121,7 @@ public:
*/
gcc_pure
unsigned GetSize() const noexcept {
const std::lock_guard<Mutex> protect(mutex);
return size;
}

@@ -207,13 +207,12 @@ try {
continue;
#ifdef _UNICODE
wchar_t buffer[MAX_PATH];
auto result = MultiByteToWideChar(CP_ACP, 0, s, -1,
buffer, ARRAY_SIZE(buffer));
if (result <= 0)
/* on Windows, playlists always contain UTF-8, because
its "narrow" charset (i.e. CP_ACP) is incapable of
storing all Unicode paths */
const auto path = AllocatedPath::FromUTF8(s);
if (path.IsNull())
continue;
const Path path = Path::FromFS(buffer);
#else
const Path path = Path::FromFS(s);
#endif

@@ -28,13 +28,25 @@
#include "fs/AllocatedPath.hxx"
#include "fs/Traits.hxx"
#include "fs/FileSystem.hxx"
#include "fs/NarrowPath.hxx"
#include "fs/io/FileOutputStream.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "util/UriUtil.hxx"
#include <stdexcept>
static void
playlist_print_path(BufferedOutputStream &os, const Path path)
{
#ifdef _UNICODE
/* on Windows, playlists always contain UTF-8, because its
"narrow" charset (i.e. CP_ACP) is incapable of storing all
Unicode paths */
os.Format("%s\n", path.ToUTF8().c_str());
#else
os.Format("%s\n", path.c_str());
#endif
}
void
playlist_print_song(BufferedOutputStream &os, const DetachedSong &song)
{
@@ -44,7 +56,7 @@ playlist_print_song(BufferedOutputStream &os, const DetachedSong &song)
try {
const auto uri_fs = AllocatedPath::FromUTF8Throw(uri_utf8);
os.Format("%s\n", NarrowPath(uri_fs).c_str());
playlist_print_path(os, uri_fs);
} catch (const std::runtime_error &) {
}
}
@@ -63,7 +75,7 @@ playlist_print_uri(BufferedOutputStream &os, const char *uri)
AllocatedPath::FromUTF8Throw(uri);
if (!path.IsNull())
os.Format("%s\n", NarrowPath(path).c_str());
playlist_print_path(os, path);
} catch (const std::runtime_error &) {
}
}

@@ -24,10 +24,12 @@
#include "tag/Tag.hxx"
#include "util/ConstBuffer.hxx"
#include "util/StringAPI.hxx"
#include "util/StringCompare.hxx"
#include "util/StringView.hxx"
#include "util/ASCII.hxx"
#include "util/TimeParser.hxx"
#include "util/UriUtil.hxx"
#include "lib/icu/Collate.hxx"
#include "lib/icu/CaseFold.hxx"
#include <stdexcept>
@@ -57,22 +59,15 @@ locate_parse_type(const char *str) noexcept
return tag_name_parse_i(str);
}
static AllocatedString<>
ImportString(const char *p, bool fold_case)
{
return fold_case
? IcuCaseFold(p)
: AllocatedString<>::Duplicate(p);
}
SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
:tag(_tag), fold_case(_fold_case),
value(ImportString(_value, _fold_case))
:tag(_tag),
value(_value),
fold_case(_fold_case ? IcuCompare(value.c_str()) : IcuCompare())
{
}
SongFilter::Item::Item(unsigned _tag, time_t _time)
:tag(_tag), value(nullptr), time(_time)
:tag(_tag), time(_time)
{
}
@@ -87,9 +82,7 @@ SongFilter::Item::StringMatch(const char *s) const noexcept
assert(tag != LOCATE_TAG_MODIFIED_SINCE);
if (fold_case) {
const auto folded = IcuCaseFold(s);
assert(!folded.IsNull());
return StringFind(folded.c_str(), value.c_str()) != nullptr;
return fold_case.IsIn(s);
} else {
return StringIsEqual(s, value.c_str());
}
@@ -283,3 +276,33 @@ SongFilter::GetBase() const noexcept
return nullptr;
}
SongFilter
SongFilter::WithoutBasePrefix(const char *_prefix) const noexcept
{
const StringView prefix(_prefix);
SongFilter result;
for (const auto &i : items) {
if (i.GetTag() == LOCATE_TAG_BASE_TYPE) {
const char *s = StringAfterPrefix(i.GetValue(), prefix);
if (s != nullptr) {
if (*s == 0)
continue;
if (*s == '/') {
++s;
if (*s != 0)
result.items.emplace_back(LOCATE_TAG_BASE_TYPE, s);
continue;
}
}
}
result.items.emplace_back(i);
}
return result;
}

@@ -20,9 +20,10 @@
#ifndef MPD_SONG_FILTER_HXX
#define MPD_SONG_FILTER_HXX
#include "util/AllocatedString.hxx"
#include "lib/icu/Compare.hxx"
#include "Compiler.h"
#include <string>
#include <list>
#include <stdint.h>
@@ -48,9 +49,12 @@ public:
class Item {
uint8_t tag;
bool fold_case;
std::string value;
AllocatedString<> value;
/**
* This value is only set if case folding is enabled.
*/
IcuCompare fold_case;
/**
* For #LOCATE_TAG_MODIFIED_SINCE
@@ -62,11 +66,6 @@ public:
Item(unsigned tag, const char *value, bool fold_case=false);
Item(unsigned tag, time_t time);
Item(const Item &other) = delete;
Item(Item &&) = default;
Item &operator=(const Item &other) = delete;
unsigned GetTag() const {
return tag;
}
@@ -153,6 +152,13 @@ public:
*/
gcc_pure
const char *GetBase() const noexcept;
/**
* Create a copy of the filter with the given prefix stripped
* from all #LOCATE_TAG_BASE_TYPE items. This is used to
* filter songs in mounted databases.
*/
SongFilter WithoutBasePrefix(const char *prefix) const noexcept;
};
/**

@@ -28,6 +28,7 @@
#include "tag/TagBuilder.hxx"
#include "util/StringUtil.hxx"
#include "util/RuntimeError.hxx"
#include "util/NumberParser.hxx"
#include <string.h>
#include <stdlib.h>
@@ -94,7 +95,7 @@ song_load(TextFile &file, const char *uri)
if ((type = tag_name_parse(line)) != TAG_NUM_OF_ITEM_TYPES) {
tag.AddItem(type, value);
} else if (strcmp(line, "Time") == 0) {
tag.SetDuration(SignedSongTime::FromS(atof(value)));
tag.SetDuration(SignedSongTime::FromS(ParseDouble(value)));
} else if (strcmp(line, "Playlist") == 0) {
tag.SetHasPlaylist(strcmp(value, "yes") == 0);
} else if (strcmp(line, SONG_MTIME) == 0) {

@@ -24,6 +24,7 @@
#include "fs/io/TextFile.hxx"
#include "fs/io/FileOutputStream.hxx"
#include "fs/io/BufferedOutputStream.hxx"
#include "storage/StorageState.hxx"
#include "Partition.hxx"
#include "Instance.hxx"
#include "mixer/Volume.hxx"
@@ -56,6 +57,9 @@ StateFile::RememberVersions() noexcept
prev_output_version = audio_output_state_get_version();
prev_playlist_version = playlist_state_get_hash(partition.playlist,
partition.pc);
#ifdef ENABLE_DATABASE
prev_storage_version = storage_state_get_hash(partition.instance);
#endif
}
bool
@@ -64,7 +68,11 @@ StateFile::IsModified() const noexcept
return prev_volume_version != sw_volume_state_get_hash() ||
prev_output_version != audio_output_state_get_version() ||
prev_playlist_version != playlist_state_get_hash(partition.playlist,
partition.pc);
partition.pc)
#ifdef ENABLE_DATABASE
|| prev_storage_version != storage_state_get_hash(partition.instance)
#endif
;
}
inline void
@@ -72,6 +80,11 @@ StateFile::Write(BufferedOutputStream &os)
{
save_sw_volume_state(os);
audio_output_state_save(os, partition.outputs);
#ifdef ENABLE_DATABASE
storage_state_save(os, partition.instance);
#endif
playlist_state_save(os, partition.playlist, partition.pc);
}
@@ -123,6 +136,10 @@ try {
playlist_state_restore(line, file, song_loader,
partition.playlist,
partition.pc);
#ifdef ENABLE_DATABASE
success = success || storage_state_restore(line, file, partition.instance);
#endif
if (!success)
FormatError(state_file_domain,
"Unrecognized line in state file: %s",

@@ -46,6 +46,10 @@ class StateFile final : private TimeoutMonitor {
unsigned prev_volume_version = 0, prev_output_version = 0,
prev_playlist_version = 0;
#ifdef ENABLE_DATABASE
unsigned prev_storage_version = 0;
#endif
public:
static constexpr std::chrono::steady_clock::duration DEFAULT_INTERVAL = std::chrono::minutes(2);

@@ -31,7 +31,7 @@
#include <chrono>
#ifndef WIN32
#ifndef _WIN32
/**
* The monotonic time stamp when MPD was started. It is used to
* calculate the uptime.
@@ -114,7 +114,7 @@ stats_print(Response &r, const Partition &partition)
{
r.Format("uptime: %u\n"
"playtime: %lu\n",
#ifdef WIN32
#ifdef _WIN32
GetProcessUptimeS(),
#else
(unsigned)std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - start_time).count(),

@@ -24,7 +24,7 @@
void
time_print(Response &r, const char *name, time_t t)
{
#ifdef WIN32
#ifdef _WIN32
const struct tm *tm2 = gmtime(&t);
#else
struct tm tm;
@@ -35,7 +35,7 @@ time_print(Response &r, const char *name, time_t t)
char buffer[32];
strftime(buffer, sizeof(buffer),
#ifdef WIN32
#ifdef _WIN32
"%Y-%m-%dT%H:%M:%SZ",
#else
"%FT%TZ",

@@ -162,7 +162,7 @@ Bzip2InputStream::FillBuffer()
if (bzstream.avail_in > 0)
return true;
size_t count = archive->istream->Read(buffer, sizeof(buffer));
size_t count = archive->istream->LockRead(buffer, sizeof(buffer));
if (count == 0)
return false;
@@ -174,6 +174,8 @@ Bzip2InputStream::FillBuffer()
size_t
Bzip2InputStream::Read(void *ptr, size_t length)
{
const ScopeUnlock unlock(mutex);
int bz_result;
size_t nbytes = 0;
@@ -224,4 +226,3 @@ const ArchivePlugin bz2_archive_plugin = {
bz2_open,
bz2_extensions,
};

@@ -115,7 +115,12 @@ Iso9660ArchiveFile::Visit(char *path, size_t length, size_t capacity,
visitor.VisitArchiveEntry(path + 1);
}
}
#if LIBCDIO_VERSION_NUM >= 20000
iso9660_filelist_free(entlist);
#else
_cdio_list_free (entlist, true);
#endif
}
static ArchiveFile *
@@ -182,6 +187,8 @@ Iso9660ArchiveFile::OpenStream(const char *pathname,
size_t
Iso9660InputStream::Read(void *ptr, size_t read_size)
{
const ScopeUnlock unlock(mutex);
int readed = 0;
int no_blocks, cur_block;
size_t left_bytes = statbuf->size - offset;

@@ -138,6 +138,8 @@ ZzipArchiveFile::OpenStream(const char *pathname,
size_t
ZzipInputStream::Read(void *ptr, size_t read_size)
{
const ScopeUnlock unlock(mutex);
int ret = zzip_file_read(file, ptr, read_size);
if (ret < 0)
throw std::runtime_error("zzip_file_read() has failed");
@@ -155,6 +157,8 @@ ZzipInputStream::IsEOF() noexcept
void
ZzipInputStream::Seek(offset_type new_offset)
{
const ScopeUnlock unlock(mutex);
zzip_off_t ofs = zzip_seek(file, new_offset, SEEK_SET);
if (ofs < 0)
throw std::runtime_error("zzip_seek() has failed");

@@ -28,7 +28,7 @@
void
Client::AllowFile(Path path_fs) const
{
#ifdef WIN32
#ifdef _WIN32
(void)path_fs;
throw ProtocolError(ACK_ERROR_PERMISSION, "Access denied");

@@ -24,7 +24,7 @@
#include <string>
#ifdef WIN32
#ifdef _WIN32
/* fuck WIN32! */
#include <windows.h>
#undef GetMessage

@@ -29,7 +29,7 @@
#include "Log.hxx"
#include <assert.h>
#ifdef WIN32
#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>

@@ -123,6 +123,10 @@ ToAck(std::exception_ptr ep) noexcept
return ACK_ERROR_SYSTEM;
} catch (const std::invalid_argument &e) {
return ACK_ERROR_ARG;
} catch (const std::length_error &e) {
return ACK_ERROR_ARG;
} catch (const std::out_of_range &e) {
return ACK_ERROR_ARG;
#ifdef GLIBCXX_49X
} catch (const std::exception &e) {
#else

@@ -20,7 +20,7 @@
#ifndef MPD_COMMAND_RESULT_HXX
#define MPD_COMMAND_RESULT_HXX
#ifdef WIN32
#ifdef _WIN32
#include <windows.h>
/* damn you, windows.h! */
#ifdef ERROR

@@ -58,7 +58,7 @@ skip_path(Path name_fs) noexcept
return name_fs.HasNewline();
}
#if defined(WIN32) && GCC_CHECK_VERSION(4,6)
#if defined(_WIN32) && GCC_CHECK_VERSION(4,6)
/* PRIu64 causes bogus compiler warning */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
@@ -101,7 +101,7 @@ handle_listfiles_local(Response &r, Path path_fs)
return CommandResult::OK;
}
#if defined(WIN32) && GCC_CHECK_VERSION(4,6)
#if defined(_WIN32) && GCC_CHECK_VERSION(4,6)
#pragma GCC diagnostic pop
#endif

@@ -50,7 +50,7 @@ skip_path(const char *name_utf8) noexcept
return strchr(name_utf8, '\n') != nullptr;
}
#if defined(WIN32) && GCC_CHECK_VERSION(4,6)
#if defined(_WIN32) && GCC_CHECK_VERSION(4,6)
/* PRIu64 causes bogus compiler warning */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
@@ -94,7 +94,7 @@ handle_listfiles_storage(Response &r, StorageDirectoryReader &reader)
}
}
#if defined(WIN32) && GCC_CHECK_VERSION(4,6)
#if defined(_WIN32) && GCC_CHECK_VERSION(4,6)
#pragma GCC diagnostic pop
#endif

@@ -22,7 +22,7 @@
#include "Compiler.h"
#if defined(WIN32) && CLANG_OR_GCC_VERSION(4,7)
#if defined(_WIN32) && CLANG_OR_GCC_VERSION(4,7)
/* "INPUT" is declared by winuser.h */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
@@ -93,7 +93,7 @@ enum class ConfigBlockOption {
MAX
};
#if defined(WIN32) && CLANG_OR_GCC_VERSION(4,7)
#if defined(_WIN32) && CLANG_OR_GCC_VERSION(4,7)
#pragma GCC diagnostic pop
#endif

@@ -29,7 +29,7 @@
#include <assert.h>
#include <string.h>
#ifndef WIN32
#ifndef _WIN32
#include <pwd.h>
/**
@@ -79,7 +79,7 @@ ParsePath(const char *path)
{
assert(path != nullptr);
#ifndef WIN32
#ifndef _WIN32
if (path[0] == '~') {
++path;
@@ -119,7 +119,7 @@ ParsePath(const char *path)
} else {
#endif
return AllocatedPath::FromUTF8Throw(path);
#ifndef WIN32
#ifndef _WIN32
}
#endif
}

@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "Selection.hxx"
#include "SongFilter.hxx"

@@ -325,6 +325,34 @@ SendConstraints(mpd_connection *connection, const DatabaseSelection &selection)
return true;
}
static bool
SendGroupMask(mpd_connection *connection, tag_mask_t mask)
{
#if LIBMPDCLIENT_CHECK_VERSION(2,12,0)
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
if ((mask & (tag_mask_t(1) << i)) == 0)
continue;
const auto tag = Convert(TagType(i));
if (tag == MPD_TAG_COUNT)
throw std::runtime_error("Unsupported tag");
if (!mpd_search_add_group_tag(connection, tag))
return false;
}
return true;
#else
(void)connection;
(void)mask;
if (mask != 0)
throw std::runtime_error("Grouping requires libmpdclient 2.12");
return true;
#endif
}
ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
const ConfigBlock &block)
:Database(proxy_db_plugin),
@@ -682,7 +710,7 @@ static void
SearchSongs(struct mpd_connection *connection,
const DatabaseSelection &selection,
VisitSong visit_song)
{
try {
assert(selection.recursive);
assert(visit_song);
@@ -709,6 +737,11 @@ SearchSongs(struct mpd_connection *connection,
if (!mpd_response_finish(connection))
ThrowError(connection);
} catch (...) {
if (connection != nullptr)
mpd_search_cancel(connection);
throw;
}
/**
@@ -756,9 +789,9 @@ ProxyDatabase::Visit(const DatabaseSelection &selection,
void
ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
TagType tag_type,
gcc_unused tag_mask_t group_mask,
tag_mask_t group_mask,
VisitTag visit_tag) const
{
try {
// TODO: eliminate the const_cast
const_cast<ProxyDatabase *>(this)->EnsureConnected();
@@ -767,32 +800,47 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
throw std::runtime_error("Unsupported tag");
if (!mpd_search_db_tags(connection, tag_type2) ||
!SendConstraints(connection, selection))
!SendConstraints(connection, selection) ||
!SendGroupMask(connection, group_mask))
ThrowError(connection);
// TODO: use group_mask
if (!mpd_search_commit(connection))
ThrowError(connection);
while (auto *pair = mpd_recv_pair_tag(connection, tag_type2)) {
TagBuilder builder;
while (auto *pair = mpd_recv_pair(connection)) {
AtScopeExit(this, pair) {
mpd_return_pair(connection, pair);
};
TagBuilder tag;
tag.AddItem(tag_type, pair->value);
const auto current_type = tag_name_parse_i(pair->name);
if (current_type == TAG_NUM_OF_ITEM_TYPES)
continue;
if (tag.IsEmpty())
if (current_type == tag_type && !builder.IsEmpty()) {
try {
visit_tag(builder.Commit());
} catch (...) {
mpd_response_finish(connection);
throw;
}
}
builder.AddItem(current_type, pair->value);
if (!builder.HasType(current_type))
/* if no tag item has been added, then the
given value was not acceptable
(e.g. empty); forcefully insert an empty
tag in this case, as the caller expects the
given tag type to be present */
tag.AddEmptyItem(tag_type);
builder.AddEmptyItem(current_type);
}
if (!builder.IsEmpty()) {
try {
visit_tag(tag.Commit());
visit_tag(builder.Commit());
} catch (...) {
mpd_response_finish(connection);
throw;
@@ -801,6 +849,11 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
if (!mpd_response_finish(connection))
ThrowError(connection);
} catch (...) {
if (connection != nullptr)
mpd_search_cancel(connection);
throw;
}
DatabaseStats

@@ -109,7 +109,7 @@ Directory::PruneEmpty() noexcept
child != end;) {
child->PruneEmpty();
if (child->IsEmpty())
if (child->IsEmpty() && !child->IsMount())
child = children.erase_and_dispose(child,
DeleteDisposer());
else
@@ -230,7 +230,7 @@ Directory::Walk(bool recursive, const SongFilter *filter,
call will lock it again */
const ScopeDatabaseUnlock unlock;
WalkMount(GetPath(), *mounted_database,
recursive, filter,
"", recursive, filter,
visit_directory, visit_song,
visit_playlist);
return;

@@ -127,7 +127,6 @@ public:
*
* @param name_utf8 the UTF-8 encoded name of the new sub directory
*/
gcc_malloc
Directory *CreateChild(const char *name_utf8);
/**

@@ -82,10 +82,11 @@ directory_save(BufferedOutputStream &os, const Directory &directory)
}
for (const auto &child : directory.children) {
os.Format(DIRECTORY_DIR "%s\n", child.GetName());
if (child.IsMount())
continue;
if (!child.IsMount())
directory_save(os, child);
os.Format(DIRECTORY_DIR "%s\n", child.GetName());
directory_save(os, child);
}
for (const auto &song : directory.songs)

@@ -20,18 +20,12 @@
#include "config.h"
#include "Mount.hxx"
#include "PrefixedLightSong.hxx"
#include "SongFilter.hxx"
#include "db/Selection.hxx"
#include "db/LightDirectory.hxx"
#include "db/Interface.hxx"
#include "fs/Traits.hxx"
#ifdef _LIBCPP_VERSION
/* workaround for "error: incomplete type 'PlaylistInfo' used in type
trait expression" with libc++ version 3900 (from Android NDK
r13b) */
#include "db/PlaylistInfo.hxx"
#endif
#include <string>
struct PrefixedLightDirectory : LightDirectory {
@@ -72,7 +66,7 @@ PrefixVisitPlaylist(const char *base, const VisitPlaylist &visit_playlist,
void
WalkMount(const char *base, const Database &db,
bool recursive, const SongFilter *filter,
const char* uri, bool recursive, const SongFilter *filter,
const VisitDirectory &visit_directory, const VisitSong &visit_song,
const VisitPlaylist &visit_playlist)
{
@@ -93,5 +87,16 @@ WalkMount(const char *base, const Database &db,
vp = std::bind(PrefixVisitPlaylist,
base, std::ref(visit_playlist), _1, _2);
db.Visit(DatabaseSelection("", recursive, filter), vd, vs, vp);
SongFilter prefix_filter;
if (base != nullptr && filter != nullptr) {
/* if the SongFilter contains a LOCATE_TAG_BASE_TYPE
item, copy the SongFilter and drop the mount point
from the filter, because the mounted database
doesn't know its own location within MPD's VFS */
prefix_filter = filter->WithoutBasePrefix(base);
filter = &prefix_filter;
}
db.Visit(DatabaseSelection(uri, recursive, filter), vd, vs, vp);
}

@@ -27,7 +27,7 @@ class SongFilter;
void
WalkMount(const char *base, const Database &db,
bool recursive, const SongFilter *filter,
const char* uri, bool recursive, const SongFilter *filter,
const VisitDirectory &visit_directory, const VisitSong &visit_song,
const VisitPlaylist &visit_playlist);

@@ -20,6 +20,7 @@
#include "config.h"
#include "SimpleDatabasePlugin.hxx"
#include "PrefixedLightSong.hxx"
#include "Mount.hxx"
#include "db/DatabasePlugin.hxx"
#include "db/Selection.hxx"
#include "db/Helpers.hxx"
@@ -115,7 +116,7 @@ SimpleDatabase::Check() const
path_utf8 + "\" because the "
"parent path is not a directory");
#ifndef WIN32
#ifndef _WIN32
/* Check if we can write to the directory */
if (!CheckAccess(dirPath, X_OK | W_OK)) {
const int e = errno;
@@ -134,7 +135,7 @@ SimpleDatabase::Check() const
if (!fi.IsRegular())
throw std::runtime_error("db file \"" + path_utf8 + "\" is not a regular file");
#ifndef WIN32
#ifndef _WIN32
/* And check that we can write to it */
if (!CheckAccess(path, R_OK | W_OK))
throw FormatErrno("Can't open db file \"%s\" for reading/writing",
@@ -270,6 +271,18 @@ SimpleDatabase::Visit(const DatabaseSelection &selection,
ScopeDatabaseLock protect;
auto r = root->LookupDirectory(selection.uri.c_str());
if (r.directory->IsMount()) {
/* pass the request and the remaining uri to the mounted database */
protect.unlock();
WalkMount(r.directory->GetPath(), *(r.directory->mounted_database),
(r.uri == nullptr)?"":r.uri, selection.recursive, selection.filter,
visit_directory, visit_song, visit_playlist);
return;
}
if (r.uri == nullptr) {
/* it's a directory */

@@ -49,6 +49,10 @@ struct UpdateQueueItem {
bool IsDefined() const {
return id != 0;
}
void Clear() {
id = 0;
}
};
class UpdateQueue {

@@ -29,6 +29,7 @@
#include "Idle.hxx"
#include "Log.hxx"
#include "thread/Thread.hxx"
#include "thread/Name.hxx"
#include "thread/Util.hxx"
#ifndef NDEBUG
@@ -43,6 +44,7 @@ UpdateService::UpdateService(EventLoop &_loop, SimpleDatabase &_db,
:DeferredMonitor(_loop),
db(_db), storage(_storage),
listener(_listener),
update_thread(BIND_THIS_METHOD(Task)),
update_task_id(0),
walk(nullptr)
{
@@ -112,6 +114,8 @@ UpdateService::Task()
{
assert(walk != nullptr);
SetThreadName("update");
if (!next.path_utf8.empty())
FormatDebug(update_domain, "starting: %s",
next.path_utf8.c_str());
@@ -140,13 +144,6 @@ UpdateService::Task()
DeferredMonitor::Schedule();
}
void
UpdateService::Task(void *ctx)
{
UpdateService &service = *(UpdateService *)ctx;
return service.Task();
}
void
UpdateService::StartThread(UpdateQueueItem &&i)
{
@@ -158,7 +155,7 @@ UpdateService::StartThread(UpdateQueueItem &&i)
next = std::move(i);
walk = new UpdateWalk(GetEventLoop(), listener, *next.storage);
update_thread.Start(Task, this);
update_thread.Start();
FormatDebug(update_domain,
"spawned thread for update job id %i", next.id);
@@ -258,7 +255,7 @@ UpdateService::RunDeferred()
delete walk;
walk = nullptr;
next = UpdateQueueItem();
next.Clear();
idle_add(IDLE_UPDATE);

@@ -98,7 +98,6 @@ private:
/* the update thread */
void Task();
static void Task(void *ctx);
void StartThread(UpdateQueueItem &&i);

@@ -91,7 +91,7 @@ bool
directory_child_access(Storage &storage, const Directory &directory,
const char *name, int mode) noexcept
{
#ifdef WIN32
#ifdef _WIN32
/* CheckAccess() is useless on WIN32 */
(void)storage;
(void)directory;

@@ -55,7 +55,7 @@ UpdateWalk::UpdateWalk(EventLoop &_loop, DatabaseListener &_listener,
storage(_storage),
editor(_loop, _listener)
{
#ifndef WIN32
#ifndef _WIN32
follow_inside_symlinks =
config_get_bool(ConfigOption::FOLLOW_INSIDE_SYMLINKS,
DEFAULT_FOLLOW_INSIDE_SYMLINKS);
@@ -104,7 +104,7 @@ inline void
UpdateWalk::PurgeDeletedFromDirectory(Directory &directory)
{
directory.ForEachChildSafe([&](Directory &child){
if (DirectoryExists(storage, child))
if (child.IsMount() || DirectoryExists(storage, child))
return;
editor.LockDeleteDirectory(&child);
@@ -133,7 +133,7 @@ UpdateWalk::PurgeDeletedFromDirectory(Directory &directory)
}
}
#ifndef WIN32
#ifndef _WIN32
static bool
update_directory_stat(Storage &storage, Directory &directory)
{
@@ -156,7 +156,7 @@ static int
FindAncestorLoop(Storage &storage, Directory *parent,
unsigned inode, unsigned device)
{
#ifndef WIN32
#ifndef _WIN32
if (device == 0 && inode == 0)
/* can't detect loops if the Storage does not support
these numbers */
@@ -258,7 +258,7 @@ bool
UpdateWalk::SkipSymlink(const Directory *directory,
const char *utf8_name) const noexcept
{
#ifndef WIN32
#ifndef _WIN32
const auto path_fs = storage.MapChildFS(directory->GetPath(),
utf8_name);
if (path_fs.IsNull())

@@ -36,7 +36,7 @@ class UpdateWalk final {
friend class UpdateArchiveVisitor;
#endif
#ifndef WIN32
#ifndef _WIN32
static constexpr bool DEFAULT_FOLLOW_INSIDE_SYMLINKS = true;
static constexpr bool DEFAULT_FOLLOW_OUTSIDE_SYMLINKS = true;

@@ -300,6 +300,7 @@ DecoderBridge::CommandFinished()
initial_seek_running = false;
timestamp = dc.start_time.ToDoubleS();
absolute_frame = dc.start_time.ToScale<uint64_t>(dc.in_audio_format.sample_rate);
return;
}
@@ -319,6 +320,7 @@ DecoderBridge::CommandFinished()
convert->Reset();
timestamp = dc.seek_time.ToDoubleS();
absolute_frame = dc.seek_time.ToScale<uint64_t>(dc.in_audio_format.sample_rate);
}
dc.command = DecoderCommand::NONE;
@@ -427,6 +429,7 @@ DecoderBridge::SubmitTimestamp(double t)
assert(t >= 0);
timestamp = t;
absolute_frame = uint64_t(t * dc.in_audio_format.sample_rate);
}
DecoderCommand
@@ -464,6 +467,29 @@ DecoderBridge::SubmitData(InputStream *is,
return cmd;
}
cmd = DecoderCommand::NONE;
const size_t frame_size = dc.in_audio_format.GetFrameSize();
size_t data_frames = length / frame_size;
if (dc.end_time.IsPositive()) {
/* enforce the given end time */
const uint64_t end_frame =
dc.end_time.ToScale<uint64_t>(dc.in_audio_format.sample_rate);
if (absolute_frame >= end_frame)
return DecoderCommand::STOP;
const uint64_t remaining_frames = end_frame - absolute_frame;
if (data_frames >= remaining_frames) {
/* past the end of the range: truncate this
data submission and stop the decoder */
data_frames = remaining_frames;
length = data_frames * frame_size;
cmd = DecoderCommand::STOP;
}
}
if (convert != nullptr) {
assert(dc.in_audio_format != dc.out_audio_format);
@@ -521,15 +547,11 @@ DecoderBridge::SubmitData(InputStream *is,
timestamp += (double)nbytes /
dc.out_audio_format.GetTimeToSize();
if (dc.end_time.IsPositive() &&
timestamp >= dc.end_time.ToDoubleS())
/* the end of this range has been reached:
stop decoding */
return DecoderCommand::STOP;
}
return DecoderCommand::NONE;
absolute_frame += data_frames;
return cmd;
}
DecoderCommand

@@ -49,6 +49,11 @@ public:
*/
double timestamp = 0;
/**
* The time stamp of the next data chunk, in PCM frames.
*/
uint64_t absolute_frame = 0;
/**
* Is the initial seek (to the start position of the sub-song)
* pending, or has it been performed already?

@@ -30,7 +30,8 @@
DecoderControl::DecoderControl(Mutex &_mutex, Cond &_client_cond,
const AudioFormat _configured_audio_format,
const ReplayGainConfig &_replay_gain_config)
:mutex(_mutex), client_cond(_client_cond),
:thread(BIND_THIS_METHOD(RunThread)),
mutex(_mutex), client_cond(_client_cond),
configured_audio_format(_configured_audio_format),
replay_gain_config(_replay_gain_config) {}

@@ -308,9 +308,14 @@ struct DecoderControl {
bool IsCurrentSong(const DetachedSong &_song) const noexcept;
gcc_pure
bool LockIsCurrentSong(const DetachedSong &_song) const noexcept {
bool IsSeekableCurrentSong(const DetachedSong &_song) const noexcept {
return seekable && IsCurrentSong(_song);
}
gcc_pure
bool LockIsSeeakbleCurrentSong(const DetachedSong &_song) const noexcept {
const std::lock_guard<Mutex> protect(mutex);
return IsCurrentSong(_song);
return IsSeekableCurrentSong(_song);
}
private:
@@ -415,6 +420,9 @@ public:
* mixramp_start/mixramp_end.
*/
void CycleMixRamp();
private:
void RunThread();
};
#endif

@@ -508,34 +508,33 @@ try {
decoder_run_song(dc, song, uri_utf8, path_fs);
} catch (...) {
dc.state = DecoderState::ERROR;
dc.command = DecoderCommand::NONE;
dc.error = std::current_exception();
dc.client_cond.signal();
}
static void
decoder_task(void *arg)
void
DecoderControl::RunThread()
{
DecoderControl &dc = *(DecoderControl *)arg;
SetThreadName("decoder");
const std::lock_guard<Mutex> protect(dc.mutex);
const std::lock_guard<Mutex> protect(mutex);
do {
assert(dc.state == DecoderState::STOP ||
dc.state == DecoderState::ERROR);
assert(state == DecoderState::STOP ||
state == DecoderState::ERROR);
switch (dc.command) {
switch (command) {
case DecoderCommand::START:
dc.CycleMixRamp();
dc.replay_gain_prev_db = dc.replay_gain_db;
dc.replay_gain_db = 0;
CycleMixRamp();
replay_gain_prev_db = replay_gain_db;
replay_gain_db = 0;
decoder_run(dc);
decoder_run(*this);
if (dc.state == DecoderState::ERROR) {
if (state == DecoderState::ERROR) {
try {
std::rethrow_exception(dc.error);
std::rethrow_exception(error);
} catch (const std::exception &e) {
LogError(e);
} catch (...) {
@@ -551,20 +550,20 @@ decoder_task(void *arg)
/* we need to clear the pipe here; usually the
PlayerThread is responsible, but it is not
aware that the decoder has finished */
dc.pipe->Clear(*dc.buffer);
pipe->Clear(*buffer);
decoder_run(dc);
decoder_run(*this);
break;
case DecoderCommand::STOP:
dc.CommandFinishedLocked();
CommandFinishedLocked();
break;
case DecoderCommand::NONE:
dc.Wait();
Wait();
break;
}
} while (dc.command != DecoderCommand::NONE || !dc.quit);
} while (command != DecoderCommand::NONE || !quit);
}
void
@@ -573,5 +572,5 @@ decoder_thread_start(DecoderControl &dc)
assert(!dc.thread.IsDefined());
dc.quit = false;
dc.thread.Start(decoder_task, &dc);
dc.thread.Start();
}

@@ -128,7 +128,7 @@ dsdlib_tag_id3(InputStream &is,
return;
const auto count64 = size - tagoffset;
if (count64 < 10 || count64 > 1024 * 1024)
if (count64 < 10 || count64 > 4 * 1024 * 1024)
return;
if (!dsdlib_skip_to(nullptr, is, tagoffset))

@@ -484,6 +484,8 @@ static const char *const dsdiff_suffixes[] = {
static const char *const dsdiff_mime_types[] = {
"application/x-dff",
"audio/x-dff",
"audio/x-dsd",
nullptr
};

@@ -358,6 +358,8 @@ static const char *const dsf_suffixes[] = {
static const char *const dsf_mime_types[] = {
"application/x-dsf",
"audio/x-dsf",
"audio/x-dsd",
nullptr
};

@@ -258,7 +258,7 @@ FfmpegSendFrame(DecoderClient &client, InputStream &is,
try {
output_buffer = copy_interleave_frame(codec_context, frame,
buffer);
} catch (const std::exception e) {
} catch (const std::exception &e) {
/* this must be a serious error, e.g. OOM */
LogError(e);
return DecoderCommand::STOP;
@@ -712,7 +712,9 @@ FfmpegDecode(DecoderClient &client, InputStream &input,
#endif
const SignedSongTime total_time =
FromFfmpegTimeChecked(av_stream.duration, av_stream.time_base);
av_stream.duration != (int64_t)AV_NOPTS_VALUE
? FromFfmpegTimeChecked(av_stream.duration, av_stream.time_base)
: FromFfmpegTimeChecked(format_context.duration, AV_TIME_BASE_Q);
client.Ready(audio_format, input.IsSeekable(), total_time);
@@ -842,6 +844,10 @@ FfmpegScanStream(AVFormatContext &format_context,
tag_handler_invoke_duration(handler, handler_ctx,
FromFfmpegTime(stream.duration,
stream.time_base));
else if (format_context.duration != (int64_t)AV_NOPTS_VALUE)
tag_handler_invoke_duration(handler, handler_ctx,
FromFfmpegTime(format_context.duration,
AV_TIME_BASE_Q));
FfmpegScanMetadata(format_context, audio_stream, handler, handler_ctx);

@@ -24,6 +24,7 @@
#include "FfmpegMetaData.hxx"
#include "tag/TagTable.hxx"
#include "tag/TagHandler.hxx"
#include "tag/Id3MusicBrainz.hxx"
extern "C" {
#include <libavutil/dict.h>
@@ -75,6 +76,11 @@ FfmpegScanDictionary(AVDictionary *dict,
i->name != nullptr; ++i)
FfmpegScanTag(i->type, dict, i->name,
handler, handler_ctx);
for (const struct tag_table *i = musicbrainz_txxx_tags;
i->name != nullptr; ++i)
FfmpegScanTag(i->type, dict, i->name,
handler, handler_ctx);
}
if (handler.pair != nullptr)

@@ -24,7 +24,6 @@
#include "config.h"
#include "FlacCommon.hxx"
#include "FlacMetadata.hxx"
#include "util/ConstBuffer.hxx"
#include "Log.hxx"
#include <stdexcept>
@@ -143,25 +142,10 @@ FlacDecoder::OnWrite(const FLAC__Frame &frame,
if (!initialized && !OnFirstFrame(frame.header))
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
const auto data = pcm_import.Import(buf, frame.header.blocksize);
chunk = pcm_import.Import(buf, frame.header.blocksize);
unsigned bit_rate = nbytes * 8 * frame.header.sample_rate /
kbit_rate = nbytes * 8 * frame.header.sample_rate /
(1000 * frame.header.blocksize);
auto cmd = GetClient()->SubmitData(GetInputStream(),
data.data, data.size,
bit_rate);
switch (cmd) {
case DecoderCommand::NONE:
case DecoderCommand::START:
break;
case DecoderCommand::STOP:
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
case DecoderCommand::SEEK:
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

@@ -27,6 +27,7 @@
#include "FlacInput.hxx"
#include "FlacPcm.hxx"
#include "../DecoderAPI.hxx"
#include "util/ConstBuffer.hxx"
#include <FLAC/stream_decoder.h>
@@ -41,6 +42,12 @@ struct FlacDecoder : public FlacInput {
*/
bool unsupported = false;
/**
* The kbit_rate parameter for the next
* DecoderBridge::SubmitData() call.
*/
uint16_t kbit_rate;
FlacPcmImport pcm_import;
/**
@@ -51,6 +58,13 @@ struct FlacDecoder : public FlacInput {
Tag tag;
/**
* Decoded PCM data obtained by our libFLAC write callback.
* If this is non-empty, then DecoderBridge::SubmitData()
* should be called.
*/
ConstBuffer<void> chunk = nullptr;
FlacDecoder(DecoderClient &_client, InputStream &_input_stream)
:FlacInput(_input_stream, &_client) {}

@@ -139,19 +139,40 @@ flac_decoder_initialize(FlacDecoder *data, FLAC__StreamDecoder *sd)
return data->initialized;
}
static DecoderCommand
FlacSubmitToClient(DecoderClient &client, FlacDecoder &d) noexcept
{
if (d.tag.IsEmpty() && d.chunk.IsEmpty())
return client.GetCommand();
if (!d.tag.IsEmpty()) {
auto cmd = client.SubmitTag(d.GetInputStream(),
std::move(d.tag));
d.tag.Clear();
if (cmd != DecoderCommand::NONE)
return cmd;
}
if (!d.chunk.IsEmpty()) {
auto cmd = client.SubmitData(d.GetInputStream(),
d.chunk.data,
d.chunk.size,
d.kbit_rate);
d.chunk = nullptr;
if (cmd != DecoderCommand::NONE)
return cmd;
}
return DecoderCommand::NONE;
}
static void
flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
{
DecoderClient &client = *data->GetClient();
while (true) {
DecoderCommand cmd;
if (!data->tag.IsEmpty()) {
cmd = client.SubmitTag(data->GetInputStream(),
std::move(data->tag));
data->tag.Clear();
} else
cmd = client.GetCommand();
DecoderCommand cmd = FlacSubmitToClient(client, *data);
if (cmd == DecoderCommand::SEEK) {
FLAC__uint64 seek_sample = client.GetSeekFrame();
@@ -160,6 +181,11 @@ flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
client.CommandFinished();
} else
client.SeekError();
/* FLAC__stream_decoder_seek_absolute()
decodes one frame and may have provided
data to be submitted to the client */
continue;
} else if (cmd == DecoderCommand::STOP)
break;

@@ -46,7 +46,7 @@ FlacIORead(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
p += nbytes;
#ifndef WIN32
#ifndef _WIN32
} catch (const std::system_error &e) {
errno = e.code().category() == ErrnoCategory()
? e.code().value()

@@ -293,13 +293,13 @@ gme_container_scan(Path path_fs)
TagBuilder tag_builder;
auto tail = list.before_begin();
for (unsigned i = 1; i <= num_songs; ++i) {
for (unsigned i = 0; i < num_songs; ++i) {
ScanMusicEmu(emu, i,
add_tag_handler, &tag_builder);
char track_name[64];
snprintf(track_name, sizeof(track_name),
SUBTUNE_PREFIX "%03u.%s", i, subtune_suffix);
SUBTUNE_PREFIX "%03u.%s", i+1, subtune_suffix);
tail = list.emplace_after(tail, track_name,
tag_builder.Commit());
}

@@ -53,6 +53,14 @@ ScanOneOpusTag(const char *name, const char *value,
long l = strtol(value, &endptr, 10);
if (endptr > value && *endptr == 0)
rgi->track.gain = double(l) / 256.;
} else if (rgi != nullptr && strcmp(name, "R128_ALBUM_GAIN") == 0) {
/* R128_ALBUM_GAIN is a Q7.8 fixed point number in
dB */
char *endptr;
long l = strtol(value, &endptr, 10);
if (endptr > value && *endptr == 0)
rgi->album.gain = double(l) / 256.;
}
tag_handler_invoke_pair(handler, ctx, name, value);

@@ -178,6 +178,20 @@ VorbisDecoder::SubmitInit()
client.Ready(audio_format, eos_granulepos > 0, duration);
}
#ifdef HAVE_TREMOR
static inline int16_t tremor_clip_sample(int32_t x)
{
x >>= 9;
if (x < INT16_MIN)
return INT16_MIN;
if (x > INT16_MAX)
return INT16_MAX;
return x;
}
#endif
bool
VorbisDecoder::SubmitSomePcm()
{
@@ -197,7 +211,7 @@ VorbisDecoder::SubmitSomePcm()
auto *dest = &buffer[c];
for (size_t i = 0; i < n_frames; ++i) {
*dest = *src++;
*dest = tremor_clip_sample(*src++);
dest += channels;
}
}

@@ -52,7 +52,7 @@ class OpusEncoder final : public OggEncoder {
ogg_int64_t packetno = 0;
ogg_int64_t granulepos;
ogg_int64_t granulepos = 0;
public:
OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc);

@@ -62,7 +62,7 @@ private:
};
class PreparedVorbisEncoder final : public PreparedEncoder {
float quality;
float quality = 3;
int bitrate;
public:
@@ -97,7 +97,7 @@ PreparedVorbisEncoder::PreparedVorbisEncoder(const ConfigBlock &block)
value = block.GetBlockValue("bitrate");
if (value == nullptr)
throw std::runtime_error("neither bitrate nor quality defined");
return;
quality = -2.0;

@@ -27,7 +27,7 @@
#include <algorithm>
EventLoop::EventLoop()
:SocketMonitor(*this)
:SocketMonitor(*this), quit(false)
{
SocketMonitor::Open(wake_fd.Get());
SocketMonitor::Schedule(SocketMonitor::READ);
@@ -46,7 +46,9 @@ EventLoop::~EventLoop()
void
EventLoop::Break()
{
quit = true;
if (quit.exchange(true))
return;
wake_fd.Write();
}

@@ -30,6 +30,7 @@
#include "SocketMonitor.hxx"
#include <chrono>
#include <atomic>
#include <list>
#include <set>
@@ -82,7 +83,7 @@ class EventLoop final : SocketMonitor
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
bool quit = false;
std::atomic_bool quit;
/**
* True when the object has been modified and another check is

@@ -23,7 +23,7 @@
#include <algorithm>
#ifndef WIN32
#ifndef _WIN32
#include <poll.h>
#endif
@@ -50,7 +50,7 @@ MultiSocketMonitor::ClearSocketList()
fds.clear();
}
#ifndef WIN32
#ifndef _WIN32
void
MultiSocketMonitor::ReplaceSocketList(pollfd *pfds, unsigned n)

@@ -31,7 +31,7 @@
#include <assert.h>
#ifdef WIN32
#ifdef _WIN32
/* ERROR is a WIN32 macro that poisons our namespace; this is a kludge
to allow us to use it anyway */
#ifdef ERROR
@@ -39,7 +39,7 @@
#endif
#endif
#ifndef WIN32
#ifndef _WIN32
struct pollfd;
#endif
@@ -189,7 +189,7 @@ public:
}
}
#ifndef WIN32
#ifndef _WIN32
/**
* Replace the socket list with the given file descriptors.
* The given pollfd array will be modified by this method.

@@ -43,7 +43,7 @@
#include <unistd.h>
#include <assert.h>
#ifdef WIN32
#ifdef _WIN32
#include <ws2tcpip.h>
#include <winsock.h>
#else

@@ -20,7 +20,7 @@
#include "config.h"
#include "SignalMonitor.hxx"
#ifndef WIN32
#ifndef _WIN32
#include "SocketMonitor.hxx"
#include "util/Manual.hxx"

@@ -24,7 +24,7 @@
class EventLoop;
#ifndef WIN32
#ifndef _WIN32
#include "util/BindMethod.hxx"

@@ -24,7 +24,7 @@
#include <assert.h>
#ifdef WIN32
#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>

@@ -28,7 +28,7 @@
#include <assert.h>
#include <stddef.h>
#ifdef WIN32
#ifdef _WIN32
/* ERROR is a WIN32 macro that poisons our namespace; this is a kludge
to allow us to use it anyway */
#ifdef ERROR

@@ -73,6 +73,10 @@ public:
return filter;
}
void Reset() override {
filter->Reset();
}
ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override {
return filter->FilterPCM(src);
}

@@ -53,10 +53,16 @@ public:
void Set(const AudioFormat &_out_audio_format);
void Reset() override {
state.Reset();
if (IsActive())
state.Reset();
}
ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override;
private:
bool IsActive() const noexcept {
return out_audio_format != in_audio_format;
}
};
class PreparedConvertFilter final : public PreparedFilter {
@@ -80,7 +86,7 @@ ConvertFilter::Set(const AudioFormat &_out_audio_format)
/* no change */
return;
if (out_audio_format != in_audio_format) {
if (IsActive()) {
out_audio_format = in_audio_format;
state.Close();
}
@@ -111,7 +117,7 @@ ConvertFilter::~ConvertFilter()
{
assert(in_audio_format.IsValid());
if (out_audio_format != in_audio_format)
if (IsActive())
state.Close();
}
@@ -120,11 +126,10 @@ ConvertFilter::FilterPCM(ConstBuffer<void> src)
{
assert(in_audio_format.IsValid());
if (out_audio_format == in_audio_format)
return IsActive()
? state.Convert(src)
/* optimized special case: no-op */
return src;
return state.Convert(src);
: src;
}
const FilterPlugin convert_filter_plugin = {

@@ -31,7 +31,7 @@ AllocatedPath::~AllocatedPath() {}
AllocatedPath
AllocatedPath::FromUTF8(const char *path_utf8) noexcept
{
#if defined(HAVE_FS_CHARSET) || defined(WIN32)
#if defined(HAVE_FS_CHARSET) || defined(_WIN32)
try {
return AllocatedPath(::PathFromUTF8(path_utf8));
} catch (const std::runtime_error &) {
@@ -45,7 +45,7 @@ AllocatedPath::FromUTF8(const char *path_utf8) noexcept
AllocatedPath
AllocatedPath::FromUTF8Throw(const char *path_utf8)
{
#if defined(HAVE_FS_CHARSET) || defined(WIN32)
#if defined(HAVE_FS_CHARSET) || defined(_WIN32)
return AllocatedPath(::PathFromUTF8(path_utf8));
#else
return FromFS(path_utf8);

@@ -24,7 +24,7 @@
#include "lib/icu/Converter.hxx"
#include "util/AllocatedString.hxx"
#ifdef WIN32
#ifdef _WIN32
#include "lib/icu/Win32.hxx"
#include <windows.h>
#endif
@@ -70,7 +70,7 @@ GetFSCharset() noexcept
{
#ifdef HAVE_FS_CHARSET
return fs_charset.empty() ? "UTF-8" : fs_charset.c_str();
#elif defined(WIN32)
#elif defined(_WIN32)
return "ACP";
#else
return "UTF-8";
@@ -100,7 +100,7 @@ PathToUTF8(PathTraitsFS::const_pointer_type path_fs)
assert(path_fs != nullptr);
#endif
#ifdef WIN32
#ifdef _WIN32
const auto buffer = WideCharToMultiByte(CP_UTF8, path_fs);
return FixSeparators(PathTraitsUTF8::string(buffer.c_str()));
#else
@@ -116,7 +116,7 @@ PathToUTF8(PathTraitsFS::const_pointer_type path_fs)
#endif
}
#if defined(HAVE_FS_CHARSET) || defined(WIN32)
#if defined(HAVE_FS_CHARSET) || defined(_WIN32)
PathTraitsFS::string
PathFromUTF8(PathTraitsUTF8::const_pointer_type path_utf8)
@@ -126,7 +126,7 @@ PathFromUTF8(PathTraitsUTF8::const_pointer_type path_utf8)
assert(path_utf8 != nullptr);
#endif
#ifdef WIN32
#ifdef _WIN32
const auto buffer = MultiByteToWideChar(CP_UTF8, path_utf8);
return PathTraitsFS::string(buffer.c_str());
#else

@@ -24,7 +24,7 @@
#include "Compiler.h"
#include "Traits.hxx"
#if (defined(HAVE_ICU) || defined(HAVE_ICONV)) && !defined(WIN32)
#if (defined(HAVE_ICU) || defined(HAVE_ICONV)) && !defined(_WIN32)
#define HAVE_FS_CHARSET
#endif

@@ -41,7 +41,7 @@ try {
return;
}
#ifndef WIN32
#ifndef _WIN32
try {
const auto x = AllocatedPath::Build(path_fs,
PathTraitsFS::CURRENT_DIRECTORY);

@@ -21,7 +21,7 @@
#include "DirectoryReader.hxx"
#include "system/Error.hxx"
#ifdef WIN32
#ifdef _WIN32
DirectoryReader::DirectoryReader(Path dir)
:handle(FindFirstFile(MakeWildcardPath(dir.c_str()), &data))

@@ -23,7 +23,7 @@
#include "check.h"
#include "Path.hxx"
#ifdef WIN32
#ifdef _WIN32
#include <windows.h>
#include <tchar.h>

@@ -26,13 +26,13 @@
#include <stdint.h>
#ifdef WIN32
#ifdef _WIN32
#include <fileapi.h>
#else
#include <sys/stat.h>
#endif
#ifdef WIN32
#ifdef _WIN32
static inline constexpr uint64_t
ConstructUint64(DWORD lo, DWORD hi)
@@ -54,7 +54,7 @@ class FileInfo {
bool follow_symlinks);
friend class FileReader;
#ifdef WIN32
#ifdef _WIN32
WIN32_FILE_ATTRIBUTE_DATA data;
#else
struct stat st;
@@ -65,7 +65,7 @@ public:
FileInfo(Path path, bool follow_symlinks=true) {
if (!GetFileInfo(path, *this, follow_symlinks)) {
#ifdef WIN32
#ifdef _WIN32
throw FormatLastError("Failed to access %s",
path.ToUTF8().c_str());
#else
@@ -76,7 +76,7 @@ public:
}
bool IsRegular() const {
#ifdef WIN32
#ifdef _WIN32
return (data.dwFileAttributes &
(FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE)) == 0;
#else
@@ -85,7 +85,7 @@ public:
}
bool IsDirectory() const {
#ifdef WIN32
#ifdef _WIN32
return data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
#else
return S_ISDIR(st.st_mode);
@@ -93,7 +93,7 @@ public:
}
uint64_t GetSize() const {
#ifdef WIN32
#ifdef _WIN32
return ConstructUint64(data.nFileSizeLow, data.nFileSizeHigh);
#else
return st.st_size;
@@ -101,14 +101,14 @@ public:
}
time_t GetModificationTime() const {
#ifdef WIN32
#ifdef _WIN32
return FileTimeToTimeT(data.ftLastWriteTime);
#else
return st.st_mtime;
#endif
}
#ifndef WIN32
#ifndef _WIN32
uid_t GetUid() const {
return st.st_uid;
}
@@ -130,7 +130,7 @@ public:
inline bool
GetFileInfo(Path path, FileInfo &info, bool follow_symlinks=true)
{
#ifdef WIN32
#ifdef _WIN32
(void)follow_symlinks;
return GetFileAttributesEx(path.c_str(), GetFileExInfoStandard,
&info.data);

@@ -29,7 +29,7 @@
void
RenameFile(Path oldpath, Path newpath)
{
#ifdef WIN32
#ifdef _WIN32
if (!MoveFileEx(oldpath.c_str(), newpath.c_str(),
MOVEFILE_REPLACE_EXISTING))
throw MakeLastError("Failed to rename file");
@@ -42,7 +42,7 @@ RenameFile(Path oldpath, Path newpath)
AllocatedPath
ReadLink(Path path)
{
#ifdef WIN32
#ifdef _WIN32
(void)path;
errno = EINVAL;
return AllocatedPath::Null();
@@ -63,7 +63,7 @@ ReadLink(Path path)
void
TruncateFile(Path path)
{
#ifdef WIN32
#ifdef _WIN32
HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, nullptr,
TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr);
@@ -83,7 +83,7 @@ TruncateFile(Path path)
void
RemoveFile(Path path)
{
#ifdef WIN32
#ifdef _WIN32
if (!DeleteFile(path.c_str()))
throw FormatLastError("Failed to delete %s", path.c_str());
#else

Some files were not shown because too many files have changed in this diff Show More