Compare commits

..

109 Commits

Author SHA1 Message Date
Max Kellermann
c67372f8af release v0.21.25 2020-07-06 21:41:53 +02:00
Max Kellermann
00789de7d4 db/upnp/Object: root nodes are allowed to omit parent_id and name
This fixes compatibility with Plex DLNA.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/851
2020-07-06 21:36:30 +02:00
Max Kellermann
5ece9685c2 PluginUnavailable: backport class PluginUnconfigured from master
Stop bothering people about the Tidal/Qobuz plugins.
2020-07-06 21:08:22 +02:00
Max Kellermann
e7c5a42821 Log: add Log() and LogFormat() overloads with std::exception_ptr
Make LogError()/FormatError() wrappers for those.  Now we can log
exceptions with a lower level.
2020-07-06 21:08:04 +02:00
Max Kellermann
36e6079c57 Log: make LogLevel the first parameter
Prepare for templated functions.
2020-07-06 21:07:26 +02:00
Max Kellermann
e5f23678ca Log: use GetFullMessage() to print exceptions
Print all nested exceptions on a single line to avoid confusion.
2020-07-06 21:07:16 +02:00
Max Kellermann
749ad7cd83 PluginUnavailable: inherit the base class constructor 2020-07-06 20:40:25 +02:00
Max Kellermann
0b59f4eaee doc/plugins.rst: merge redundant nfs:// documentation 2020-07-06 20:37:58 +02:00
Max Kellermann
402663de74 doc/plugins.rst: more markup 2020-07-06 20:34:04 +02:00
Max Kellermann
eaa66c7ee3 doc/plugins.rst: add smb:// with password example
Closes https://github.com/MusicPlayerDaemon/MPD/issues/864
2020-07-06 20:32:41 +02:00
Max Kellermann
996714d6ff doc/plugins.rst: more markup 2020-07-06 20:32:11 +02:00
Max Kellermann
fe48e5596f command/storage: automatically scan new mounts
Closes https://github.com/MusicPlayerDaemon/MPD/issues/841
2020-07-06 20:23:41 +02:00
Max Kellermann
d7744d2b8e command/storage: check if storage is already mounted
Mounting one storage URI twice on different mount points can lead to
conflicts with the database cache file, and it doesn't make a lot of
sense.

But most importantly, our udisks storage plugin will unmount the disk
from the kernel VFS, and if two exist, they will compete with each
others.  We could (and should) fix this in the udisks storage plugin,
but for now, this workaround is good enough (and useful).
2020-07-06 18:02:47 +02:00
Max Kellermann
33ee35ab92 command/storage: check if mount point is busy
When mounting something over a directory that is already a mount
point, CompositeStorage::Mount() silently overwrites the previously
mounted storage, disposing it.  After that, SimpleDatabase::Mount()
will fail and handle_mount() will roll back the
CompositeStorage::Mount() command, effectively unmounting what was
there before (and also leaking memory).

Closes https://github.com/MusicPlayerDaemon/MPD/issues/918
2020-07-06 17:49:38 +02:00
Max Kellermann
5b291ff768 db/update/Walk: pass concatenated .mpdignore URI to storage.MapUTF8()
Fixes the "Unrecognized URI" error with the udisks storage plugin,
which is caused by the kludge in UdisksStorage::MapUTF8().
2020-07-06 17:19:38 +02:00
Max Kellermann
39d6816a6d neighbor/upnp: roll back changes if DoOpen() fails 2020-07-06 16:23:58 +02:00
Max Kellermann
6517b2d2ac neighbor/upnp: remove D-Bus filter and match in Close()
Fixes use-after-free crash bug during MPD shutdown.
2020-07-06 16:15:18 +02:00
Max Kellermann
bfdf13dca3 decoder/Plugin: allow scan_{file,stream}() to throw
Bug #915 is about an I/O exception thrown where none was allowed,
leading to crash via std::terminate().  However, instead of catching
and logging the error inside the decoder plugin, it should be able to
propagate the I/O error to the MPD core, so MPD can avoid trying other
decoder plugins.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/915
2020-07-06 14:13:34 +02:00
Max Kellermann
daefc61aa4 output/osx: postpone start until the end of Play()
Wait until there is data in the ring buffer.
2020-07-02 15:26:38 +02:00
Max Kellermann
6fed6e50e4 output/osx: merge some duplicate code 2020-07-02 15:25:51 +02:00
Max Kellermann
bc9e074822 output/osx: postpone start until the first Play() call
Wait until there is some data; don't let our render callback be
invoked without any data.
2020-07-02 15:21:54 +02:00
Max Kellermann
8047102542 output/osx: don't restart AudioUnit at the end of Cancel()
We shouldn't restart the AudioUnit while the ring buffer is empty, or
else our render callback may emit noise.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/771
2020-07-02 15:20:43 +02:00
Max Kellermann
fe5b81e180 output/osx: check started in Close() and Cancel() 2020-07-02 15:19:40 +02:00
Max Kellermann
f032925c2d output/osx: add started flag
This will keep track of AudioOutputUnitStart() and
AudioOutputUnitStop().  This will provide some separation between "not
(yet) (re)started" and "paused".
2020-07-02 15:18:37 +02:00
Max Kellermann
8125a5dddb output/osx: don't uninitialize AudioUnit if restart fails
This shall be done by Close(), which will be called automatically
after an error.
2020-07-02 15:10:03 +02:00
Max Kellermann
154170e475 output/osx: clear pause flag only after successful AudioOutputUnitStart() 2020-07-02 15:08:59 +02:00
Max Kellermann
fb83936feb apple/AudioUnit: add AudioUnitSetPropertyT() 2020-07-02 14:59:40 +02:00
Max Kellermann
db8bf52f7d apple/AudioObject: add AudioObjectGetStringProperty() 2020-07-02 13:50:05 +02:00
Max Kellermann
756f0b8027 apple: build static library
Move build rules from src/output/plugins/meson.build
2020-07-02 13:49:54 +02:00
Max Kellermann
b1fba8d3d7 apple/AudioObject: add missing inline 2020-07-02 13:49:52 +02:00
Max Kellermann
e606044271 apple/AudioUnit: library wrapping AudioUnit*() functions 2020-07-01 23:02:22 +02:00
Max Kellermann
bcbb3371ff apple/AudioUnit: rename to AudioObject.hxx 2020-07-01 22:49:03 +02:00
Max Kellermann
de632882d1 output/osx: move code to FindAudioDeviceByName() 2020-07-01 22:48:12 +02:00
Max Kellermann
745e492d15 output/osx: use [[maybe_unused]] 2020-07-01 22:41:00 +02:00
Max Kellermann
c5dc615efe output/osx: use IsDigitASCII() 2020-07-01 22:39:54 +02:00
Max Kellermann
beeb02025e output/osx: use range-based for 2020-07-01 22:06:36 +02:00
Max Kellermann
cdf7062597 apple/AudioUnit: wrapper functions for AudioObject properties 2020-07-01 22:05:11 +02:00
Max Kellermann
346084da1e apple/Throw: new helper library replacing osx_os_status_to_cstring() 2020-07-01 22:05:11 +02:00
Max Kellermann
bbceb5eb91 output/osx: silently ignore some errors in osx_output_set_device() 2020-07-01 22:05:11 +02:00
Max Kellermann
90d85319c2 apple/ErrorRef: new library wrapping CFErrorRef 2020-07-01 22:05:10 +02:00
Max Kellermann
3d03683e7d output: use StringIsEqual() 2020-07-01 22:04:26 +02:00
Max Kellermann
d8a74802d1 apple/StringRef: new library wrapping CFStringRef 2020-07-01 22:01:53 +02:00
Max Kellermann
191919d1b1 output/osx: remove trailing newline from exception messages 2020-07-01 22:01:51 +02:00
Max Kellermann
df38e7565b util/HugeAllocator: import std::swap() 2020-07-01 21:56:58 +02:00
Max Kellermann
cb49a03fd7 util/HugeAllocator: add noexcept 2020-07-01 21:56:54 +02:00
Max Kellermann
faee5bbb78 decoder/opus: implement End Trimming (RFC7845 4.4)
Closes https://github.com/MusicPlayerDaemon/MPD/issues/867
2020-07-01 21:26:34 +02:00
Max Kellermann
7befab7e83 decoder/opus: keep track of the granulepos
Will be needed for End Trimming (RFC7845 4.4,
https://github.com/MusicPlayerDaemon/MPD/issues/867).
2020-07-01 21:21:06 +02:00
Max Kellermann
4244e61214 decoder/opus: simplify indentation in HandleAudio() 2020-07-01 21:19:52 +02:00
Max Kellermann
46eab05045 decoder/opus: allocate buffer only in the first chained song
Fixes memory leak.  That's what we get for
2020-07-01 21:07:49 +02:00
Max Kellermann
5ca137c73c decoder/opus: add API docs 2020-07-01 20:55:18 +02:00
Max Kellermann
760238fe16 decoder/opus: apply pre-skip (RFC7845 4.2)
Fixes the first part of
https://github.com/MusicPlayerDaemon/MPD/issues/867
2020-07-01 20:44:53 +02:00
Max Kellermann
a99b4abae8 decoder/OpusHead: return pre-skip 2020-07-01 17:51:07 +02:00
Max Kellermann
472881cb95 util/ByteOrder: remove redundant inline keywords from constexpr functions 2020-07-01 17:50:34 +02:00
Max Kellermann
c4efc37ad8 system/ByteOrder: move to util/ 2020-07-01 17:49:57 +02:00
Max Kellermann
691b6a236e output/osx: improve sample rate selection
The formula in osx_output_score_sample_rate() to detect multiples of
the source sample rate was broken: when given a 44.1 kHz input file,
it preferred 16 kHz over 48 kHz, because its `frac_portion(16)=0.75`
is smaller than `frac_portion(48)=0.91`.

That formula, introduced by commit 40a1ebee29, looks completely
wrong.  It doesn't do what the code comment pretends it does.

Instead of using that `frac_portion` to calculate a score, this patch
adds to the score only if `frac_portion` is nearly `0` or `1`.  This
means that the factor is nearly integer.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/904
2020-07-01 17:38:08 +02:00
Max Kellermann
5c7243d3ad output/osx: make several fields const 2020-07-01 17:35:39 +02:00
Max Kellermann
44cfdff39a output/osx: make variables more local 2020-07-01 17:35:33 +02:00
Max Kellermann
5eedda691a output/osx: make more AudioObjectPropertyAddress instances static constexpr 2020-07-01 17:35:19 +02:00
Max Kellermann
a30d5e1b6a output/osx: make AudioObjectPropertyAddress variables static constexpr 2020-07-01 17:34:12 +02:00
Max Kellermann
8ef09a0a71 output/osx: don't use C99 designated initializers
Fixes `-Wpedantic`.
2020-07-01 17:34:06 +02:00
Max Kellermann
e8044663b3 output/{alsa,osx}: use ConstBuffer::empty() 2020-07-01 17:32:37 +02:00
Max Kellermann
8444c33514 output/osx: don't use variable-length arrays 2020-07-01 17:31:46 +02:00
Max Kellermann
2b7328b434 output/osx: fix coding style 2020-07-01 17:11:02 +02:00
Max Kellermann
ca705e1e37 python/build/meson.py: set BOOST_ROOT for Meson 0.54
Commit
08224dafcb
changed Meson to require BOOST_ROOT for cross builds.
2020-07-01 16:55:28 +02:00
Max Kellermann
d9f9b3df10 input/file: detect premature end of file
A bug report (https://github.com/MusicPlayerDaemon/MPD/issues/912)
suggests that on Linux, reading on `cifs` files may rarely return 0 (=
end of file) before the end of the file has really been reached.  But
that's just a theory which I need to validate, so this runtime check
shall catch this condition before the assertion in
DecoderBridge::Read() crashes MPD.  Let's see.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/912
2020-07-01 15:14:27 +02:00
Max Kellermann
a43ee97746 util/UriUtil: strip credentials from smb:// URIs
Closes https://github.com/MusicPlayerDaemon/MPD/issues/910
2020-06-22 22:48:56 +02:00
Max Kellermann
43c32372e7 util/UriUtil: make schemes array static 2020-06-22 22:48:07 +02:00
Max Kellermann
5716cde1fb queue/PlaylistEdit: fix crash in SetSongIdRange() while playing
An assertion failure in UpdateQueuedSong() could trigger because the
`prev` parameter is always `nullptr`, but `queued` may be set.  And in
fact, calling UpdateQueuedSong() is only necessary when the queued
song was edited, to re-queue it with the new range.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/901
2020-06-11 07:07:02 +02:00
Max Kellermann
b7a99b4a4b increment version number to 0.21.25 2020-06-11 06:29:08 +02:00
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
122 changed files with 1749 additions and 799 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

45
NEWS
View File

@@ -1,3 +1,48 @@
ver 0.21.25 (2020/07/06)
* protocol:
- fix crash when using "rangeid" while playing
* database
- simple: automatically scan new mounts
- upnp: fix compatibility with Plex DLNA
* storage
- fix disappearing mounts after mounting twice
- udisks: fix reading ".mpdignore"
* input
- file: detect premature end of file
- smbclient: don't send credentials to MPD clients
* decoder
- opus: apply pre-skip and end trimming
- opus: fix memory leak
- opus: fix crash bug
- vorbis: fix crash bug
* output
- osx: improve sample rate selection
- osx: fix noise while stopping
* neighbor
- upnp: fix crash during shutdown
* Windows/Android:
- fix Boost detection after breaking change in Meson 0.54
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

View File

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="46"
android:versionName="0.21.23">
android:versionCode="48"
android:versionName="0.21.25">
<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.23'
version = '0.21.25'
# The full version, including alpha/beta/rc tags.
release = version

View File

@@ -60,25 +60,25 @@ The default plugin which gives :program:`MPD` access to local files. It is used
curl
----
A WebDAV client using libcurl. It is used when :code:`music_directory` contains a http:// or https:// URI, for example :samp:`https://the.server/dav/`.
A WebDAV client using libcurl. It is used when :code:`music_directory`
contains a ``http://`` or ``https://`` URI, for example
:samp:`https://the.server/dav/`.
smbclient
---------
Load music files from a SMB/CIFS server. It is used when :code:`music_directory` contains a smb:// URI, for example :samp:`smb://myfileserver/Music`.
Load music files from a SMB/CIFS server. It is used when
:code:`music_directory` contains a ``smb://`` URI, for example
:samp:`smb://myfileserver/Music`.
nfs
---
Load music files from a NFS server. It is used when :code:`music_directory` contains a nfs:// URI according to RFC2224, for example :samp:`nfs://servername/path`.
Load music files from a NFS server. It is used when
:code:`music_directory` contains a ``nfs://`` URI according to
RFC2224, for example :samp:`nfs://servername/path`.
This plugin uses libnfs, which supports only NFS version 3. Since :program:`MPD` is not allowed to bind to "privileged ports", the NFS server needs to enable the "insecure" setting; example :file:`/etc/exports`:
.. code-block:: none
/srv/mp3 192.168.1.55(ro,insecure)
Don't fear: "insecure" does not mean that your NFS server is insecure. A few decades ago, people thought the concept of "privileged ports" would make network services "secure", which was a fallacy. The absence of this obsolete "security" measure means little.
See :ref:`input_nfs` for more information.
udisks
------
@@ -162,7 +162,10 @@ curl
Opens remote files or streams over HTTP using libcurl.
Note that unless overridden by the below settings (e.g. by setting them to a blank value), general curl configuration from environment variables such as http_proxy or specified in :file:`~/.curlrc` will be in effect.
Note that unless overridden by the below settings (e.g. by setting
them to a blank value), general curl configuration from environment
variables such as ``http_proxy`` or specified in :file:`~/.curlrc`
will be in effect.
.. list-table::
:widths: 20 80
@@ -182,7 +185,9 @@ Note that unless overridden by the below settings (e.g. by setting them to a bla
ffmpeg
------
Access to various network protocols implemented by the FFmpeg library: gopher://, rtp://, rtsp://, rtmp://, rtmpt://, rtmps://
Access to various network protocols implemented by the FFmpeg library:
``gopher://``, ``rtp://``, ``rtsp://``, ``rtmp://``, ``rtmpt://``,
``rtmps://``
file
----
@@ -194,30 +199,51 @@ mms
Plays streams with the MMS protocol using `libmms <https://launchpad.net/libmms>`_.
.. _input_nfs:
nfs
---
Allows :program:`MPD` to access files on NFSv3 servers without actually mounting them (i.e. in userspace, without help from the kernel's VFS layer). All URIs with the nfs:// scheme are used according to RFC2224. Example:
Allows :program:`MPD` to access files on NFS servers without actually
mounting them (i.e. with :program:`libnfs` in userspace, without help
from the kernel's VFS layer). All URIs with the ``nfs://`` scheme are
used according to RFC2224. Example:
.. code-block:: none
mpc add nfs://servername/path/filename.ogg
Note that this usually requires enabling the "insecure" flag in the server's /etc/exports file, because :program:`MPD` cannot bind to so-called "privileged" ports. Don't fear: this will not make your file server insecure; the flag was named in a time long ago when privileged ports were thought to be meaningful for security. By today's standards, NFSv3 is not secure at all, and if you believe it is, you're already doomed.
This plugin uses :program:`libnfs`, which supports only NFS version 3.
Since :program:`MPD` is not allowed to bind to so-called "privileged
ports", the NFS server needs to enable the ``insecure`` setting;
example :file:`/etc/exports`:
.. code-block:: none
/srv/mp3 192.168.1.55(ro,insecure)
Don't fear: this will not make your file server insecure; the flag was
named a time long ago when privileged ports were thought to be
meaningful for security. By today's standards, NFSv3 is not secure at
all, and if you believe it is, you're already doomed.
smbclient
---------
Allows :program:`MPD` to access files on SMB/CIFS servers (e.g. Samba or Microsoft Windows). All URIs with the smb:// scheme are used. Example:
Allows :program:`MPD` to access files on SMB/CIFS servers (e.g. Samba
or Microsoft Windows). All URIs with the ``smb://`` scheme are
used. Example:
.. code-block:: none
mpc add smb://servername/sharename/filename.ogg
mpc add smb://username:password@servername/sharename/filename.ogg
qobuz
-----
Play songs from the commercial streaming service Qobuz. It plays URLs in the form qobuz://track/ID, e.g.:
Play songs from the commercial streaming service Qobuz. It plays URLs
in the form ``qobuz://track/ID``, e.g.:
.. code-block:: none
@@ -243,7 +269,9 @@ Play songs from the commercial streaming service Qobuz. It plays URLs in the for
tidal
-----
Play songs from the commercial streaming service `Tidal <http://tidal.com/>`_. It plays URLs in the form tidal://track/ID, e.g.:
Play songs from the commercial streaming service `Tidal
<http://tidal.com/>`_. It plays URLs in the form ``tidal://track/ID``,
e.g.:
.. warning::

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.23',
version: '0.21.25',
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)
@@ -315,6 +319,8 @@ subdir('src/thread')
subdir('src/net')
subdir('src/event')
subdir('src/apple')
subdir('src/lib/dbus')
subdir('src/lib/icu')
subdir('src/lib/smbclient')

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

@@ -91,7 +91,12 @@ def configure(toolchain, src, build, args=()):
'--cross-file', cross_file,
] + args
subprocess.check_call(configure, env=toolchain.env)
env = toolchain.env.copy()
# Meson 0.54 requires the BOOST_ROOT environment variable
env['BOOST_ROOT'] = toolchain.install_prefix
subprocess.check_call(configure, env=env)
class MesonProject(Project):
def __init__(self, url, md5, installed, configure_args=[],

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

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,8 +19,7 @@
#include "LogV.hxx"
#include "util/Domain.hxx"
#include <exception>
#include "util/Exception.hxx"
#include <stdio.h>
#include <string.h>
@@ -29,20 +28,20 @@
static constexpr Domain exception_domain("exception");
void
LogFormatV(const Domain &domain, LogLevel level,
LogFormatV(LogLevel level, const Domain &domain,
const char *fmt, va_list ap) noexcept
{
char msg[1024];
vsnprintf(msg, sizeof(msg), fmt, ap);
Log(domain, level, msg);
Log(level, domain, msg);
}
void
LogFormat(const Domain &domain, LogLevel level, const char *fmt, ...) noexcept
LogFormat(LogLevel level, const Domain &domain, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, level, fmt, ap);
LogFormatV(level, domain, fmt, ap);
va_end(ap);
}
@@ -51,7 +50,7 @@ FormatDebug(const Domain &domain, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, LogLevel::DEBUG, fmt, ap);
LogFormatV(LogLevel::DEBUG, domain, fmt, ap);
va_end(ap);
}
@@ -60,7 +59,7 @@ FormatInfo(const Domain &domain, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, LogLevel::INFO, fmt, ap);
LogFormatV(LogLevel::INFO, domain, fmt, ap);
va_end(ap);
}
@@ -69,7 +68,7 @@ FormatDefault(const Domain &domain, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, LogLevel::DEFAULT, fmt, ap);
LogFormatV(LogLevel::DEFAULT, domain, fmt, ap);
va_end(ap);
}
@@ -78,7 +77,7 @@ FormatWarning(const Domain &domain, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, LogLevel::WARNING, fmt, ap);
LogFormatV(LogLevel::WARNING, domain, fmt, ap);
va_end(ap);
}
@@ -87,42 +86,24 @@ FormatError(const Domain &domain, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, LogLevel::ERROR, fmt, ap);
LogFormatV(LogLevel::ERROR, domain, fmt, ap);
va_end(ap);
}
void
LogError(const std::exception &e) noexcept
Log(LogLevel level, const std::exception &e) noexcept
{
Log(exception_domain, LogLevel::ERROR, e.what());
try {
std::rethrow_if_nested(e);
} catch (const std::exception &nested) {
LogError(nested, "nested");
} catch (...) {
Log(exception_domain, LogLevel::ERROR,
"Unrecognized nested exception");
}
Log(level, exception_domain, GetFullMessage(e).c_str());
}
void
LogError(const std::exception &e, const char *msg) noexcept
Log(LogLevel level, const std::exception &e, const char *msg) noexcept
{
FormatError(exception_domain, "%s: %s", msg, e.what());
try {
std::rethrow_if_nested(e);
} catch (const std::exception &nested) {
LogError(nested);
} catch (...) {
Log(exception_domain, LogLevel::ERROR,
"Unrecognized nested exception");
}
LogFormat(level, exception_domain, "%s: %s", msg, GetFullMessage(e).c_str());
}
void
FormatError(const std::exception &e, const char *fmt, ...) noexcept
LogFormat(LogLevel level, const std::exception &e, const char *fmt, ...) noexcept
{
char msg[1024];
va_list ap;
@@ -130,37 +111,24 @@ FormatError(const std::exception &e, const char *fmt, ...) noexcept
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
LogError(e, msg);
Log(level, e, msg);
}
void
LogError(const std::exception_ptr &ep) noexcept
Log(LogLevel level, const std::exception_ptr &ep) noexcept
{
try {
std::rethrow_exception(ep);
} catch (const std::exception &e) {
LogError(e);
} catch (...) {
Log(exception_domain, LogLevel::ERROR,
"Unrecognized exception");
}
Log(level, exception_domain, GetFullMessage(ep).c_str());
}
void
LogError(const std::exception_ptr &ep, const char *msg) noexcept
Log(LogLevel level, const std::exception_ptr &ep, const char *msg) noexcept
{
try {
std::rethrow_exception(ep);
} catch (const std::exception &e) {
LogError(e, msg);
} catch (...) {
FormatError(exception_domain,
"%s: Unrecognized exception", msg);
}
LogFormat(level, exception_domain, "%s: %s", msg,
GetFullMessage(ep).c_str());
}
void
FormatError(const std::exception_ptr &ep, const char *fmt, ...) noexcept
LogFormat(LogLevel level, const std::exception_ptr &ep, const char *fmt, ...) noexcept
{
char msg[1024];
va_list ap;
@@ -168,13 +136,13 @@ FormatError(const std::exception_ptr &ep, const char *fmt, ...) noexcept
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
LogError(ep, msg);
Log(level, ep, msg);
}
void
LogErrno(const Domain &domain, int e, const char *msg) noexcept
{
LogFormat(domain, LogLevel::ERROR, "%s: %s", msg, strerror(e));
LogFormat(LogLevel::ERROR, domain, "%s: %s", msg, strerror(e));
}
void

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -28,16 +28,38 @@
class Domain;
void
Log(const Domain &domain, LogLevel level, const char *msg) noexcept;
Log(LogLevel level, const Domain &domain, const char *msg) noexcept;
gcc_printf(3,4)
void
LogFormat(const Domain &domain, LogLevel level, const char *fmt, ...) noexcept;
LogFormat(LogLevel level, const Domain &domain, const char *fmt, ...) noexcept;
void
Log(LogLevel level, const std::exception &e) noexcept;
void
Log(LogLevel level, const std::exception &e, const char *msg) noexcept;
gcc_printf(3,4)
void
LogFormat(LogLevel level, const std::exception &e,
const char *fmt, ...) noexcept;
void
Log(LogLevel level, const std::exception_ptr &ep) noexcept;
void
Log(LogLevel level, const std::exception_ptr &ep, const char *msg) noexcept;
gcc_printf(3,4)
void
LogFormat(LogLevel level, const std::exception_ptr &ep,
const char *fmt, ...) noexcept;
static inline void
LogDebug(const Domain &domain, const char *msg) noexcept
{
Log(domain, LogLevel::DEBUG, msg);
Log(LogLevel::DEBUG, domain, msg);
}
gcc_printf(2,3)
@@ -47,7 +69,7 @@ FormatDebug(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogInfo(const Domain &domain, const char *msg) noexcept
{
Log(domain, LogLevel::INFO, msg);
Log(LogLevel::INFO, domain, msg);
}
gcc_printf(2,3)
@@ -57,7 +79,7 @@ FormatInfo(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogDefault(const Domain &domain, const char *msg) noexcept
{
Log(domain, LogLevel::DEFAULT, msg);
Log(LogLevel::DEFAULT, domain, msg);
}
gcc_printf(2,3)
@@ -67,7 +89,7 @@ FormatDefault(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogWarning(const Domain &domain, const char *msg) noexcept
{
Log(domain, LogLevel::WARNING, msg);
Log(LogLevel::WARNING, domain, msg);
}
gcc_printf(2,3)
@@ -77,28 +99,47 @@ FormatWarning(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogError(const Domain &domain, const char *msg) noexcept
{
Log(domain, LogLevel::ERROR, msg);
Log(LogLevel::ERROR, domain, msg);
}
void
LogError(const std::exception &e) noexcept;
inline void
LogError(const std::exception &e) noexcept
{
Log(LogLevel::ERROR, e);
}
void
LogError(const std::exception &e, const char *msg) noexcept;
inline void
LogError(const std::exception &e, const char *msg) noexcept
{
Log(LogLevel::ERROR, e, msg);
}
gcc_printf(2,3)
void
FormatError(const std::exception &e, const char *fmt, ...) noexcept;
template<typename... Args>
inline void
FormatError(const std::exception &e, const char *fmt, Args&&... args) noexcept
{
LogFormat(LogLevel::ERROR, e, fmt, std::forward<Args>(args)...);
}
void
LogError(const std::exception_ptr &ep) noexcept;
inline void
LogError(const std::exception_ptr &ep) noexcept
{
Log(LogLevel::ERROR, ep);
}
void
LogError(const std::exception_ptr &ep, const char *msg) noexcept;
inline void
LogError(const std::exception_ptr &ep, const char *msg) noexcept
{
Log(LogLevel::ERROR, ep, msg);
}
gcc_printf(2,3)
void
FormatError(const std::exception_ptr &ep, const char *fmt, ...) noexcept;
template<typename... Args>
inline void
FormatError(const std::exception_ptr &ep,
const char *fmt, Args&&... args) noexcept
{
LogFormat(LogLevel::ERROR, ep, fmt, std::forward<Args>(args)...);
}
gcc_printf(2,3)
void

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -176,7 +176,7 @@ FileLog(const Domain &domain, const char *message) noexcept
#endif /* !ANDROID */
void
Log(const Domain &domain, LogLevel level, const char *msg) noexcept
Log(LogLevel level, const Domain &domain, const char *msg) noexcept
{
#ifdef ANDROID
__android_log_print(ToAndroidLogLevel(level), "MPD",

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2019 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -25,7 +25,7 @@
#include <stdarg.h>
void
LogFormatV(const Domain &domain, LogLevel level,
LogFormatV(LogLevel level, const Domain &domain,
const char *fmt, va_list ap) noexcept;
#endif /* LOG_H */

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2020 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -27,10 +27,20 @@
* that this plugin is unavailable. It will be disabled, and MPD can
* continue initialization.
*/
class PluginUnavailable final : public std::runtime_error {
class PluginUnavailable : public std::runtime_error {
public:
explicit PluginUnavailable(const char *msg)
:std::runtime_error(msg) {}
using std::runtime_error::runtime_error;
};
/**
* Like #PluginUnavailable, but denotes that the plugin is not
* available because it was not explicitly enabled in the
* configuration. The message may describe the necessary steps to
* enable it.
*/
class PluginUnconfigured : public PluginUnavailable {
public:
using PluginUnavailable::PluginUnavailable;
};
#endif

View File

@@ -79,17 +79,22 @@ Song::UpdateFile(Storage &storage) noexcept
TagBuilder tag_builder;
auto new_audio_format = AudioFormat::Undefined();
const auto path_fs = storage.MapFS(relative_uri.c_str());
if (path_fs.IsNull()) {
const auto absolute_uri =
storage.MapUTF8(relative_uri.c_str());
if (!tag_stream_scan(absolute_uri.c_str(), tag_builder,
&new_audio_format))
return false;
} else {
if (!ScanFileTagsWithGeneric(path_fs, tag_builder,
try {
const auto path_fs = storage.MapFS(relative_uri.c_str());
if (path_fs.IsNull()) {
const auto absolute_uri =
storage.MapUTF8(relative_uri.c_str());
if (!tag_stream_scan(absolute_uri.c_str(), tag_builder,
&new_audio_format))
return false;
return false;
} else {
if (!ScanFileTagsWithGeneric(path_fs, tag_builder,
&new_audio_format))
return false;
}
} catch (...) {
// TODO: log or propagate I/O errors?
return false;
}
mtime = info.mtime;
@@ -153,8 +158,14 @@ DetachedSong::LoadFile(Path path) noexcept
return false;
TagBuilder tag_builder;
if (!ScanFileTagsWithGeneric(path, tag_builder))
try {
if (!ScanFileTagsWithGeneric(path, tag_builder))
return false;
} catch (...) {
// TODO: log or propagate I/O errors?
return false;
}
mtime = fi.GetModificationTime();
tag_builder.Commit(tag);
@@ -173,8 +184,14 @@ DetachedSong::Update() noexcept
return LoadFile(path_fs);
} else if (IsRemote()) {
TagBuilder tag_builder;
if (!tag_stream_scan(uri.c_str(), tag_builder))
try {
if (!tag_stream_scan(uri.c_str(), tag_builder))
return false;
} catch (...) {
// TODO: log or propagate I/O errors?
return false;
}
mtime = std::chrono::system_clock::time_point::min();
tag_builder.Commit(tag);

View File

@@ -47,11 +47,11 @@ public:
handler(_handler),
is(nullptr) {}
bool ScanFile(const DecoderPlugin &plugin) noexcept {
bool ScanFile(const DecoderPlugin &plugin) {
return plugin.ScanFile(path_fs, handler);
}
bool ScanStream(const DecoderPlugin &plugin) noexcept {
bool ScanStream(const DecoderPlugin &plugin) {
if (plugin.scan_stream == nullptr)
return false;
@@ -73,14 +73,14 @@ public:
return plugin.ScanStream(*is, handler);
}
bool Scan(const DecoderPlugin &plugin) noexcept {
bool Scan(const DecoderPlugin &plugin) {
return plugin.SupportsSuffix(suffix) &&
(ScanFile(plugin) || ScanStream(plugin));
}
};
bool
ScanFileTagsNoGeneric(Path path_fs, TagHandler &handler) noexcept
ScanFileTagsNoGeneric(Path path_fs, TagHandler &handler)
{
assert(!path_fs.IsNull());
@@ -100,7 +100,7 @@ ScanFileTagsNoGeneric(Path path_fs, TagHandler &handler) noexcept
bool
ScanFileTagsWithGeneric(Path path, TagBuilder &builder,
AudioFormat *audio_format) noexcept
AudioFormat *audio_format)
{
FullTagHandler h(builder, audio_format);

View File

@@ -30,22 +30,26 @@ class TagBuilder;
* but does not fall back to generic scanners (APE and ID3) if no tags
* were found (but the file was recognized).
*
* Throws on I/O error.
*
* @return true if the file was recognized (even if no metadata was
* found)
*/
bool
ScanFileTagsNoGeneric(Path path, TagHandler &handler) noexcept;
ScanFileTagsNoGeneric(Path path, TagHandler &handler);
/**
* Scan the tags of a song file. Invokes matching decoder plugins,
* and falls back to generic scanners (APE and ID3) if no tags were
* found (but the file was recognized).
*
* Throws on I/O error.
*
* @return true if the file was recognized (even if no metadata was
* found)
*/
bool
ScanFileTagsWithGeneric(Path path, TagBuilder &builder,
AudioFormat *audio_format=nullptr) noexcept;
AudioFormat *audio_format=nullptr);
#endif

View File

@@ -45,7 +45,7 @@ CheckDecoderPlugin(const DecoderPlugin &plugin,
}
bool
tag_stream_scan(InputStream &is, TagHandler &handler) noexcept
tag_stream_scan(InputStream &is, TagHandler &handler)
{
assert(is.IsReady());
@@ -73,19 +73,17 @@ tag_stream_scan(InputStream &is, TagHandler &handler) noexcept
}
bool
tag_stream_scan(const char *uri, TagHandler &handler) noexcept
try {
tag_stream_scan(const char *uri, TagHandler &handler)
{
Mutex mutex;
auto is = InputStream::OpenReady(uri, mutex);
return tag_stream_scan(*is, handler);
} catch (const std::exception &e) {
return false;
}
bool
tag_stream_scan(InputStream &is, TagBuilder &builder,
AudioFormat *audio_format) noexcept
AudioFormat *audio_format)
{
assert(is.IsReady());
@@ -102,12 +100,10 @@ tag_stream_scan(InputStream &is, TagBuilder &builder,
bool
tag_stream_scan(const char *uri, TagBuilder &builder,
AudioFormat *audio_format) noexcept
try {
AudioFormat *audio_format)
{
Mutex mutex;
auto is = InputStream::OpenReady(uri, mutex);
return tag_stream_scan(*is, builder, audio_format);
} catch (const std::exception &e) {
return false;
}

View File

@@ -29,29 +29,39 @@ class TagBuilder;
* Scan the tags of an #InputStream. Invokes matching decoder
* plugins, but does not invoke the special "APE" and "ID3" scanners.
*
* Throws on I/O error.
*
* @return true if the file was recognized (even if no metadata was
* found)
*/
bool
tag_stream_scan(InputStream &is, TagHandler &handler) noexcept;
tag_stream_scan(InputStream &is, TagHandler &handler);
/**
* Throws on I/O error.
*/
bool
tag_stream_scan(const char *uri, TagHandler &handler) noexcept;
tag_stream_scan(const char *uri, TagHandler &handler);
/**
* Scan the tags of an #InputStream. Invokes matching decoder
* plugins, and falls back to generic scanners (APE and ID3) if no
* tags were found (but the file was recognized).
*
* Throws on I/O error.
*
* @return true if the file was recognized (even if no metadata was
* found)
*/
bool
tag_stream_scan(InputStream &is, TagBuilder &builder,
AudioFormat *audio_format=nullptr) noexcept;
AudioFormat *audio_format=nullptr);
/**
* Throws on I/O error.
*/
bool
tag_stream_scan(const char *uri, TagBuilder &builder,
AudioFormat *audio_format=nullptr) noexcept;
AudioFormat *audio_format=nullptr);
#endif

40
src/apple/AudioObject.cxx Normal file
View File

@@ -0,0 +1,40 @@
/*
* Copyright 2020 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "AudioObject.hxx"
#include "StringRef.hxx"
Apple::StringRef
AudioObjectGetStringProperty(AudioObjectID inObjectID,
const AudioObjectPropertyAddress &inAddress)
{
auto s = AudioObjectGetPropertyDataT<CFStringRef>(inObjectID,
inAddress);
return Apple::StringRef(s);
}

105
src/apple/AudioObject.hxx Normal file
View File

@@ -0,0 +1,105 @@
/*
* Copyright 2020 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef APPLE_AUDIO_OBJECT_HXX
#define APPLE_AUDIO_OBJECT_HXX
#include "Throw.hxx"
#include "util/AllocatedArray.hxx"
#include <CoreAudio/AudioHardware.h>
#include <cstddef>
namespace Apple {
class StringRef;
}
inline std::size_t
AudioObjectGetPropertyDataSize(AudioObjectID inObjectID,
const AudioObjectPropertyAddress &inAddress)
{
UInt32 size;
OSStatus status = AudioObjectGetPropertyDataSize(inObjectID,
&inAddress,
0, nullptr, &size);
if (status != noErr)
Apple::ThrowOSStatus(status);
return size;
}
template<typename T>
T
AudioObjectGetPropertyDataT(AudioObjectID inObjectID,
const AudioObjectPropertyAddress &inAddress)
{
OSStatus status;
UInt32 size = sizeof(T);
T value;
status = AudioObjectGetPropertyData(inObjectID, &inAddress,
0, nullptr,
&size, &value);
if (status != noErr)
Apple::ThrowOSStatus(status);
return value;
}
Apple::StringRef
AudioObjectGetStringProperty(AudioObjectID inObjectID,
const AudioObjectPropertyAddress &inAddress);
template<typename T>
AllocatedArray<T>
AudioObjectGetPropertyDataArray(AudioObjectID inObjectID,
const AudioObjectPropertyAddress &inAddress)
{
OSStatus status;
UInt32 size;
status = AudioObjectGetPropertyDataSize(inObjectID,
&inAddress,
0, nullptr, &size);
if (status != noErr)
Apple::ThrowOSStatus(status);
AllocatedArray<T> result(size / sizeof(T));
status = AudioObjectGetPropertyData(inObjectID, &inAddress,
0, nullptr,
&size, result.begin());
if (status != noErr)
Apple::ThrowOSStatus(status);
return result;
}
#endif

111
src/apple/AudioUnit.hxx Normal file
View File

@@ -0,0 +1,111 @@
/*
* Copyright 2020 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef APPLE_AUDIO_UNIT_HXX
#define APPLE_AUDIO_UNIT_HXX
#include "Throw.hxx"
#include <AudioUnit/AudioUnit.h>
template<typename T>
T
AudioUnitGetPropertyT(AudioUnit inUnit, AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement)
{
UInt32 size = sizeof(T);
T value;
OSStatus status = AudioUnitGetProperty(inUnit, inID, inScope,
inElement,
&value, &size);
if (status != noErr)
Apple::ThrowOSStatus(status);
return value;
}
template<typename T>
void
AudioUnitSetPropertyT(AudioUnit inUnit, AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
const T &value)
{
OSStatus status = AudioUnitSetProperty(inUnit, inID, inScope,
inElement,
&value, sizeof(value));
if (status != noErr)
Apple::ThrowOSStatus(status);
}
inline void
AudioUnitSetCurrentDevice(AudioUnit inUnit, const AudioDeviceID &value)
{
AudioUnitSetPropertyT(inUnit, kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global, 0,
value);
}
inline void
AudioUnitSetInputStreamFormat(AudioUnit inUnit,
const AudioStreamBasicDescription &value)
{
AudioUnitSetPropertyT(inUnit, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0,
value);
}
inline void
AudioUnitSetInputRenderCallback(AudioUnit inUnit,
const AURenderCallbackStruct &value)
{
AudioUnitSetPropertyT(inUnit, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0,
value);
}
inline UInt32
AudioUnitGetBufferFrameSize(AudioUnit inUnit)
{
return AudioUnitGetPropertyT<UInt32>(inUnit,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Global, 0);
}
inline void
AudioUnitSetBufferFrameSize(AudioUnit inUnit, const UInt32 &value)
{
AudioUnitSetPropertyT(inUnit, kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Global, 0,
value);
}
#endif

75
src/apple/ErrorRef.hxx Normal file
View File

@@ -0,0 +1,75 @@
/*
* Copyright 2020 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef APPLE_ERROR_REF_HXX
#define APPLE_ERROR_REF_HXX
#include <CoreFoundation/CFError.h>
#include <utility>
namespace Apple {
class ErrorRef {
CFErrorRef ref = nullptr;
public:
explicit ErrorRef(CFErrorRef _ref) noexcept
:ref(_ref) {}
ErrorRef(CFAllocatorRef allocator, CFErrorDomain domain,
CFIndex code, CFDictionaryRef userInfo) noexcept
:ref(CFErrorCreate(allocator, domain, code, userInfo)) {}
ErrorRef(ErrorRef &&src) noexcept
:ref(std::exchange(src.ref, nullptr)) {}
~ErrorRef() noexcept {
if (ref)
CFRelease(ref);
}
ErrorRef &operator=(ErrorRef &&src) noexcept {
using std::swap;
swap(ref, src.ref);
return *this;
}
operator bool() const noexcept {
return ref != nullptr;
}
CFStringRef CopyDescription() const noexcept {
return CFErrorCopyDescription(ref);
}
};
} // namespace Apple
#endif

73
src/apple/StringRef.hxx Normal file
View File

@@ -0,0 +1,73 @@
/*
* Copyright 2020 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef APPLE_STRING_REF_HXX
#define APPLE_STRING_REF_HXX
#include <CoreFoundation/CFString.h>
#include <utility>
namespace Apple {
class StringRef {
CFStringRef ref = nullptr;
public:
explicit StringRef(CFStringRef _ref) noexcept
:ref(_ref) {}
StringRef(StringRef &&src) noexcept
:ref(std::exchange(src.ref, nullptr)) {}
~StringRef() noexcept {
if (ref)
CFRelease(ref);
}
StringRef &operator=(StringRef &&src) noexcept {
using std::swap;
swap(ref, src.ref);
return *this;
}
operator bool() const noexcept {
return ref != nullptr;
}
bool GetCString(char *buffer, std::size_t size,
CFStringEncoding encoding=kCFStringEncodingUTF8) const noexcept
{
return CFStringGetCString(ref, buffer, size, encoding);
}
};
} // namespace Apple
#endif

67
src/apple/Throw.cxx Normal file
View File

@@ -0,0 +1,67 @@
/*
* Copyright 2020 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "Throw.hxx"
#include "ErrorRef.hxx"
#include "StringRef.hxx"
#include <stdexcept>
namespace Apple {
void
ThrowOSStatus(OSStatus status)
{
const Apple::ErrorRef cferr(nullptr, kCFErrorDomainOSStatus,
status, nullptr);
const Apple::StringRef cfstr(cferr.CopyDescription());
char msg[1024];
if (!cfstr.GetCString(msg, sizeof(msg)))
throw std::runtime_error("Unknown OSStatus");
throw std::runtime_error(msg);
}
void
ThrowOSStatus(OSStatus status, const char *_msg)
{
const Apple::ErrorRef cferr(nullptr, kCFErrorDomainOSStatus,
status, nullptr);
const Apple::StringRef cfstr(cferr.CopyDescription());
char msg[1024];
strcpy(msg, _msg);
size_t length = strlen(msg);
cfstr.GetCString(msg + length, sizeof(msg) - length);
throw std::runtime_error(msg);
}
} // namespace Apple

45
src/apple/Throw.hxx Normal file
View File

@@ -0,0 +1,45 @@
/*
* Copyright 2020 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef APPLE_THROW_HXX
#define APPLE_THROW_HXX
#include <CoreFoundation/CFBase.h>
namespace Apple {
void
ThrowOSStatus(OSStatus status);
void
ThrowOSStatus(OSStatus status, const char *msg);
} // namespace Apple
#endif

28
src/apple/meson.build Normal file
View File

@@ -0,0 +1,28 @@
if not is_darwin
apple_dep = dependency('', required: false)
subdir_done()
endif
audiounit_dep = declare_dependency(
link_args: ['-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreServices'],
dependencies: [
boost_dep,
],
)
apple = static_library(
'apple',
'AudioObject.cxx',
'Throw.cxx',
include_directories: inc,
dependencies: [
audiounit_dep,
],
)
apple_dep = declare_dependency(
link_with: apple,
dependencies: [
audiounit_dep,
],
)

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

@@ -198,6 +198,16 @@ handle_mount(Client &client, Request args, Response &r)
return CommandResult::ERROR;
}
if (composite.IsMountPoint(local_uri)) {
r.Error(ACK_ERROR_ARG, "Mount point busy");
return CommandResult::ERROR;
}
if (composite.IsMounted(remote_uri)) {
r.Error(ACK_ERROR_ARG, "This storage is already mounted");
return CommandResult::ERROR;
}
auto &event_loop = instance.io_thread.GetEventLoop();
auto storage = CreateStorageURI(event_loop, remote_uri);
if (storage == nullptr) {
@@ -210,8 +220,10 @@ handle_mount(Client &client, Request args, Response &r)
#ifdef ENABLE_DATABASE
if (auto *db = dynamic_cast<SimpleDatabase *>(instance.GetDatabase())) {
bool need_update;
try {
db->Mount(local_uri, remote_uri);
need_update = !db->Mount(local_uri, remote_uri);
} catch (...) {
composite.Unmount(local_uri);
throw;
@@ -220,6 +232,12 @@ handle_mount(Client &client, Request args, Response &r)
// TODO: call Instance::OnDatabaseModified()?
// TODO: trigger database update?
instance.EmitIdle(IDLE_DATABASE);
if (need_update) {
UpdateService *update = client.GetInstance().update;
if (update != nullptr)
update->Enqueue(local_uri, false);
}
}
#endif

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

@@ -428,7 +428,7 @@ IsUnsafeChar(char ch)
return !IsSafeChar(ch);
}
void
bool
SimpleDatabase::Mount(const char *local_uri, const char *storage_uri)
{
if (cache_path.IsNull())
@@ -447,14 +447,11 @@ SimpleDatabase::Mount(const char *local_uri, const char *storage_uri)
compress);
db->Open();
// TODO: update the new database instance?
bool exists = db->FileExists();
try {
Mount(local_uri, std::move(db));
} catch (...) {
db->Close();
throw;
}
Mount(local_uri, std::move(db));
return exists;
}
inline DatabasePtr

View File

@@ -103,9 +103,11 @@ public:
/**
* Throws #std::runtime_error on error.
*
* @return false if the mounted database needs to be updated
*/
gcc_nonnull_all
void Mount(const char *local_uri, const char *storage_uri);
bool Mount(const char *local_uri, const char *storage_uri);
gcc_nonnull_all
bool Unmount(const char *uri) noexcept;

View File

@@ -89,9 +89,18 @@ public:
tag.Clear();
}
gcc_pure
bool IsRoot() const noexcept {
return type == Type::CONTAINER && id == "0";
}
gcc_pure
bool Check() const noexcept {
return !id.empty() && !parent_id.empty() && !name.empty() &&
return !id.empty() &&
/* root nodes don't need a parent id and a
name */
(IsRoot() || (!parent_id.empty() &&
!name.empty())) &&
(type != UPnPDirObject::Type::ITEM ||
item_class != UPnPDirObject::ItemClass::UNKNOWN);
}

View File

@@ -341,8 +341,8 @@ UpdateWalk::UpdateDirectory(Directory &directory,
try {
Mutex mutex;
auto is = InputStream::OpenReady(PathTraitsUTF8::Build(storage.MapUTF8(directory.GetPath()).c_str(),
".mpdignore").c_str(),
auto is = InputStream::OpenReady(storage.MapUTF8(PathTraitsUTF8::Build(directory.GetPath(),
".mpdignore").c_str()).c_str(),
mutex);
child_exclude_list.Load(std::move(is));
} catch (...) {

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;
@@ -67,18 +67,22 @@ struct DecoderPlugin {
void (*file_decode)(DecoderClient &client, Path path_fs);
/**
* Scan metadata of a file.
* Scan metadata of a file.
*
* Throws on I/O error.
*
* @return false if the operation has failed
* @return false if the file was not recognized
*/
bool (*scan_file)(Path path_fs, TagHandler &handler) noexcept;
bool (*scan_file)(Path path_fs, TagHandler &handler);
/**
* Scan metadata of a file.
* Scan metadata of a stream.
*
* Throws on I/O error.
*
* @return false if the operation has failed
* @return false if the stream was not recognized
*/
bool (*scan_stream)(InputStream &is, TagHandler &handler) noexcept;
bool (*scan_stream)(InputStream &is, TagHandler &handler);
/**
* @brief Return a "virtual" filename for subtracks in
@@ -135,7 +139,7 @@ struct DecoderPlugin {
* Read the tag of a file.
*/
template<typename P>
bool ScanFile(P path_fs, TagHandler &handler) const noexcept {
bool ScanFile(P path_fs, TagHandler &handler) const {
return scan_file != nullptr
? scan_file(path_fs, handler)
: false;
@@ -144,7 +148,7 @@ struct DecoderPlugin {
/**
* Read the tag of a stream.
*/
bool ScanStream(InputStream &is, TagHandler &handler) const noexcept {
bool ScanStream(InputStream &is, TagHandler &handler) const {
return scan_stream != nullptr
? scan_stream(is, handler)
: false;

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

@@ -241,7 +241,7 @@ audiofile_stream_decode(DecoderClient &client, InputStream &is)
}
static bool
audiofile_scan_stream(InputStream &is, TagHandler &handler) noexcept
audiofile_scan_stream(InputStream &is, TagHandler &handler)
{
if (!is.IsSeekable() || !is.KnownSize())
return false;

View File

@@ -20,7 +20,7 @@
#ifndef MPD_DECODER_DSDLIB_HXX
#define MPD_DECODER_DSDLIB_HXX
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "input/Offset.hxx"
#include "util/Compiler.h"

View File

@@ -32,7 +32,7 @@
#include "input/InputStream.hxx"
#include "CheckAudioFormat.hxx"
#include "util/bit_reverse.h"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "tag/Handler.hxx"
#include "DsdLib.hxx"
#include "Log.hxx"
@@ -449,7 +449,7 @@ dsdiff_stream_decode(DecoderClient &client, InputStream &is)
}
static bool
dsdiff_scan_stream(InputStream &is, TagHandler &handler) noexcept
dsdiff_scan_stream(InputStream &is, TagHandler &handler)
{
DsdiffMetaData metadata;
DsdiffChunkHeader chunk_header;

View File

@@ -33,7 +33,7 @@
#include "input/InputStream.hxx"
#include "CheckAudioFormat.hxx"
#include "util/bit_reverse.h"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "DsdLib.hxx"
#include "tag/Handler.hxx"
#include "Log.hxx"
@@ -326,7 +326,7 @@ dsf_stream_decode(DecoderClient &client, InputStream &is)
}
static bool
dsf_scan_stream(InputStream &is, TagHandler &handler) noexcept
dsf_scan_stream(InputStream &is, TagHandler &handler)
{
/* check DSF metadata */
DsfMetaData metadata;

View File

@@ -414,7 +414,7 @@ faad_stream_decode(DecoderClient &client, InputStream &is)
}
static bool
faad_scan_stream(InputStream &is, TagHandler &handler) noexcept
faad_scan_stream(InputStream &is, TagHandler &handler)
{
auto result = faad_get_file_time(is);
if (!result.first)

View File

@@ -646,8 +646,7 @@ ffmpeg_decode(DecoderClient &client, InputStream &input)
}
static bool
FfmpegScanStream(AVFormatContext &format_context,
TagHandler &handler) noexcept
FfmpegScanStream(AVFormatContext &format_context, TagHandler &handler)
{
const int find_result =
avformat_find_stream_info(&format_context, nullptr);
@@ -680,7 +679,7 @@ FfmpegScanStream(AVFormatContext &format_context,
}
static bool
ffmpeg_scan_stream(InputStream &is, TagHandler &handler) noexcept
ffmpeg_scan_stream(InputStream &is, TagHandler &handler)
{
AvioStream stream(nullptr, is);
if (!stream.Open())

View File

@@ -69,7 +69,7 @@ flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame,
}
static bool
flac_scan_file(Path path_fs, TagHandler &handler) noexcept
flac_scan_file(Path path_fs, TagHandler &handler)
{
FlacMetadataChain chain;
if (!chain.Read(NarrowPath(path_fs))) {
@@ -84,7 +84,7 @@ flac_scan_file(Path path_fs, TagHandler &handler) noexcept
}
static bool
flac_scan_stream(InputStream &is, TagHandler &handler) noexcept
flac_scan_stream(InputStream &is, TagHandler &handler)
{
FlacMetadataChain chain;
if (!chain.Read(is)) {
@@ -313,7 +313,7 @@ oggflac_init(gcc_unused const ConfigBlock &block)
}
static bool
oggflac_scan_file(Path path_fs, TagHandler &handler) noexcept
oggflac_scan_file(Path path_fs, TagHandler &handler)
{
FlacMetadataChain chain;
if (!chain.ReadOgg(NarrowPath(path_fs))) {
@@ -328,7 +328,7 @@ oggflac_scan_file(Path path_fs, TagHandler &handler) noexcept
}
static bool
oggflac_scan_stream(InputStream &is, TagHandler &handler) noexcept
oggflac_scan_stream(InputStream &is, TagHandler &handler)
{
FlacMetadataChain chain;
if (!chain.ReadOgg(is)) {

View File

@@ -70,8 +70,8 @@ fluidsynth_mpd_log_function(int level,
char *message,
void *)
{
Log(fluidsynth_domain,
fluidsynth_level_to_mpd(fluid_log_level(level)),
Log(fluidsynth_level_to_mpd(fluid_log_level(level)),
fluidsynth_domain,
message);
}

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

@@ -20,7 +20,7 @@
#include "HybridDsdDecoderPlugin.hxx"
#include "../DecoderAPI.hxx"
#include "input/InputStream.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "util/Domain.hxx"
#include "util/WritableBuffer.hxx"
#include "util/StaticFifoBuffer.hxx"

View File

@@ -1051,7 +1051,7 @@ MadDecoder::RunScan(TagHandler &handler) noexcept
}
static bool
mad_decoder_scan_stream(InputStream &is, TagHandler &handler) noexcept
mad_decoder_scan_stream(InputStream &is, TagHandler &handler)
{
MadDecoder data(nullptr, is);
return data.RunScan(handler);

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

@@ -275,7 +275,7 @@ mpcdec_get_file_duration(InputStream &is)
}
static bool
mpcdec_scan_stream(InputStream &is, TagHandler &handler) noexcept
mpcdec_scan_stream(InputStream &is, TagHandler &handler)
{
const auto duration = mpcdec_get_file_duration(is);
if (duration.IsNegative())

View File

@@ -75,6 +75,19 @@ class MPDOpusDecoder final : public OggDecoder {
OpusDecoder *opus_decoder = nullptr;
opus_int16 *output_buffer = nullptr;
/**
* The pre-skip value from the Opus header. Initialized by
* OnOggBeginning().
*/
unsigned pre_skip;
/**
* The number of decoded samples which shall be skipped. At
* the beginning of the file, this gets set to #pre_skip (by
* OnOggBeginning()), and may also be set while seeking.
*/
unsigned skip;
/**
* If non-zero, then a previous Opus stream has been found
* already with this number of channels. If opus_decoder is
@@ -85,6 +98,13 @@ class MPDOpusDecoder final : public OggDecoder {
size_t frame_size;
/**
* The granulepos of the next sample to be submitted to
* DecoderClient::SubmitData(). Negative if unkown.
* Initialized by OnOggBeginning().
*/
ogg_int64_t granulepos;
public:
explicit MPDOpusDecoder(DecoderReader &reader)
:OggDecoder(reader) {}
@@ -101,6 +121,13 @@ public:
bool Seek(uint64_t where_frame);
private:
void AddGranulepos(ogg_int64_t n) noexcept {
assert(n >= 0);
if (granulepos >= 0)
granulepos += n;
}
void HandleTags(const ogg_packet &packet);
void HandleAudio(const ogg_packet &packet);
@@ -137,10 +164,13 @@ MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet)
throw std::runtime_error("BOS packet must be OpusHead");
unsigned channels;
if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
if (!ScanOpusHeader(packet.packet, packet.bytes, channels, pre_skip) ||
!audio_valid_channel_count(channels))
throw std::runtime_error("Malformed BOS packet");
granulepos = 0;
skip = pre_skip;
assert(opus_decoder == nullptr);
assert(IsInitialized() == (output_buffer != nullptr));
@@ -177,8 +207,12 @@ MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet)
client.Ready(audio_format, eos_granulepos > 0, duration);
frame_size = audio_format.GetFrameSize();
output_buffer = new opus_int16[opus_output_buffer_frames
* audio_format.channels];
if (output_buffer == nullptr)
/* note: if we ever support changing the channel count
in chained streams, we need to reallocate this
buffer instead of keeping it */
output_buffer = new opus_int16[opus_output_buffer_frames
* audio_format.channels];
auto cmd = client.GetCommand();
if (cmd != DecoderCommand::NONE)
@@ -231,22 +265,58 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
packet.bytes,
output_buffer, opus_output_buffer_frames,
0);
if (nframes < 0)
throw FormatRuntimeError("libopus error: %s",
opus_strerror(nframes));
if (nframes > 0) {
const size_t nbytes = nframes * frame_size;
auto cmd = client.SubmitData(input_stream,
output_buffer, nbytes,
0);
if (cmd != DecoderCommand::NONE)
throw cmd;
if (packet.granulepos > 0)
client.SubmitTimestamp(FloatDuration(packet.granulepos)
/ opus_sample_rate);
if (gcc_unlikely(nframes <= 0)) {
if (nframes < 0)
throw FormatRuntimeError("libopus error: %s",
opus_strerror(nframes));
else
return;
}
/* apply the "skip" value */
if (skip >= (unsigned)nframes) {
skip -= nframes;
AddGranulepos(nframes);
return;
}
const opus_int16 *data = output_buffer;
data += skip * previous_channels;
nframes -= skip;
AddGranulepos(skip);
skip = 0;
if (packet.e_o_s && packet.granulepos > 0 && granulepos >= 0) {
/* End Trimming (RFC7845 4.4): "The page with the 'end
of stream' flag set MAY have a granule position
that indicates the page contains less audio data
than would normally be returned by decoding up
through the final packet. This is used to end the
stream somewhere other than an even frame
boundary. [...] The remaining samples are
discarded. */
ogg_int64_t remaining = packet.granulepos - granulepos;
if (remaining <= 0)
return;
if (remaining < nframes)
nframes = remaining;
}
/* submit decoded samples to the DecoderClient */
const size_t nbytes = nframes * frame_size;
auto cmd = client.SubmitData(input_stream,
data, nbytes,
0);
if (cmd != DecoderCommand::NONE)
throw cmd;
if (packet.granulepos > 0) {
granulepos = packet.granulepos;
client.SubmitTimestamp(FloatDuration(granulepos - pre_skip)
/ opus_sample_rate);
} else
AddGranulepos(nframes);
}
bool
@@ -258,8 +328,20 @@ MPDOpusDecoder::Seek(uint64_t where_frame)
const ogg_int64_t where_granulepos(where_frame);
/* we don't know the exact granulepos after seeking, so let's
set it to -1 - it will be set after the next packet which
declares its granulepos */
granulepos = -1;
try {
SeekGranulePos(where_granulepos);
/* since all frame numbers are offset by the file's
pre-skip value, we need to apply it here as well;
we could just seek to "where_frame+pre_skip" as
well, but I think by decoding those samples and
discard them, we're safer */
skip = pre_skip;
return true;
} catch (...) {
return false;
@@ -302,13 +384,14 @@ mpd_opus_stream_decode(DecoderClient &client,
static bool
ReadAndParseOpusHead(OggSyncState &sync, OggStreamState &stream,
unsigned &channels)
unsigned &channels, unsigned &pre_skip)
{
ogg_packet packet;
return OggReadPacket(sync, stream, packet) && packet.b_o_s &&
IsOpusHead(packet) &&
ScanOpusHeader(packet.packet, packet.bytes, channels) &&
ScanOpusHeader(packet.packet, packet.bytes, channels,
pre_skip) &&
audio_valid_channel_count(channels);
}
@@ -327,11 +410,12 @@ ReadAndVisitOpusTags(OggSyncState &sync, OggStreamState &stream,
static void
VisitOpusDuration(InputStream &is, OggSyncState &sync, OggStreamState &stream,
TagHandler &handler)
ogg_int64_t pre_skip, TagHandler &handler)
{
ogg_packet packet;
if (OggSeekFindEOS(sync, stream, packet, is)) {
if (OggSeekFindEOS(sync, stream, packet, is) &&
packet.granulepos >= pre_skip) {
const auto duration =
SongTime::FromScale<uint64_t>(packet.granulepos,
opus_sample_rate);
@@ -340,7 +424,7 @@ VisitOpusDuration(InputStream &is, OggSyncState &sync, OggStreamState &stream,
}
static bool
mpd_opus_scan_stream(InputStream &is, TagHandler &handler) noexcept
mpd_opus_scan_stream(InputStream &is, TagHandler &handler)
{
InputStreamReader reader(is);
OggSyncState oy(reader);
@@ -351,15 +435,15 @@ mpd_opus_scan_stream(InputStream &is, TagHandler &handler) noexcept
OggStreamState os(first_page);
unsigned channels;
if (!ReadAndParseOpusHead(oy, os, channels) ||
unsigned channels, pre_skip;
if (!ReadAndParseOpusHead(oy, os, channels, pre_skip) ||
!ReadAndVisitOpusTags(oy, os, handler))
return false;
handler.OnAudioFormat(AudioFormat(opus_sample_rate,
SampleFormat::S16, channels));
VisitOpusDuration(is, oy, os, handler);
VisitOpusDuration(is, oy, os, pre_skip, handler);
return true;
}

View File

@@ -18,6 +18,7 @@
*/
#include "OpusHead.hxx"
#include "util/ByteOrder.hxx"
#include <stdint.h>
@@ -31,12 +32,14 @@ struct OpusHead {
};
bool
ScanOpusHeader(const void *data, size_t size, unsigned &channels_r)
ScanOpusHeader(const void *data, size_t size, unsigned &channels_r,
unsigned &pre_skip_r)
{
const OpusHead *h = (const OpusHead *)data;
if (size < 19 || (h->version & 0xf0) != 0)
return false;
channels_r = h->channels;
pre_skip_r = FromLE16(h->pre_skip);
return true;
}

View File

@@ -23,6 +23,7 @@
#include <stddef.h>
bool
ScanOpusHeader(const void *data, size_t size, unsigned &channels_r);
ScanOpusHeader(const void *data, size_t size, unsigned &channels_r,
unsigned &pre_skip_r);
#endif

View File

@@ -22,7 +22,7 @@
#include "CheckAudioFormat.hxx"
#include "pcm/PcmPack.hxx"
#include "input/InputStream.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "util/Domain.hxx"
#include "util/ByteReverse.hxx"
#include "util/StaticFifoBuffer.hxx"

View File

@@ -35,7 +35,7 @@
#include "util/Domain.hxx"
#include "util/AllocatedString.hxx"
#include "util/CharUtil.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "Log.hxx"
#ifdef HAVE_SIDPLAYFP

View File

@@ -267,7 +267,7 @@ static constexpr struct {
};
static bool
sndfile_scan_stream(InputStream &is, TagHandler &handler) noexcept
sndfile_scan_stream(InputStream &is, TagHandler &handler)
{
SF_INFO info;

View File

@@ -370,7 +370,7 @@ VisitVorbisDuration(InputStream &is,
}
static bool
vorbis_scan_stream(InputStream &is, TagHandler &handler) noexcept
vorbis_scan_stream(InputStream &is, TagHandler &handler)
{
/* initialize libogg */

View File

@@ -614,7 +614,7 @@ wavpack_scan_file(Path path_fs, TagHandler &handler) noexcept
}
static bool
wavpack_scan_stream(InputStream &is, TagHandler &handler) noexcept
wavpack_scan_stream(InputStream &is, TagHandler &handler)
{
WavpackInput isp(nullptr, is);

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

@@ -22,7 +22,7 @@
#include "AudioFormat.hxx"
#include "config/Domain.hxx"
#include "util/Alloc.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "util/StringUtil.hxx"
#include <opus.h>

View File

@@ -19,7 +19,7 @@
#include "WaveEncoderPlugin.hxx"
#include "../EncoderAPI.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "util/DynamicFifoBuffer.hxx"
#include <assert.h>

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

@@ -58,6 +58,11 @@ input_stream_global_init(const ConfigData &config, EventLoop &event_loop)
if (plugin->init != nullptr)
plugin->init(event_loop, *block);
input_plugins_enabled[i] = true;
} catch (const PluginUnconfigured &e) {
LogFormat(LogLevel::INFO, e,
"Input plugin '%s' is not configured",
plugin->name);
continue;
} catch (const PluginUnavailable &e) {
FormatError(e,
"Input plugin '%s' is unavailable",

View File

@@ -30,7 +30,7 @@
#include "util/StringCompare.hxx"
#include "util/RuntimeError.hxx"
#include "util/Domain.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "fs/AllocatedPath.hxx"
#include "Log.hxx"
#include "config/Block.hxx"

View File

@@ -26,6 +26,8 @@
#include "system/FileDescriptor.hxx"
#include "util/RuntimeError.hxx"
#include <cinttypes> // for PRIu64 (PRIoffset)
#include <sys/stat.h>
#include <fcntl.h>
@@ -94,6 +96,11 @@ FileInputStream::Read(void *ptr, size_t read_size)
nbytes = reader.Read(ptr, read_size);
}
if (nbytes == 0 && !IsEOF())
throw FormatRuntimeError("Unexpected end of file %s"
" at %" PRIoffset " of %" PRIoffset,
GetURI(), GetOffset(), GetSize());
offset += nbytes;
return nbytes;
}

View File

@@ -133,11 +133,11 @@ InitQobuzInput(EventLoop &event_loop, const ConfigBlock &block)
const char *app_id = block.GetBlockValue("app_id");
if (app_id == nullptr)
throw PluginUnavailable("No Qobuz app_id configured");
throw PluginUnconfigured("No Qobuz app_id configured");
const char *app_secret = block.GetBlockValue("app_secret");
if (app_secret == nullptr)
throw PluginUnavailable("No Qobuz app_secret configured");
throw PluginUnconfigured("No Qobuz app_secret configured");
const char *device_manufacturer_id = block.GetBlockValue("device_manufacturer_id",
"df691fdc-fa36-11e7-9718-635337d7df8f");
@@ -145,11 +145,11 @@ InitQobuzInput(EventLoop &event_loop, const ConfigBlock &block)
const char *username = block.GetBlockValue("username");
const char *email = block.GetBlockValue("email");
if (username == nullptr && email == nullptr)
throw PluginUnavailable("No Qobuz username configured");
throw PluginUnconfigured("No Qobuz username configured");
const char *password = block.GetBlockValue("password");
if (password == nullptr)
throw PluginUnavailable("No Qobuz password configured");
throw PluginUnconfigured("No Qobuz password configured");
const char *format_id = block.GetBlockValue("format_id", "5");

View File

@@ -170,15 +170,15 @@ InitTidalInput(EventLoop &event_loop, const ConfigBlock &block)
const char *token = block.GetBlockValue("token");
if (token == nullptr)
throw PluginUnavailable("No Tidal application token configured");
throw PluginUnconfigured("No Tidal application token configured");
const char *username = block.GetBlockValue("username");
if (username == nullptr)
throw PluginUnavailable("No Tidal username configured");
throw PluginUnconfigured("No Tidal username configured");
const char *password = block.GetBlockValue("password");
if (password == nullptr)
throw PluginUnavailable("No Tidal password configured");
throw PluginUnconfigured("No Tidal password configured");
FormatWarning(tidal_domain, "The Tidal input plugin is deprecated because Tidal has changed the protocol and doesn't share documentation");

View File

@@ -19,7 +19,7 @@
#include "HwSetup.hxx"
#include "Format.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "util/Domain.hxx"
#include "util/RuntimeError.hxx"
#include "AudioFormat.hxx"

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

@@ -62,6 +62,6 @@ FfmpegLogCallback(gcc_unused void *ptr, int level, const char *fmt, va_list vl)
ffmpeg_domain.GetName(),
cls->item_name(ptr));
const Domain d(domain);
LogFormatV(d, FfmpegImportLogLevel(level), fmt, vl);
LogFormatV(FfmpegImportLogLevel(level), d, fmt, vl);
}
}

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

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

@@ -47,6 +47,11 @@ ToNeighborInfo(const UDisks2::Object &o) noexcept
return {o.GetUri(), o.path};
}
static constexpr char udisks_neighbor_match[] =
"type='signal',sender='" UDISKS2_INTERFACE "',"
"interface='" DBUS_OM_INTERFACE "',"
"path='" UDISKS2_PATH "'";
class UdisksNeighborExplorer final
: public NeighborExplorer {
@@ -108,26 +113,37 @@ UdisksNeighborExplorer::DoOpen()
auto &connection = GetConnection();
/* this ugly try/catch cascade is only here because this
method has no RAII for this method - TODO: improve this */
try {
Error error;
dbus_bus_add_match(connection,
"type='signal',sender='" UDISKS2_INTERFACE "',"
"interface='" DBUS_OM_INTERFACE "',"
"path='" UDISKS2_PATH "'",
error);
dbus_bus_add_match(connection, udisks_neighbor_match, error);
error.CheckThrow("DBus AddMatch error");
dbus_connection_add_filter(connection,
HandleMessage, this,
nullptr);
try {
dbus_connection_add_filter(connection,
HandleMessage, this,
nullptr);
auto msg = Message::NewMethodCall(UDISKS2_INTERFACE,
UDISKS2_PATH,
DBUS_OM_INTERFACE,
"GetManagedObjects");
list_request.Send(connection, *msg.Get(),
std::bind(&UdisksNeighborExplorer::OnListNotify,
this, std::placeholders::_1));
try {
auto msg = Message::NewMethodCall(UDISKS2_INTERFACE,
UDISKS2_PATH,
DBUS_OM_INTERFACE,
"GetManagedObjects");
list_request.Send(connection, *msg.Get(),
std::bind(&UdisksNeighborExplorer::OnListNotify,
this, std::placeholders::_1));
} catch (...) {
dbus_connection_remove_filter(connection,
HandleMessage,
this);
throw;
}
} catch (...) {
dbus_bus_remove_match(connection,
udisks_neighbor_match, nullptr);
throw;
}
} catch (...) {
dbus_glue.Destruct();
throw;
@@ -147,8 +163,10 @@ UdisksNeighborExplorer::DoClose() noexcept
list_request.Cancel();
}
// TODO: remove_match
// TODO: remove_filter
auto &connection = GetConnection();
dbus_connection_remove_filter(connection, HandleMessage, this);
dbus_bus_remove_match(connection, udisks_neighbor_match, nullptr);
dbus_glue.Destruct();
}

View File

@@ -31,7 +31,7 @@
#define IPV4_ADDRESS_HXX
#include "SocketAddress.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include <stdint.h>

View File

@@ -31,7 +31,7 @@
#define IPV6_ADDRESS_HXX
#include "SocketAddress.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "util/Compiler.h"
#include <stdint.h>

View File

@@ -39,6 +39,7 @@
#include "config/Option.hxx"
#include "config/Block.hxx"
#include "util/RuntimeError.hxx"
#include "util/StringAPI.hxx"
#include "util/StringFormat.hxx"
#include "Log.hxx"
@@ -214,7 +215,7 @@ FilteredAudioOutput::Setup(EventLoop &event_loop,
const char *replay_gain_handler =
block.GetBlockValue("replay_gain_handler", "software");
if (strcmp(replay_gain_handler, "none") != 0) {
if (!StringIsEqual(replay_gain_handler, "none")) {
prepared_replay_gain_filter =
NewReplayGainFilter(replay_gain_config);
assert(prepared_replay_gain_filter != nullptr);
@@ -240,14 +241,14 @@ FilteredAudioOutput::Setup(EventLoop &event_loop,
/* use the hardware mixer for replay gain? */
if (strcmp(replay_gain_handler, "mixer") == 0) {
if (StringIsEqual(replay_gain_handler, "mixer")) {
if (mixer != nullptr)
replay_gain_filter_set_mixer(*prepared_replay_gain_filter,
mixer, 100);
else
FormatError(output_domain,
"No such mixer for output '%s'", name);
} else if (strcmp(replay_gain_handler, "software") != 0 &&
} else if (!StringIsEqual(replay_gain_handler, "software") &&
prepared_replay_gain_filter != nullptr) {
throw std::runtime_error("Invalid \"replay_gain_handler\" value");
}

View File

@@ -28,6 +28,7 @@
#include "config/Data.hxx"
#include "config/Option.hxx"
#include "util/RuntimeError.hxx"
#include "util/StringAPI.hxx"
#include <stdexcept>
@@ -147,7 +148,7 @@ AudioOutputControl *
MultipleOutputs::FindByName(const char *name) noexcept
{
for (auto *i : outputs)
if (strcmp(i->GetName(), name) == 0)
if (StringIsEqual(i->GetName(), name))
return i;
return nullptr;

View File

@@ -38,8 +38,7 @@
#include "plugins/sles/SlesOutputPlugin.hxx"
#include "plugins/SolarisOutputPlugin.hxx"
#include "plugins/WinmmOutputPlugin.hxx"
#include <string.h>
#include "util/StringAPI.hxx"
const AudioOutputPlugin *const audio_output_plugins[] = {
#ifdef HAVE_SHOUT
@@ -101,7 +100,7 @@ const AudioOutputPlugin *
AudioOutputPlugin_get(const char *name)
{
audio_output_plugins_for_each(plugin)
if (strcmp(plugin->name, name) == 0)
if (StringIsEqual(plugin->name, name))
return plugin;
return nullptr;

View File

@@ -939,7 +939,7 @@ AlsaOutput::Play(const void *chunk, size_t size)
assert(size % in_frame_size == 0);
const auto e = pcm_export->Export({chunk, size});
if (e.size == 0)
if (e.empty())
/* the DoP (DSD over PCM) filter converts two frames
at a time and ignores the last odd frame; if there
was only one frame (e.g. the last frame in the

View File

@@ -25,12 +25,11 @@
#include "util/SplitString.hxx"
#include "util/RuntimeError.hxx"
#include "util/Domain.hxx"
#include "util/StringAPI.hxx"
#include "Log.hxx"
#include <ao/ao.h>
#include <string.h>
/* An ao_sample_format, with all fields set to zero: */
static ao_sample_format OUR_AO_FORMAT_INITIALIZER;
@@ -105,7 +104,7 @@ AoOutput::AoOutput(const ConfigBlock &block)
write_size(block.GetPositiveValue("write_size", 1024U))
{
const char *value = block.GetBlockValue("driver", "default");
if (0 == strcmp(value, "default"))
if (StringIsEqual(value, "default"))
driver = ao_default_driver_id();
else
driver = ao_driver_id(value);

File diff suppressed because it is too large Load Diff

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

@@ -25,7 +25,7 @@
#include "util/ConstBuffer.hxx"
#include "util/Domain.hxx"
#include "util/Macros.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "Log.hxx"
#include <stdexcept>

View File

@@ -35,7 +35,6 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static constexpr unsigned DEFAULT_CONN_TIMEOUT = 2;
@@ -123,15 +122,15 @@ ShoutOutput::ShoutOutput(const ConfigBlock &block)
unsigned protocol;
const char *value = block.GetBlockValue("protocol");
if (value != nullptr) {
if (0 == strcmp(value, "shoutcast") &&
if (StringIsEqual(value, "shoutcast") &&
!StringIsEqual(mime_type, "audio/mpeg"))
throw FormatRuntimeError("you cannot stream \"%s\" to shoutcast, use mp3",
mime_type);
else if (0 == strcmp(value, "shoutcast"))
else if (StringIsEqual(value, "shoutcast"))
protocol = SHOUT_PROTOCOL_ICY;
else if (0 == strcmp(value, "icecast1"))
else if (StringIsEqual(value, "icecast1"))
protocol = SHOUT_PROTOCOL_XAUDIOCAST;
else if (0 == strcmp(value, "icecast2"))
else if (StringIsEqual(value, "icecast2"))
protocol = SHOUT_PROTOCOL_HTTP;
else
throw FormatRuntimeError("shout protocol \"%s\" is not \"shoutcast\" or "
@@ -145,15 +144,15 @@ ShoutOutput::ShoutOutput(const ConfigBlock &block)
unsigned tls;
value = block.GetBlockValue("tls");
if (value != nullptr) {
if (0 == strcmp(value, "disabled"))
if (StringIsEqual(value, "disabled"))
tls = SHOUT_TLS_DISABLED;
else if(0 == strcmp(value, "auto"))
else if (StringIsEqual(value, "auto"))
tls = SHOUT_TLS_AUTO;
else if(0 == strcmp(value, "auto_no_plain"))
else if (StringIsEqual(value, "auto_no_plain"))
tls = SHOUT_TLS_AUTO_NO_PLAIN;
else if(0 == strcmp(value, "rfc2818"))
else if (StringIsEqual(value, "rfc2818"))
tls = SHOUT_TLS_RFC2818;
else if(0 == strcmp(value, "rfc2817"))
else if (StringIsEqual(value, "rfc2817"))
tls = SHOUT_TLS_RFC2817;
else
throw FormatRuntimeError("invalid shout TLS option \"%s\"", value);

View File

@@ -72,17 +72,9 @@ if enable_oss
endif
if is_darwin
output_plugins_sources += 'OSXOutputPlugin.cxx'
audiounit_dep = declare_dependency(
link_args: [
'-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreServices',
],
dependencies: [
boost_dep,
],
)
else
audiounit_dep = dependency('', required: false)
output_plugins_sources += [
'OSXOutputPlugin.cxx',
]
endif
conf.set('HAVE_OSX', is_darwin)
@@ -145,7 +137,7 @@ output_plugins = static_library(
include_directories: inc,
dependencies: [
alsa_dep,
audiounit_dep,
apple_dep,
libao_dep,
libjack_dep,
pulse_dep,

View File

@@ -27,7 +27,7 @@
#include "thread/Cond.hxx"
#include "util/Macros.hxx"
#include "util/Domain.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
#include "mixer/MixerList.hxx"
#include "Log.hxx"

View File

@@ -18,7 +18,7 @@
*/
#include "PcmPack.hxx"
#include "system/ByteOrder.hxx"
#include "util/ByteOrder.hxx"
static void
pack_sample(uint8_t *dest, const int32_t *src0) noexcept

View File

@@ -62,7 +62,7 @@ mixramp_interpolate(const char *ramp_list, float required_db) noexcept
++ramp_list;
/* Check for exact match. */
if (db >= required_db) {
if (db == required_db) {
return duration;
}

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;
@@ -982,7 +985,7 @@ Player::Run() noexcept
const std::lock_guard<Mutex> lock(pc.mutex);
StartDecoder(pipe);
StartDecoder(pipe, true);
ActivateDecoder();
pc.state = PlayerState::PLAY;
@@ -1022,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

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