Compare commits

...

65 Commits

Author SHA1 Message Date
Max Kellermann
24741c5d06 release v0.21.24 2020-06-10 22:48:50 +02:00
Max Kellermann
6b3a282db4 lib/curl/Request: don't enable CURLOPT_NETRC on Windows
Our Windows build is built with `--disable-netrc`, and that makes
CURLOPT_NETRC fail, causing failures with all streams.  D'oh!

Closes https://github.com/MusicPlayerDaemon/MPD/issues/886
2020-06-10 22:46:42 +02:00
Max Kellermann
7583cfe9b7 {android,win32}/build.py: enable the GME decoder plugin
Closes https://github.com/MusicPlayerDaemon/MPD/issues/891
2020-06-10 21:33:29 +02:00
Max Kellermann
aafc9ce75b decoder/gme: use class NarrowPath() for Windows compatibility 2020-06-10 21:22:00 +02:00
Max Kellermann
fea326530b decoder/gme: simplify LoadGmeAndM3u() by moving code to ReplaceSuffix() 2020-06-10 21:20:49 +02:00
Max Kellermann
8925cc17d8 decoder/gme: use StringAfterPrefix() 2020-06-10 21:11:08 +02:00
Max Kellermann
14412c867f add a few IWYU pragmas 2020-06-10 21:10:33 +02:00
Max Kellermann
c5cc256bf2 decoder/gme: use Path::GetSuffix() 2020-06-10 21:02:07 +02:00
Max Kellermann
563c7318f9 fs/AllocatedPath: add method GetSuffix() 2020-06-10 21:00:41 +02:00
Max Kellermann
e92129f449 doc/protocol.rst: clarify the term "UNIX time"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/893
2020-06-10 20:42:42 +02:00
Max Kellermann
374cc51f77 decoder/Bridge: add flag to make initial seek errors fatal
When the client wants to seek, but the decoder has already finished
decoding the current song, the player restarts the decoder with an
initial seek at the new position.  When this initial seek fails, MPD
pretends nothing has happened and plays this song from the start.

With this new flag, a restarted decoder marks the initial seek as
"essential" and fails the decoder if that seek fails.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/895
2020-06-10 17:49:10 +02:00
Rosen Penev
1008d5f67c use cwchar include
Needed for std::wmemchr under libcxx

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2020-05-30 13:22:04 +02:00
Shen-Ta Hsieh
8e07ea7ad8 src/db: fitting libmpdclient interface 2020-05-29 19:00:16 +02:00
Max Kellermann
d751df0a73 storage/State: disable -Wcomma to work around Boost compiler warning 2020-05-28 14:00:31 +02:00
Max Kellermann
2c084781b0 output/openal: disable -Wdeprecated-declarations on Apple 2020-05-28 13:59:52 +02:00
Max Kellermann
ae7d550a01 meson.build: remove -Wall -Wextra, to be set by Meson 2020-05-28 13:19:34 +02:00
Max Kellermann
30d97fe8a0 meson.build: fix the WildMidi check when the feature is disabled
Fixes regression from commit 69f09648a4
2020-05-27 16:06:49 +02:00
Max Kellermann
5cb0080052 meson.build: default to warning_level=2
This branch isn't yet ready for level 3 (`-Wpedantic`) due to several
C++ violations (e.g. variable length arrays).  These are already
cleaned up in the master branch (0.22).
2020-05-27 15:57:13 +02:00
Max Kellermann
8e4ca23727 lib/ffmpeg/Time: replace C99 compound literal with C++ initializer list 2020-05-27 15:54:34 +02:00
Max Kellermann
bdc861f058 util/TemplateString: remove extra semicolon 2020-05-27 15:46:55 +02:00
Rosen Penev
8925040262 remove some more extra semicolons
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2020-05-27 15:36:53 +02:00
Max Kellermann
c065950ced .travis.yml: disable zzip on OS X to fix Travis-CI build failure 2020-05-27 15:31:34 +02:00
Max Kellermann
257a77fa35 {android,win32}/build.py: build libmodplug and WildMidi
Closes https://github.com/MusicPlayerDaemon/MPD/issues/866
2020-05-27 15:03:49 +02:00
Max Kellermann
4e5d6e560b decoder/modplug: assume ModPlug is built as static library on Windows 2020-05-27 15:03:46 +02:00
Max Kellermann
d276d8eda2 decoder/wildmidi: assume WildMidi is built as static library on Windows 2020-05-27 15:03:35 +02:00
Max Kellermann
ebcb5e9368 decoder/wildmidi: use NarrowPath, fixing the Windows build 2020-05-27 15:03:33 +02:00
Max Kellermann
69f09648a4 meson.build: attempt to detect WildMidi using pkg-config
The WildMidi project added the pkg-config file in version 0.3.3, but
unfortunately, Debian still doesn't ship it 4 years later:

 https://bugs.debian.org/916631

However, for cross-compiling, the pkg-config file is very helpful.
2020-05-27 15:03:16 +02:00
Max Kellermann
9adda30c38 NEWS: move two lines below Windows/Android 2020-05-27 14:33:43 +02:00
Max Kellermann
d2d4a0251e .gitignore: add emacs lsp-mode files 2020-05-26 21:07:56 +02:00
Max Kellermann
f7b6431b6f meson.build: work around Meson bug detecting strndup() on Windows
Work around Meson bug https://github.com/mesonbuild/meson/issues/3672
2020-05-26 20:50:56 +02:00
Max Kellermann
03b9bd3a9e python/build/libs.py: update FFmpeg to 4.2.3 2020-05-26 18:54:56 +02:00
Max Kellermann
61aed60f6d python/build/libs.py: update CURL to 7.70.0 2020-05-07 14:18:55 +02:00
Max Kellermann
2cc323c9fe python/build/libs.py: update Boost to 1.73.0 2020-05-07 14:18:21 +02:00
Max Kellermann
f24ab120ee android/build.py: use -fpic instead of -fPIC on ARM/Aarch64
Sync with the Android NDK build scripts.
2020-05-07 13:58:36 +02:00
Max Kellermann
68349bc55c android/build.py: use -mfpu=vfpv3-d16 on ARMv7
This flag is used by the Android NDK build scripts as well, and this
fixes a build failure (assembler error) with FFmpeg and NDK r21.
2020-05-07 13:50:33 +02:00
Max Kellermann
209364adf2 db/simple: fix crash when mounting twice
The `db->close()` call was a `nullptr` dereference because the `db`
variable had already been moved.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/839
2020-05-05 18:57:29 +02:00
Max Kellermann
24afdee35c command/all: "tagtypes" requires no permissions
The command is used to configure the client's connection, and this
shouldn't require any permissions.  The client should be able to do
that before sending a password.
2020-04-30 13:08:09 +02:00
Max Kellermann
7aea285361 Revert "Fix unsafe float comparison."
This reverts commit a5273d6992.  It was
wrong and broke the MixRamp unit test.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/844
2020-04-30 06:57:36 +02:00
Rosen Penev
47a7707df1 Math.hxx: fix wrong macro name
_GLIBCXX_USE_C99_MATH_TR1 is the correct one.

_GLIBCXX_USE_C99_MATH is always defined.
2020-04-29 23:20:04 +02:00
Max Kellermann
6fdae1139f increment version number to 0.21.24 2020-04-29 23:20:04 +02:00
Max Kellermann
6c240f667c release v0.21.23 2020-04-23 17:46:20 +02:00
Max Kellermann
3040ddb5ec lib/nfs/FileReader: use struct stat64 on Windows 32-bit
libnfs is compiled with `-D_FILE_OFFSET_BITS=64`, but Meson decides
not to enable this mode.  We could force this mode, but then again,
these days, nobody should be using 32-bit Windows ... so this is a
kludge only for debugging with 32-bit WINE.
2020-04-23 17:32:34 +02:00
Max Kellermann
fdb28eb0c4 fs/NarrowPath: preserve nullptr in Path operator
Fixes Path::IsNull() checks on Windows.
2020-04-23 17:10:28 +02:00
Max Kellermann
7ded244a61 lib/nfs/Connection: pass POLLHUP and POLLERR to nfs_service() 2020-04-23 16:58:53 +02:00
Max Kellermann
8ed533acf3 event/SocketMonitor: handle epoll_ctl()=EBADF/ENOENT in Schedule()
This fixes a freeze bug in the NFS input/storage plugins: when libnfs
auto-reconnets after a failure, it installs the new socket on the same
file descriptor number.  MPD's attempt to unregister the old socket by
calling SocketMonitor::Steal() from NfsConnection::ScheduleSocket()
fails because the new/old socket number is not registered in epoll, so
epoll_ctl() returns ENOENT.  The problem is that it left
`scheduled_flags`, and so subsequent Schedule() calls will use
`EPOLL_CTL_MOD`, which will fail again and again.  Instead, we need to
use `EPOLL_CTL_ADD` to register the new socket.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/806

Closes https://github.com/MusicPlayerDaemon/MPD/issues/756
2020-04-23 16:58:26 +02:00
Max Kellermann
a27580d0cc lib/nfs/Connection: don't pass HANGUP to Schedule()
This flag is output-only.
2020-04-23 15:21:04 +02:00
Max Kellermann
905db05cf9 zeroconf/AvahiPoll: don't pass ERROR|HANGUP to Schedule()
These flags are output-only.
2020-04-23 15:19:24 +02:00
Max Kellermann
4242aee21e event/SocketMonitor: remove HANGUP|ERROR from ScheduleRead()
These flags are output-only.  Using them here is misleading.
2020-04-23 15:18:18 +02:00
Max Kellermann
e71bd2a08b event/PollGroupWinSelect: make EVENT_{READ,WRITE} static 2020-04-23 15:10:57 +02:00
Max Kellermann
e53a4d0a9e lib/nfs/FileReader: reset state in OnNfsCallback()
The object's state is `IDLE` when OnNfsCallback() gets invoked, so
let's use the start of the method to reset the `state` field.
2020-04-23 14:54:52 +02:00
Max Kellermann
159389164a lib/nfs/FileReader: set state=IDLE before invoking callback
Fixes assertion failure if the callback fails.
2020-04-23 14:51:43 +02:00
geneticdrift
0a92fbc18e tag/Fallback: add tag fallback for AlbumSort
Closes https://github.com/MusicPlayerDaemon/MPD/issues/832
2020-04-22 22:00:38 +02:00
Max Kellermann
138c29320b gme: adapt to API change in the upcoming version 0.7.0
Closes https://github.com/MusicPlayerDaemon/MPD/issues/833
2020-04-22 21:53:00 +02:00
Max Kellermann
8f00dbea45 lib/icu/Compare: add Windows implementation
Using CompareStringEx() and FindNLSStringEx().

Implements a missing piece for
https://github.com/MusicPlayerDaemon/MPD/issues/820
2020-04-22 21:42:12 +02:00
Max Kellermann
f3fd2eb618 lib/icu/Compare: use AllocatedString::Clone() 2020-04-22 21:39:13 +02:00
Max Kellermann
fc92db83cf lib/icu/Collate: use NORM_IGNORECASE instead of LINGUISTIC_IGNORECASE
LINGUISTIC_IGNORECASE is unimplemented on Wine, but since we don't
have any locale support (yet), and we're using LOCALE_NAME_INVARIANT,
NORM_IGNORECASE should essentially be the same, so why bother.
2020-04-22 21:39:13 +02:00
Max Kellermann
3b0f8d5516 lib/icu/CaseFold: remove Windows implementation
Reverts commit fb3564fbe7

LCMapStringEx() doesn't do what I imagined it would do 5 years ago.
D'oh!

Closes https://github.com/MusicPlayerDaemon/MPD/issues/820
2020-04-22 19:32:36 +02:00
Rosen Penev
a5273d6992 Fix unsafe float comparison.
Switching == to >= should be safe here since the next if is the opposite.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2020-04-22 18:21:40 +02:00
Thomas Guillem
b18074f899 storage/curl: fix path comparison when the server escapes differently
Unescape the base path and the path coming from the server (href) to fix the
comparison when the server uses different escaped characters.

The outputted name need to be unescaped. Doing that before or after the
HrefToEscapedName() call should not change the current behavior.
2020-04-15 13:50:12 +02:00
Thomas Guillem
3d8067a041 storage/curl: fix href when file has a '&' char
If the file name is "Hello & bye", 3 CharacterData events will be sent with the
State::HREF state:
 - "Hello%20"
 - "&"
 - "%20bye"

Reproduced with files hosted on an apache2 DAV server: 2.4.38-3+deb10u3.
2020-04-15 13:18:16 +02:00
Florian Heese
f6fe001fa9 Added missing channel order setups for ALSA 2020-04-15 13:13:09 +02:00
Max Kellermann
32a5bf043b player/Thread: drain outputs at end of song in "single" mode
Without this, the Pause() call would drop the ring buffers and would
skip a considerable portion of the end of the song.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/824
2020-04-14 16:07:03 +02:00
Max Kellermann
8d2079482f Merge branch 'lkj' of git://github.com/neheb/MPD into v0.21.x 2020-04-14 13:12:42 +02:00
Max Kellermann
c331c75fde increment version number to 0.21.23 2020-04-14 13:12:36 +02:00
Rosen Penev
6080c3b4ba Math.hxx: move cmath include out of define
The _GLIBCXX_USE_C99_MATH macro is defined in glibcxx by c++config.h, which
gets included by every header. Which means a header needs to be present.

(cherry picked from commit 79e9aff338)
2020-04-09 14:24:06 -07:00
54 changed files with 504 additions and 136 deletions

3
.gitignore vendored
View File

@@ -6,3 +6,6 @@
/output/
__pycache__/
/.clangd/
/compile_commands.json

View File

@@ -135,7 +135,8 @@ jobs:
- chromaprint
- libsamplerate
- libsoxr
- libzzip
# libzzip appears to be broken on Homebrew: "ld: library not found for -lzzip"
#- libzzip
- flac
- opus
- libvorbis

38
NEWS
View File

@@ -1,3 +1,41 @@
ver 0.21.24 (2020/06/10)
* protocol
- "tagtypes" requires no permissions
* database
- simple: fix crash when mounting twice
* decoder
- modplug: fix Windows build failure
- wildmidi: attempt to detect WildMidi using pkg-config
- wildmidi: fix Windows build failure
* player
- don't restart current song if seeking beyond end
* Android
- enable the decoder plugins GME, ModPlug and WildMidi
- fix build failure with Android NDK r21
* Windows
- fix stream playback
- enable the decoder plugins GME, ModPlug and WildMidi
- work around Meson bug breaking the Windows build with GCC 10
* fix unit test failure
ver 0.21.23 (2020/04/23)
* protocol
- add tag fallback for AlbumSort
* storage
- curl: fix corrupt "href" values in the presence of XML entities
- curl: unescape "href" values
* input
- nfs: fix crash bug
- nfs: fix freeze bug on reconnect
* decoder
- gme: adapt to API change in the upcoming version 0.7.0
* output
- alsa: implement channel mapping for 5.0 and 7.0
* player
- drain outputs at end of song in "single" mode
* Windows
- fix case insensitive search
ver 0.21.22 (2020/04/02)
* database
- simple: optimize startup

View File

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="45"
android:versionName="0.21.22">
android:versionCode="47"
android:versionName="0.21.24">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>

View File

@@ -26,7 +26,7 @@ android_abis = {
'ndk_arch': 'arm',
'toolchain_arch': 'arm-linux-androideabi',
'llvm_triple': 'armv7-linux-androideabi',
'cflags': '-march=armv7-a -mfpu=vfp -mfloat-abi=softfp',
'cflags': '-fpic -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp',
},
'arm64-v8a': {
@@ -34,7 +34,7 @@ android_abis = {
'ndk_arch': 'arm64',
'toolchain_arch': 'aarch64-linux-android',
'llvm_triple': 'aarch64-linux-android',
'cflags': '',
'cflags': '-fpic',
},
'x86': {
@@ -42,7 +42,7 @@ android_abis = {
'ndk_arch': 'x86',
'toolchain_arch': 'x86',
'llvm_triple': 'i686-linux-android',
'cflags': '-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
'cflags': '-fPIC -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
},
'x86_64': {
@@ -50,7 +50,7 @@ android_abis = {
'ndk_arch': 'x86_64',
'toolchain_arch': 'x86_64',
'llvm_triple': 'x86_64-linux-android',
'cflags': '-m64',
'cflags': '-fPIC -m64',
},
}
@@ -97,7 +97,6 @@ class AndroidNdkToolchain:
llvm_triple = abi_info['llvm_triple'] + android_api_level
common_flags = '-Os -g'
common_flags += ' -fPIC'
common_flags += ' ' + abi_info['cflags']
toolchain_bin = os.path.join(toolchain_path, 'bin')
@@ -169,6 +168,9 @@ thirdparty_libs = [
opus,
flac,
libid3tag,
libmodplug,
wildmidi,
gme,
ffmpeg,
curl,
libexpat,

View File

@@ -38,7 +38,7 @@ author = 'Max Kellermann'
# built documents.
#
# The short X.Y version.
version = '0.21.22'
version = '0.21.24'
# The full version, including alpha/beta/rc tags.
release = version

View File

@@ -464,7 +464,8 @@ Querying :program:`MPD`'s status
- ``songs``: number of songs
- ``uptime``: daemon uptime in seconds
- ``db_playtime``: sum of all song times in the database in seconds
- ``db_update``: last db update in UNIX time
- ``db_update``: last db update in UNIX time (seconds since
1970-01-01 UTC)
- ``playtime``: time length of music played
Playback options

View File

@@ -1,11 +1,12 @@
project(
'mpd',
['c', 'cpp'],
version: '0.21.22',
version: '0.21.24',
meson_version: '>= 0.49.0',
default_options: [
'c_std=c99',
'cpp_std=c++14'
'cpp_std=c++14',
'warning_level=2',
],
license: 'GPLv2+',
)
@@ -40,9 +41,6 @@ common_cxxflags = [
]
test_common_flags = [
'-Wall',
'-Wextra',
'-fvisibility=hidden',
'-ffast-math',
@@ -142,7 +140,13 @@ conf.set('HAVE_GETPWNAM_R', compiler.has_function('getpwnam_r'))
conf.set('HAVE_GETPWUID_R', compiler.has_function('getpwuid_r'))
conf.set('HAVE_INITGROUPS', compiler.has_function('initgroups'))
conf.set('HAVE_FNMATCH', compiler.has_function('fnmatch'))
conf.set('HAVE_STRNDUP', compiler.has_function('strndup', prefix: '#define _GNU_SOURCE\n#include <string.h>'))
# Explicitly exclude Windows in this check because
# https://github.com/mesonbuild/meson/issues/3672 (reported in 2018,
# still not fixed in 2020) causes Meson to believe it exists, because
# __builtin_strndup() exists (but strndup() still cannot be used).
conf.set('HAVE_STRNDUP', not is_windows and compiler.has_function('strndup', prefix: '#define _GNU_SOURCE\n#include <string.h>'))
conf.set('HAVE_STRCASESTR', compiler.has_function('strcasestr'))
conf.set('HAVE_PRCTL', is_linux)

45
python/build/cmake.py Normal file
View File

@@ -0,0 +1,45 @@
import subprocess
from build.project import Project
def configure(toolchain, src, build, args=()):
cross_args = []
if toolchain.is_windows:
cross_args.append('-DCMAKE_SYSTEM_NAME=Windows')
cross_args.append('-DCMAKE_RC_COMPILER=' + toolchain.windres)
configure = [
'cmake',
src,
'-DCMAKE_INSTALL_PREFIX=' + toolchain.install_prefix,
'-DCMAKE_BUILD_TYPE=release',
'-DCMAKE_C_COMPILER=' + toolchain.cc,
'-DCMAKE_CXX_COMPILER=' + toolchain.cxx,
'-DCMAKE_C_FLAGS=' + toolchain.cflags + ' ' + toolchain.cppflags,
'-DCMAKE_CXX_FLAGS=' + toolchain.cxxflags + ' ' + toolchain.cppflags,
'-GNinja',
] + cross_args + args
subprocess.check_call(configure, env=toolchain.env, cwd=build)
class CmakeProject(Project):
def __init__(self, url, md5, installed, configure_args=[],
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args
def configure(self, toolchain):
src = self.unpack(toolchain)
build = self.make_build_path(toolchain)
configure(toolchain, src, build, self.configure_args)
return build
def build(self, toolchain):
build = self.configure(toolchain)
subprocess.check_call(['ninja', 'install'],
cwd=build, env=toolchain.env)

View File

@@ -4,6 +4,7 @@ from os.path import abspath
from build.project import Project
from build.zlib import ZlibProject
from build.meson import MesonProject
from build.cmake import CmakeProject
from build.autotools import AutotoolsProject
from build.ffmpeg import FfmpegProject
from build.boost import BoostProject
@@ -111,9 +112,44 @@ liblame = AutotoolsProject(
],
)
libmodplug = AutotoolsProject(
'https://downloads.sourceforge.net/modplug-xmms/libmodplug/0.8.9.0/libmodplug-0.8.9.0.tar.gz',
'457ca5a6c179656d66c01505c0d95fafaead4329b9dbaa0f997d00a3508ad9de',
'lib/libmodplug.a',
[
'--disable-shared', '--enable-static',
],
)
wildmidi = CmakeProject(
'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.3',
'498e5a96455bb4b91b37188ad6dcb070824e92c44f5ed452b90adbaec8eef3c5',
'lib/libWildMidi.a',
[
'-DBUILD_SHARED_LIBS=OFF',
'-DWANT_PLAYER=OFF',
'-DWANT_STATIC=ON',
],
base='wildmidi-wildmidi-0.4.3',
name='wildmidi',
version='0.4.3',
)
gme = CmakeProject(
'https://bitbucket.org/mpyne/game-music-emu/downloads/game-music-emu-0.6.3.tar.xz',
'aba34e53ef0ec6a34b58b84e28bf8cfbccee6585cebca25333604c35db3e051d',
'lib/libgme.a',
[
'-DBUILD_SHARED_LIBS=OFF',
'-DENABLE_UBSAN=OFF',
'-DZLIB_INCLUDE_DIR=OFF',
'-DSDL2_DIR=OFF',
],
)
ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-4.2.2.tar.xz',
'cb754255ab0ee2ea5f66f8850e1bd6ad5cac1cd855d0a2f4990fb8c668b0d29c',
'http://ffmpeg.org/releases/ffmpeg-4.2.3.tar.xz',
'9df6c90aed1337634c1fb026fb01c154c29c82a64ea71291ff2da9aacb9aad31',
'lib/libavcodec.a',
[
'--disable-shared', '--enable-static',
@@ -341,8 +377,8 @@ ffmpeg = FfmpegProject(
)
curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.69.1.tar.xz',
'03c7d5e6697f7b7e40ada1b2256e565a555657398e6c1fcfa4cb251ccd819d4f',
'http://curl.haxx.se/download/curl-7.70.0.tar.xz',
'032f43f2674008c761af19bf536374128c16241fb234699a55f9fb603fcfbae7',
'lib/libcurl.a',
[
'--disable-shared', '--enable-static',
@@ -397,7 +433,7 @@ libnfs = AutotoolsProject(
)
boost = BoostProject(
'https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.tar.bz2',
'59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722',
'https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.bz2',
'4eb3b8d442b426dc35346235c8733b5ae35ba431690e38c6a8263dce9fcbb402',
'include/boost/version.hpp',
)

View File

@@ -20,7 +20,7 @@
#ifndef MPD_AUDIO_FORMAT_HXX
#define MPD_AUDIO_FORMAT_HXX
#include "pcm/SampleFormat.hxx"
#include "pcm/SampleFormat.hxx" // IWYU pragma: export
#include "util/Compiler.h"
#include <chrono>

View File

@@ -193,7 +193,7 @@ static constexpr struct command commands[] = {
{ "subscribe", PERMISSION_READ, 1, 1, handle_subscribe },
{ "swap", PERMISSION_CONTROL, 2, 2, handle_swap },
{ "swapid", PERMISSION_CONTROL, 2, 2, handle_swapid },
{ "tagtypes", PERMISSION_READ, 0, -1, handle_tagtypes },
{ "tagtypes", PERMISSION_NONE, 0, -1, handle_tagtypes },
{ "toggleoutput", PERMISSION_ADMIN, 1, 1, handle_toggleoutput },
#ifdef ENABLE_DATABASE
{ "unmount", PERMISSION_ADMIN, 1, 1, handle_unmount },

View File

@@ -493,9 +493,13 @@ ProxyDatabase::Connect()
try {
CheckError(connection);
if (mpd_connection_cmp_server_version(connection, 0, 19, 0) < 0)
throw FormatRuntimeError("Connect to MPD %s, but this plugin requires at least version 0.19",
mpd_connection_get_server_version(connection));
if (mpd_connection_cmp_server_version(connection, 0, 19, 0) < 0) {
const unsigned *version =
mpd_connection_get_server_version(connection);
throw FormatRuntimeError("Connect to MPD %u.%u.%u, but this "
"plugin requires at least version 0.19",
version[0], version[1], version[2]);
}
if (!password.empty() &&
!mpd_run_password(connection, password.c_str()))

View File

@@ -449,12 +449,7 @@ SimpleDatabase::Mount(const char *local_uri, const char *storage_uri)
// TODO: update the new database instance?
try {
Mount(local_uri, std::move(db));
} catch (...) {
db->Close();
throw;
}
Mount(local_uri, std::move(db));
}
inline DatabasePtr

View File

@@ -33,6 +33,8 @@
#include "util/ConstBuffer.hxx"
#include "util/StringBuffer.hxx"
#include <stdexcept>
#include <assert.h>
#include <string.h>
#include <math.h>
@@ -344,6 +346,10 @@ DecoderBridge::SeekError()
/* d'oh, we can't seek to the sub-song start position,
what now? - no idea, ignoring the problem for now. */
initial_seek_running = false;
if (initial_seek_essential)
error = std::make_exception_ptr(std::runtime_error("Decoder failed to seek"));
return;
}

View File

@@ -62,6 +62,11 @@ public:
*/
bool initial_seek_pending;
/**
* Are initial seek failures fatal?
*/
const bool initial_seek_essential;
/**
* Is the initial seek currently running? During this time,
* the decoder command is SEEK. This flag is set by
@@ -107,9 +112,11 @@ public:
std::exception_ptr error;
DecoderBridge(DecoderControl &_dc, bool _initial_seek_pending,
bool _initial_seek_essential,
std::unique_ptr<Tag> _tag)
:dc(_dc),
initial_seek_pending(_initial_seek_pending),
initial_seek_essential(_initial_seek_essential),
song_tag(std::move(_tag)) {}
~DecoderBridge();

View File

@@ -90,6 +90,7 @@ DecoderControl::IsCurrentSong(const DetachedSong &_song) const noexcept
void
DecoderControl::Start(std::unique_ptr<DetachedSong> _song,
SongTime _start_time, SongTime _end_time,
bool _initial_seek_essential,
MusicBuffer &_buffer,
std::shared_ptr<MusicPipe> _pipe) noexcept
{
@@ -99,6 +100,7 @@ DecoderControl::Start(std::unique_ptr<DetachedSong> _song,
song = std::move(_song);
start_time = _start_time;
end_time = _end_time;
initial_seek_essential = _initial_seek_essential;
buffer = &_buffer;
pipe = std::move(_pipe);

View File

@@ -117,6 +117,12 @@ public:
bool seek_error;
bool seekable;
/**
* @see #DecoderBridge::initial_seek_essential
*/
bool initial_seek_essential;
SongTime seek_time;
private:
@@ -398,11 +404,14 @@ public:
* owned and freed by the decoder
* @param start_time see #DecoderControl
* @param end_time see #DecoderControl
* @param initial_seek_essential see
* #DecoderBridge::initial_seek_essential
* @param pipe the pipe which receives the decoded chunks (owned by
* the caller)
*/
void Start(std::unique_ptr<DetachedSong> song,
SongTime start_time, SongTime end_time,
bool initial_seek_essential,
MusicBuffer &buffer,
std::shared_ptr<MusicPipe> pipe) noexcept;

View File

@@ -22,7 +22,7 @@
#include "util/Compiler.h"
#include <forward_list>
#include <forward_list> // IWYU pragma: export
struct ConfigBlock;
class InputStream;

View File

@@ -461,6 +461,7 @@ decoder_run_song(DecoderControl &dc,
dc.start_time = dc.seek_time;
DecoderBridge bridge(dc, dc.start_time.IsPositive(),
dc.initial_seek_essential,
/* pass the song tag only if it's
authoritative, i.e. if it's a local
file - tags on "stream" songs are just

View File

@@ -27,10 +27,10 @@
#include "fs/Path.hxx"
#include "fs/AllocatedPath.hxx"
#include "fs/FileSystem.hxx"
#include "fs/NarrowPath.hxx"
#include "util/ScopeExit.hxx"
#include "util/StringCompare.hxx"
#include "util/StringFormat.hxx"
#include "util/UriUtil.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
@@ -38,7 +38,6 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define SUBTUNE_PREFIX "tune_"
@@ -76,11 +75,10 @@ gcc_pure
static unsigned
ParseSubtuneName(const char *base) noexcept
{
if (memcmp(base, SUBTUNE_PREFIX, sizeof(SUBTUNE_PREFIX) - 1) != 0)
base = StringAfterPrefix(base, SUBTUNE_PREFIX);
if (base == nullptr)
return 0;
base += sizeof(SUBTUNE_PREFIX) - 1;
char *endptr;
auto track = strtoul(base, &endptr, 10);
if (endptr == base || *endptr != '.')
@@ -99,41 +97,46 @@ ParseContainerPath(Path path_fs)
const Path base = path_fs.GetBase();
unsigned track;
if (base.IsNull() ||
(track = ParseSubtuneName(base.c_str())) < 1)
(track = ParseSubtuneName(NarrowPath(base))) < 1)
return { AllocatedPath(path_fs), 0 };
return { path_fs.GetDirectoryName(), track - 1 };
}
static AllocatedPath
ReplaceSuffix(Path src,
const PathTraitsFS::const_pointer_type new_suffix) noexcept
{
const auto *old_suffix = src.GetSuffix();
if (old_suffix == nullptr)
return nullptr;
PathTraitsFS::string s(src.c_str(), old_suffix);
s += new_suffix;
return AllocatedPath::FromFS(std::move(s));
}
static Music_Emu*
LoadGmeAndM3u(GmeContainerPath container) {
const char *path = container.path.c_str();
const char *suffix = uri_get_suffix(path);
Music_Emu *emu;
const char *gme_err =
gme_open_file(path, &emu, GME_SAMPLE_RATE);
gme_open_file(NarrowPath(container.path), &emu, GME_SAMPLE_RATE);
if (gme_err != nullptr) {
LogWarning(gme_domain, gme_err);
return nullptr;
}
if(suffix == nullptr) {
return emu;
}
std::string m3u_path(path,suffix);
m3u_path += "m3u";
const auto m3u_path = ReplaceSuffix(container.path,
PATH_LITERAL("m3u"));
/*
* Some GME formats lose metadata if you attempt to
* load a non-existant M3U file, so check that one
* exists before loading.
*/
if(FileExists(Path::FromFS(m3u_path.c_str()))) {
gme_load_m3u(emu,m3u_path.c_str());
}
if (!m3u_path.IsNull() && FileExists(m3u_path))
gme_load_m3u(emu, NarrowPath(m3u_path));
return emu;
}
@@ -185,7 +188,11 @@ gme_file_decode(DecoderClient &client, Path path_fs)
LogWarning(gme_domain, gme_err);
if (length > 0)
gme_set_fade(emu, length);
gme_set_fade(emu, length
#if GME_VERSION >= 0x000700
, 8000
#endif
);
/* play */
DecoderCommand cmd;
@@ -299,7 +306,7 @@ gme_container_scan(Path path_fs)
if (num_songs < 2)
return list;
const char *subtune_suffix = uri_get_suffix(path_fs.c_str());
const auto *subtune_suffix = path_fs.GetSuffix();
TagBuilder tag_builder;

View File

@@ -26,8 +26,13 @@
#include "util/RuntimeError.hxx"
#include "Log.hxx"
#include <libmodplug/modplug.h>
#ifdef _WIN32
/* assume ModPlug is built as static library on Windows; without
this, linking to the static library would fail */
#define MODPLUG_STATIC
#endif
#include <libmodplug/modplug.h>
#include <assert.h>

View File

@@ -25,9 +25,16 @@
#include "fs/AllocatedPath.hxx"
#include "fs/FileSystem.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "Log.hxx"
#include "PluginUnavailable.hxx"
#ifdef _WIN32
/* assume WildMidi is built as static library on Windows; without
this, linking to the static library would fail */
#define WILDMIDI_STATIC
#endif
extern "C" {
#include <wildmidi_lib.h>
}
@@ -53,7 +60,8 @@ wildmidi_init(const ConfigBlock &block)
AtScopeExit() { WildMidi_ClearError(); };
#endif
if (WildMidi_Init(path.c_str(), wildmidi_audio_format.sample_rate,
if (WildMidi_Init(NarrowPath(path),
wildmidi_audio_format.sample_rate,
0) != 0) {
#ifdef LIBWILDMIDI_VERSION
/* WildMidi_GetError() requires libwildmidi 0.4 */
@@ -96,7 +104,7 @@ wildmidi_file_decode(DecoderClient &client, Path path_fs)
midi *wm;
const struct _WM_Info *info;
wm = WildMidi_Open(path_fs.c_str());
wm = WildMidi_Open(NarrowPath(path_fs));
if (wm == nullptr)
return;
@@ -136,7 +144,7 @@ wildmidi_file_decode(DecoderClient &client, Path path_fs)
static bool
wildmidi_scan_file(Path path_fs, TagHandler &handler) noexcept
{
midi *wm = WildMidi_Open(path_fs.c_str());
midi *wm = WildMidi_Open(NarrowPath(path_fs));
if (wm == nullptr)
return false;

View File

@@ -129,7 +129,16 @@ if wavpack_dep.found()
decoder_plugins_sources += 'WavpackDecoderPlugin.cxx'
endif
wildmidi_dep = c_compiler.find_library('WildMidi', required: get_option('wildmidi'))
wildmidi_required = get_option('wildmidi')
if wildmidi_required.enabled()
# if the user has force-enabled WildMidi, allow the pkg-config test
# to fail; after that, the find_library() check must succeed
wildmidi_required = false
endif
wildmidi_dep = dependency('wildmidi', required: wildmidi_required)
if not wildmidi_dep.found()
wildmidi_dep = c_compiler.find_library('WildMidi', required: get_option('wildmidi'))
endif
conf.set('ENABLE_WILDMIDI', wildmidi_dep.found())
if wildmidi_dep.found()
decoder_plugins_sources += 'WildmidiDecoderPlugin.cxx'

View File

@@ -23,8 +23,8 @@
#include "PollGroupWinSelect.hxx"
constexpr int EVENT_READ = 0;
constexpr int EVENT_WRITE = 1;
static constexpr int EVENT_READ = 0;
static constexpr int EVENT_WRITE = 1;
static constexpr
bool HasEvent(unsigned events, int event_id) noexcept

View File

@@ -20,6 +20,10 @@
#include "SocketMonitor.hxx"
#include "Loop.hxx"
#ifdef USE_EPOLL
#include <cerrno>
#endif
#include <assert.h>
#ifdef _WIN32
@@ -86,6 +90,21 @@ SocketMonitor::Schedule(unsigned flags) noexcept
if (success)
scheduled_flags = flags;
#ifdef USE_EPOLL
else if (errno == EBADF || errno == ENOENT)
/* the socket was probably closed by somebody else
(EBADF) or a new file descriptor with the same
number was created but not registered already
(ENOENT) - we can assume that there are no
scheduled events */
/* note that when this happens, we're actually lucky
that it has failed - imagine another thread may
meanwhile have created something on the same file
descriptor number, and has registered it; the
epoll_ctl() call above would then have succeeded,
but broke the other thread's epoll registration */
scheduled_flags = 0;
#endif
return success;
}

View File

@@ -109,7 +109,7 @@ public:
}
bool ScheduleRead() noexcept {
return Schedule(GetScheduledFlags() | READ | HANGUP | ERROR);
return Schedule(GetScheduledFlags() | READ);
}
bool ScheduleWrite() noexcept {
@@ -117,7 +117,7 @@ public:
}
void CancelRead() noexcept {
Schedule(GetScheduledFlags() & ~(READ|HANGUP|ERROR));
Schedule(GetScheduledFlags() & ~READ);
}
void CancelWrite() noexcept {

View File

@@ -285,6 +285,11 @@ public:
bool IsAbsolute() const noexcept {
return Traits::IsAbsolute(c_str());
}
gcc_pure
const_pointer_type GetSuffix() const noexcept {
return ((Path)*this).GetSuffix();
}
};
#endif

View File

@@ -90,6 +90,11 @@ public:
constexpr
#endif
operator Path() const noexcept {
#ifdef _UNICODE
if (value.IsNull())
return nullptr;
#endif
return value;
}
};

View File

@@ -56,7 +56,7 @@ CurlRequest::CurlRequest(CurlGlobal &_global,
easy.SetUserAgent("Music Player Daemon " VERSION);
easy.SetHeaderFunction(_HeaderFunction, this);
easy.SetWriteFunction(WriteFunction, this);
#ifndef ANDROID
#if !defined(ANDROID) && !defined(_WIN32)
easy.SetOption(CURLOPT_NETRC, 1L);
#endif
easy.SetErrorBuffer(error_buffer);

View File

@@ -86,7 +86,7 @@ struct WrapVariant : BasicValue<T> {
template<typename T>
static WrapVariant<T> Variant(const T &_value) noexcept {
return WrapVariant<T>(_value);
};
}
template<typename T>
struct WrapFixedArray {
@@ -103,7 +103,7 @@ template<typename T>
static WrapFixedArray<T> FixedArray(const T *_data,
size_t _size) noexcept {
return WrapFixedArray<T>(_data, _size);
};
}
template<typename... T>
struct WrapStruct {
@@ -118,7 +118,7 @@ struct WrapStruct {
template<typename... T>
static WrapStruct<T...> Struct(const T&... values) noexcept {
return WrapStruct<T...>(values...);
};
}
} /* namespace ODBus */

View File

@@ -45,7 +45,7 @@ FfmpegTimeToDouble(int64_t t, const AVRational time_base) noexcept
{
assert(t != (int64_t)AV_NOPTS_VALUE);
return FloatDuration(av_rescale_q(t, time_base, (AVRational){1, 1024}))
return FloatDuration(av_rescale_q(t, time_base, {1, 1024}))
/ 1024;
}
@@ -69,7 +69,7 @@ FromFfmpegTime(int64_t t, const AVRational time_base) noexcept
assert(t != (int64_t)AV_NOPTS_VALUE);
return SongTime::FromMS(av_rescale_q(t, time_base,
(AVRational){1, 1000}));
{1, 1000}));
}
/**

View File

@@ -36,11 +36,6 @@
#include <ctype.h>
#endif
#ifdef _WIN32
#include "Win32.hxx"
#include <windows.h>
#endif
#include <memory>
#include <assert.h>
@@ -72,25 +67,6 @@ try {
folded.SetSize(folded_length);
return UCharToUTF8({folded.begin(), folded.size()});
#elif defined(_WIN32)
const auto u = MultiByteToWideChar(CP_UTF8, src);
const int size = LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, nullptr, 0,
nullptr, nullptr, 0);
if (size <= 0)
return AllocatedString<>::Duplicate(src);
std::unique_ptr<wchar_t[]> buffer(new wchar_t[size]);
if (LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, buffer.get(), size,
nullptr, nullptr, 0) <= 0)
return AllocatedString<>::Duplicate(src);
return WideCharToMultiByte(CP_UTF8, buffer.get());
#else
#error not implemented
#endif

View File

@@ -22,7 +22,7 @@
#include "config.h"
#if defined(HAVE_ICU) || defined(_WIN32)
#ifdef HAVE_ICU
#define HAVE_ICU_CASE_FOLD
#include "util/Compiler.h"

View File

@@ -109,7 +109,7 @@ IcuCollate(const char *a, const char *b) noexcept
}
auto result = CompareStringEx(LOCALE_NAME_INVARIANT,
LINGUISTIC_IGNORECASE,
NORM_IGNORECASE,
wa.c_str(), -1,
wb.c_str(), -1,
nullptr, nullptr, 0);

View File

@@ -22,6 +22,11 @@
#include "util/StringAPI.hxx"
#include "config.h"
#ifdef _WIN32
#include "Win32.hxx"
#include <windows.h>
#endif
#include <string.h>
#ifdef HAVE_ICU_CASE_FOLD
@@ -29,6 +34,17 @@
IcuCompare::IcuCompare(const char *_needle) noexcept
:needle(IcuCaseFold(_needle)) {}
#elif defined(_WIN32)
IcuCompare::IcuCompare(const char *_needle) noexcept
:needle(nullptr)
{
try {
needle = MultiByteToWideChar(CP_UTF8, _needle);
} catch (...) {
}
}
#else
IcuCompare::IcuCompare(const char *_needle) noexcept
@@ -41,6 +57,22 @@ IcuCompare::operator==(const char *haystack) const noexcept
{
#ifdef HAVE_ICU_CASE_FOLD
return StringIsEqual(IcuCaseFold(haystack).c_str(), needle.c_str());
#elif defined(_WIN32)
if (needle.IsNull())
/* the MultiByteToWideChar() call in the constructor
has failed, so let's always fail the comparison */
return false;
try {
auto w_haystack = MultiByteToWideChar(CP_UTF8, haystack);
return CompareStringEx(LOCALE_NAME_INVARIANT,
NORM_IGNORECASE,
w_haystack.c_str(), -1,
needle.c_str(), -1,
nullptr, nullptr, 0) == CSTR_EQUAL;
} catch (...) {
return false;
}
#else
return strcasecmp(haystack, needle.c_str());
#endif
@@ -52,6 +84,24 @@ IcuCompare::IsIn(const char *haystack) const noexcept
#ifdef HAVE_ICU_CASE_FOLD
return StringFind(IcuCaseFold(haystack).c_str(),
needle.c_str()) != nullptr;
#elif defined(_WIN32)
if (needle.IsNull())
/* the MultiByteToWideChar() call in the constructor
has failed, so let's always fail the comparison */
return false;
try {
auto w_haystack = MultiByteToWideChar(CP_UTF8, haystack);
return FindNLSStringEx(LOCALE_NAME_INVARIANT,
FIND_FROMSTART|NORM_IGNORECASE,
w_haystack.c_str(), -1,
needle.c_str(), -1,
nullptr,
nullptr, nullptr, 0) >= 0;
} catch (...) {
/* MultiByteToWideChar() has failed */
return false;
}
#elif defined(HAVE_STRCASESTR)
return strcasestr(haystack, needle.c_str()) != nullptr;
#else

View File

@@ -23,13 +23,23 @@
#include "util/Compiler.h"
#include "util/AllocatedString.hxx"
#ifdef _WIN32
#include <wchar.h>
#endif
/**
* This class can compare one string ("needle") with lots of other
* strings ("haystacks") efficiently, ignoring case. With some
* configurations, it can prepare a case-folded version of the needle.
*/
class IcuCompare {
#ifdef _WIN32
/* Windows API functions work with wchar_t strings, so let's
cache the MultiByteToWideChar() result for performance */
AllocatedString<wchar_t> needle;
#else
AllocatedString<> needle;
#endif
public:
IcuCompare():needle(nullptr) {}
@@ -38,12 +48,12 @@ public:
IcuCompare(const IcuCompare &src) noexcept
:needle(src
? AllocatedString<>::Duplicate(src.needle.c_str())
? src.needle.Clone()
: nullptr) {}
IcuCompare &operator=(const IcuCompare &src) noexcept {
needle = src
? AllocatedString<>::Duplicate(src.needle.c_str())
? src.needle.Clone()
: nullptr;
return *this;
}

View File

@@ -191,7 +191,9 @@ static constexpr int
events_to_libnfs(unsigned i) noexcept
{
return ((i & SocketMonitor::READ) ? POLLIN : 0) |
((i & SocketMonitor::WRITE) ? POLLOUT : 0);
((i & SocketMonitor::WRITE) ? POLLOUT : 0) |
((i & SocketMonitor::HANGUP) ? POLLHUP : 0) |
((i & SocketMonitor::ERROR) ? POLLERR : 0);
}
NfsConnection::~NfsConnection() noexcept
@@ -450,8 +452,7 @@ NfsConnection::ScheduleSocket() noexcept
SocketMonitor::Open(_fd);
}
SocketMonitor::Schedule(libnfs_to_events(which_events)
| SocketMonitor::HANGUP);
SocketMonitor::Schedule(libnfs_to_events(which_events));
}
inline int

View File

@@ -180,7 +180,6 @@ NfsFileReader::OnNfsConnectionDisconnected(std::exception_ptr e) noexcept
inline void
NfsFileReader::OpenCallback(nfsfh *_fh) noexcept
{
assert(state == State::OPEN);
assert(connection != nullptr);
assert(_fh != nullptr);
@@ -197,27 +196,33 @@ NfsFileReader::OpenCallback(nfsfh *_fh) noexcept
}
inline void
NfsFileReader::StatCallback(const struct stat *st) noexcept
NfsFileReader::StatCallback(const struct stat *_st) noexcept
{
assert(state == State::STAT);
assert(connection != nullptr);
assert(fh != nullptr);
assert(st != nullptr);
assert(_st != nullptr);
#if defined(_WIN32) && !defined(_WIN64)
/* on 32-bit Windows, libnfs enables -D_FILE_OFFSET_BITS=64,
but MPD (Meson) doesn't - to work around this mismatch, we
cast explicitly to "struct stat64" */
const auto *st = (const struct stat64 *)_st;
#else
const auto *st = _st;
#endif
if (!S_ISREG(st->st_mode)) {
OnNfsFileError(std::make_exception_ptr(std::runtime_error("Not a regular file")));
return;
}
state = State::IDLE;
OnNfsFileOpen(st->st_size);
}
void
NfsFileReader::OnNfsCallback(unsigned status, void *data) noexcept
{
switch (state) {
switch (std::exchange(state, State::IDLE)) {
case State::INITIAL:
case State::DEFER:
case State::MOUNT:
@@ -234,7 +239,6 @@ NfsFileReader::OnNfsCallback(unsigned status, void *data) noexcept
break;
case State::READ:
state = State::IDLE;
OnNfsFileRead(data, status);
break;
}

View File

@@ -41,6 +41,6 @@ public:
LockGuard &operator=(const LockGuard &) = delete;
};
};
}
#endif

View File

@@ -30,6 +30,6 @@ namespace ixmlwrap {
const char *getFirstElementValue(IXML_Document *doc,
const char *name) noexcept;
};
}
#endif /* _IXMLWRAP_H_INCLUDED_ */

View File

@@ -29,6 +29,9 @@
#else
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
/* on macOS, OpenAL is deprecated, but since the user asked to enable
this plugin, let's ignore the compiler warnings */
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
class OpenALOutput final : AudioOutput {

View File

@@ -21,6 +21,28 @@
#include "PcmBuffer.hxx"
#include "util/ConstBuffer.hxx"
/*
* According to:
* - https://xiph.org/flac/format.html#frame_header
* - https://github.com/nu774/qaac/wiki/Multichannel--handling
* the source channel order (after decoding, e.g., flac, alac) is for
* - 1ch: mono
* - 2ch: left, right
* - 3ch: left, right, center
* - 4ch: front left, front right, back left, back right
* - 5ch: front left, front right, front center, back/surround left, back/surround right
* - 6ch (aka 5.1): front left, front right, front center, LFE, back/surround left, back/surround right
* - 7ch: front left, front right, front center, LFE, back center, side left, side right
* - 8ch: (aka 7.1): front left, front right, front center, LFE, back left, back right, side left, side right
*
* The ALSA default channel map is (see /usr/share/alsa/pcm/surround71.conf):
* - front left, front right, back left, back right, front center, LFE, side left, side right
*
* Hence, in case of the following source channel orders 3ch, 5ch, 6ch (aka
* 5.1), 7ch and 8ch the channel order has to be adapted
*/
template<typename V>
struct TwoPointers {
V *dest;
@@ -44,17 +66,57 @@ struct TwoPointers {
return *this;
}
TwoPointers<V> &ToAlsa50() noexcept {
*dest++ = src[0]; // front left
*dest++ = src[1]; // front right
*dest++ = src[3]; // surround left
*dest++ = src[4]; // surround right
*dest++ = src[2]; // front center
src += 5;
return *this;
}
TwoPointers<V> &ToAlsa51() noexcept {
return CopyTwo() // left+right
.SwapTwoPairs(); // center, LFE, surround left+right
}
TwoPointers<V> &ToAlsa70() noexcept {
*dest++ = src[0]; // front left
*dest++ = src[1]; // front right
*dest++ = src[5]; // side left
*dest++ = src[6]; // side right
*dest++ = src[2]; // front center
*dest++ = src[3]; // LFE
*dest++ = src[4]; // back center
src += 7;
return *this;
}
TwoPointers<V> &ToAlsa71() noexcept {
return ToAlsa51()
.CopyTwo(); // side left+right
}
};
template<typename V>
static void
ToAlsaChannelOrder50(V *dest, const V *src, size_t n) noexcept
{
TwoPointers<V> p{dest, src};
for (size_t i = 0; i != n; ++i)
p.ToAlsa50();
}
template<typename V>
static inline ConstBuffer<V>
ToAlsaChannelOrder50(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
{
auto dest = buffer.GetT<V>(src.size);
ToAlsaChannelOrder50(dest, src.data, src.size / 5);
return { dest, src.size };
}
template<typename V>
static void
ToAlsaChannelOrder51(V *dest, const V *src, size_t n) noexcept
@@ -73,6 +135,24 @@ ToAlsaChannelOrder51(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
return { dest, src.size };
}
template<typename V>
static void
ToAlsaChannelOrder70(V *dest, const V *src, size_t n) noexcept
{
TwoPointers<V> p{dest, src};
for (size_t i = 0; i != n; ++i)
p.ToAlsa70();
}
template<typename V>
static inline ConstBuffer<V>
ToAlsaChannelOrder70(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
{
auto dest = buffer.GetT<V>(src.size);
ToAlsaChannelOrder70(dest, src.data, src.size / 7);
return { dest, src.size };
}
template<typename V>
static void
ToAlsaChannelOrder71(V *dest, const V *src, size_t n) noexcept
@@ -97,9 +177,15 @@ ToAlsaChannelOrderT(PcmBuffer &buffer, ConstBuffer<V> src,
unsigned channels) noexcept
{
switch (channels) {
case 5: // 5.0
return ToAlsaChannelOrder50(buffer, src);
case 6: // 5.1
return ToAlsaChannelOrder51(buffer, src);
case 7: // 7.0
return ToAlsaChannelOrder70(buffer, src);
case 8: // 7.1
return ToAlsaChannelOrder71(buffer, src);

View File

@@ -223,7 +223,8 @@ private:
*
* Caller must lock the mutex.
*/
void StartDecoder(std::shared_ptr<MusicPipe> pipe) noexcept;
void StartDecoder(std::shared_ptr<MusicPipe> pipe,
bool initial_seek_essential) noexcept;
/**
* The decoder has acknowledged the "START" command (see
@@ -364,7 +365,8 @@ public:
};
void
Player::StartDecoder(std::shared_ptr<MusicPipe> _pipe) noexcept
Player::StartDecoder(std::shared_ptr<MusicPipe> _pipe,
bool initial_seek_essential) noexcept
{
assert(queued || pc.command == PlayerCommand::SEEK);
assert(pc.next_song != nullptr);
@@ -376,6 +378,7 @@ Player::StartDecoder(std::shared_ptr<MusicPipe> _pipe) noexcept
dc.Start(std::make_unique<DetachedSong>(*pc.next_song),
start_time, pc.next_song->GetEndTime(),
initial_seek_essential,
buffer, std::move(_pipe));
}
@@ -633,7 +636,7 @@ Player::SeekDecoder() noexcept
pipe->Clear();
/* re-start the decoder */
StartDecoder(pipe);
StartDecoder(pipe, true);
ActivateDecoder();
pc.seeking = true;
@@ -711,7 +714,7 @@ Player::ProcessCommand() noexcept
pc.CommandFinished();
if (dc.IsIdle())
StartDecoder(std::make_shared<MusicPipe>());
StartDecoder(std::make_shared<MusicPipe>(), false);
break;
@@ -964,6 +967,12 @@ Player::SongBorder() noexcept
if (border_pause) {
paused = true;
pc.listener.OnBorderPause();
/* drain all outputs to guarantee the current song is
really being played to the end; without this, the
Pause() call would drop all ring buffers */
pc.outputs.Drain();
pc.outputs.Pause();
idle_add(IDLE_PLAYER);
}
@@ -976,7 +985,7 @@ Player::Run() noexcept
const std::lock_guard<Mutex> lock(pc.mutex);
StartDecoder(pipe);
StartDecoder(pipe, true);
ActivateDecoder();
pc.state = PlayerState::PLAY;
@@ -1016,7 +1025,7 @@ Player::Run() noexcept
assert(dc.pipe == nullptr || dc.pipe == pipe);
StartDecoder(std::make_shared<MusicPipe>());
StartDecoder(std::make_shared<MusicPipe>(), false);
}
if (/* no cross-fading if MPD is going to pause at the

View File

@@ -33,9 +33,16 @@
#include "Instance.hxx"
#include "Log.hxx"
#include <set>
#ifdef __clang__
/* ignore -Wcomma due to strange code in boost/array.hpp (in Boost
1.72) */
#pragma GCC diagnostic ignored "-Wcomma"
#endif
#include <boost/crc.hpp>
#include <set>
#define MOUNT_STATE_BEGIN "mount_begin"
#define MOUNT_STATE_END "mount_end"
#define MOUNT_STATE_STORAGE_URI "uri: "

View File

@@ -402,7 +402,7 @@ private:
break;
case State::HREF:
response.href.assign(s, len);
response.href.append(s, len);
break;
case State::STATUS:
@@ -482,7 +482,7 @@ class HttpListDirectoryOperation final : public PropfindOperation {
public:
HttpListDirectoryOperation(CurlGlobal &curl, const char *uri)
:PropfindOperation(curl, uri, 1),
base_path(UriPathOrSlash(uri)) {}
base_path(CurlUnescape(GetEasy(), UriPathOrSlash(uri))) {}
std::unique_ptr<StorageDirectoryReader> Perform() {
DeferStart();
@@ -507,8 +507,7 @@ private:
/* kludge: ignoring case in this comparison to avoid
false negatives if the web server uses a different
case in hex digits in escaped characters; TODO:
implement properly */
case */
path = StringAfterPrefixIgnoreCase(path, base_path.c_str());
if (path == nullptr || *path == 0)
return nullptr;
@@ -531,11 +530,12 @@ protected:
if (r.status != 200)
return;
const auto escaped_name = HrefToEscapedName(r.href.c_str());
if (escaped_name.IsNull())
std::string href = CurlUnescape(GetEasy(), r.href.c_str());
const auto name = HrefToEscapedName(href.c_str());
if (name.IsNull())
return;
entries.emplace_front(CurlUnescape(GetEasy(), escaped_name));
entries.emplace_front(std::string(name.data, name.size));
auto &info = entries.front().info;
info = StorageFileInfo(r.collection

View File

@@ -45,6 +45,10 @@ ApplyTagFallback(TagType type, F &&f) noexcept
"AlbumArtist"/"ArtistSort" was found */
return f(TAG_ARTIST);
if (type == TAG_ALBUM_SORT)
/* fall back to "Album" if no "AlbumSort" was found */
return f(TAG_ALBUM);
return false;
}

View File

@@ -94,7 +94,7 @@ SetThreadIdlePriority() noexcept
#elif defined(_WIN32)
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
#endif
};
}
void
SetThreadRealtime()
@@ -111,4 +111,4 @@ SetThreadRealtime()
if (linux_sched_setscheduler(0, policy, &sched_param) < 0)
throw MakeErrno("sched_setscheduler failed");
#endif // __linux__
};
}

View File

@@ -30,11 +30,16 @@
#ifndef MATH_HXX
#define MATH_HXX
#if (defined(__GLIBCPP__) || defined(__GLIBCXX__)) && !defined(_GLIBCXX_USE_C99_MATH)
#include <cmath>
/*
* C99 math can be optionally omitted with gcc's libstdc++.
* Use boost if unavailable.
*/
#if (defined(__GLIBCPP__) || defined(__GLIBCXX__)) && !defined(_GLIBCXX_USE_C99_MATH_TR1)
#include <boost/math/special_functions/round.hpp>
using boost::math::lround;
#else
#include <cmath>
using std::lround;
#endif

View File

@@ -30,7 +30,7 @@
#ifndef STRING_FORMAT_HXX
#define STRING_FORMAT_HXX
#include "StringBuffer.hxx"
#include "StringBuffer.hxx" // IWYU pragma: export
#include <stdio.h>

View File

@@ -121,6 +121,6 @@ namespace TemplateString {
template<>
struct Concat<> : Empty {};
};
}
#endif

View File

@@ -32,7 +32,7 @@
#include "Compiler.h"
#include <wchar.h>
#include <cwchar>
gcc_pure gcc_nonnull_all
static inline size_t

View File

@@ -26,9 +26,7 @@ static unsigned
FromAvahiWatchEvent(AvahiWatchEvent e)
{
return (e & AVAHI_WATCH_IN ? SocketMonitor::READ : 0) |
(e & AVAHI_WATCH_OUT ? SocketMonitor::WRITE : 0) |
(e & AVAHI_WATCH_ERR ? SocketMonitor::ERROR : 0) |
(e & AVAHI_WATCH_HUP ? SocketMonitor::HANGUP : 0);
(e & AVAHI_WATCH_OUT ? SocketMonitor::WRITE : 0);
}
static AvahiWatchEvent

View File

@@ -96,6 +96,9 @@ thirdparty_libs = [
zlib,
libid3tag,
liblame,
libmodplug,
wildmidi,
gme,
ffmpeg,
curl,
libexpat,