Compare commits

...

89 Commits

Author SHA1 Message Date
Max Kellermann
323231d1dd release v0.20.12 2017-11-25 19:32:37 +01:00
Max Kellermann
714011c81e lib/upnp: adapt to libupnp 1.8 API changes
Closes #155
2017-11-16 11:39:11 +01:00
Max Kellermann
952ff4207b lib/upnp/Callback: make "evp" parameter const 2017-11-16 11:37:58 +01:00
Max Kellermann
150b16ec2c lib/upnp/Discovery: make Upnp_Discovery pointers const 2017-11-16 11:37:04 +01:00
Max Kellermann
c98bc4a243 playlist/PlaylistRegistry: use LockRewind() instead of Rewind()
Fixes a deadlock caused by commit
31ab78ae8e.  That commit was not
actually bad - just these two calls have always been bad, which went
unnoticed for a long time.
2017-11-14 21:19:22 +01:00
Max Kellermann
014f8cd693 output/httpd: flush encoder after tag
Without the flush, ReadPage() may not return any data, or not all
data.  This may result in incomplete ddata the new "header" page,
corrupting streams with some encoders such as Vorbis.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

LAME currently doesn't support Android:

 checking host system type... Invalid configuration `arm-linux-androideabi': system `androideabi' not recognized
2017-07-19 20:53:52 +02:00
Max Kellermann
07b06d76be {android,win32}/build.py: concatenate variables from the command line 2017-07-19 20:53:52 +02:00
Max Kellermann
856fe2da15 python/build/libs: upgrade FFmpeg to 3.3.2, CURL to 7.54.1 2017-06-15 21:53:22 +02:00
Max Kellermann
f82aae65cd doc/user: add more Debian build dependencies 2017-06-15 21:37:07 +02:00
Max Kellermann
3fbd11a104 doc/user: update build dependencies for Debian Jessie 2017-06-15 21:37:05 +02:00
Max Kellermann
58a99f1907 increment version number to 0.20.10 2017-06-15 21:35:23 +02:00
Max Kellermann
cf86dfd317 release v0.20.9 2017-06-04 12:51:36 +02:00
Max Kellermann
a057b4f6d8 *: add lost of "noexcept" specifications 2017-06-04 12:46:48 +02:00
Max Kellermann
62b03cfddf storage, db, mixer, command: remove more bogus "pure" attributes
This commit is similar to 788e3b31e1,
and removes more "pure" attributes which were placed on functions that
could throw exceptions, which is illegal according to clang's
understanding of the attribute (but not according to GCC's).  GitHub
issue #58 was most likely about StorageDirectoryReader::GetInfo() and
Storage::GetInfo(), which still had "pure" attributes.

Closes #58
2017-06-03 21:54:24 +02:00
Jörg Raftopoulos
18b827b979 decoder/ffmpeg: add support for adx
Add ffmpeg decoder support for *.adx files (Sega game console)

Closes #60
2017-06-03 20:57:20 +02:00
Max Kellermann
0a379fc514 system/ByteOrder: fix byte order detection on FreeBSD/aarch64
Patch from
 https://svnweb.freebsd.org/ports/head/audio/musicpd/files/patch-src_system_ByteOrder.hxx?revision=441921&view=co
(with a tiny modification)

Closes #59
2017-05-29 20:37:14 +02:00
Max Kellermann
445c11b8d9 increment version number to 0.20.9 2017-05-29 20:36:08 +02:00
Max Kellermann
8d290ad509 release v0.20.8 2017-05-19 20:10:22 +02:00
Max Kellermann
b90c48b50f .travis.yml: enable Mac OS X 2017-05-19 19:54:27 +02:00
Max Kellermann
d19e7db09e .travis.yml: add shell variable OPTIONS 2017-05-19 19:54:27 +02:00
Max Kellermann
9939904b02 .travis.yml: configure with --disable-silent-rules --disable-dependency-tracking 2017-05-19 19:54:27 +02:00
Max Kellermann
ca23b15f5c test/test_byte_reverse: move "alignas" attribute to the front
Apparently, this makes old clang versions happy ("'alignas' attribute
cannot be applied to types).
2017-05-19 19:45:42 +02:00
Mario Di Raimondo
ffa676f577 playlist/m3u: support for mime-type audio-mpegurl for M3U playlists 2017-05-19 15:25:58 +02:00
Max Kellermann
6d023c4df3 .travis.yml: remove the unnecessary "compiler" setting 2017-05-17 16:22:19 +02:00
Max Kellermann
b31bd37a30 .travis.yml: check $TRAVIS_OS_NAME
Prepare for Mac OS X support by omitting those Ubuntu-specific
commands.
2017-05-17 15:59:25 +02:00
Max Kellermann
78faee8c7c .travis.yml: choose compiler with environment variables
.. and not update-alternatives, which requires "sudo".
2017-05-17 15:56:19 +02:00
Max Kellermann
40e2a703d0 .travis.yml: parallel build 2017-05-17 15:36:59 +02:00
Max Kellermann
b01edcb9bc .travis.yml: indent 2 2017-05-17 15:27:40 +02:00
André Klitzing
f7fffc9be8 Add initial travis configuration 2017-05-16 07:37:46 +02:00
Max Kellermann
50e8634097 python/build/libs: upgrade FFmpeg to 3.3.1 2017-05-16 07:27:08 +02:00
Max Kellermann
e3994e517e INSTALL: merge into doc/user.xml 2017-05-16 07:24:02 +02:00
Max Kellermann
2bb7785189 doc/user.xml: add sticker documentation 2017-05-16 07:23:57 +02:00
Max Kellermann
90c8408111 doc/user.xml: add Zeroconf documentation 2017-05-16 07:23:52 +02:00
Max Kellermann
64786ec12a Main: omit "constexpr" on MIN_BUFFER_SIZE with GCC 4.x 2017-05-16 07:20:47 +02:00
Max Kellermann
b3c82f8886 output/{osx,haiku,pulse,sles}: add missing "noexcept"
Fixes build failure on OS X, closes #44.  With the other plugins,
that's not critical, because those use the AudioOutputWrapper, which
hides this problem.
2017-05-16 07:12:30 +02:00
Max Kellermann
063259dc52 increment version number to 0.20.8 2017-05-16 07:06:57 +02:00
135 changed files with 1502 additions and 719 deletions

36
.travis.yml Normal file
View File

@@ -0,0 +1,36 @@
dist: trusty
language: cpp
addons:
apt:
packages:
- libcppunit-dev
- libboost-dev
os:
- linux
- osx
env:
global:
- MAKEFLAGS="-j2"
before_install:
# C++14
- test "$TRAVIS_OS_NAME" != "linux" || sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- test "$TRAVIS_OS_NAME" != "linux" || sudo apt-get update -qq
- test "$TRAVIS_OS_NAME" != "osx" || brew update
install:
# C++14
- test "$TRAVIS_OS_NAME" != "linux" || sudo apt-get install -qq g++-5
- test "$TRAVIS_OS_NAME" != "osx" || brew install cppunit
script:
- OPTIONS="--enable-test"
- test "$TRAVIS_OS_NAME" != "linux" || export CC=gcc-5 CXX=g++-5
- test "$TRAVIS_OS_NAME" != "osx" || OPTIONS="$OPTIONS --enable-osx"
- ./autogen.sh
- ./configure --disable-silent-rules --disable-dependency-tracking $OPTIONS
- make
- make check

View File

@@ -30,3 +30,4 @@ The following people have contributed code to MPD:
Jurgen Kramer <gtmkramer@xs4all.nl> Jurgen Kramer <gtmkramer@xs4all.nl>
Jean-Francois Dockes <jf@dockes.org> Jean-Francois Dockes <jf@dockes.org>
Yue Wang <yuleopen@gmail.com> Yue Wang <yuleopen@gmail.com>
Matthew Leon Grinshpun <ml@matthewleon.com>

206
INSTALL
View File

@@ -1,206 +0,0 @@
Music Player Daemon (MPD) - INSTALL
Introduction
------------
This document is a very small amount of documentation about what is needed to
install MPD. If more information is desired, read the user manual:
http://www.musicpd.org/doc/user/
Dependencies
------------
gcc 4.7 or later - http://gcc.gnu.org/
clang 3.2 or later - http://clang.llvm.org/
Any other C++11 compliant compiler should also work.
Boost 1.46 - http://www.boost.org/
Optional Output Dependencies
----------------------------
You will need at least one of these to compile MPD.
Most of these are available as packages on major distributions. Be sure to
install both the library package as well as the development package.
AO - http://www.xiph.org/ao/
A portable library that abstracts many audio output types as one API. Should
be used only if there is no native plugin available or if the native plugin
doesn't work. You will need libao.
ALSA - http://www.alsa-project.org/
The Advanced Linux Sound Architecture. Recommended audio output if you use
Linux. You will need libasound.
FIFO
This is a mostly undocumented, developer plugin to transmit raw data.
OSS - http://www.opensound.com
Open Sound System.
PulseAudio - http://www.pulseaudio.org/
An advanced sound daemon. You will need libpulse.
JACK - http://www.jackaudio.org/
A low-latency sound daemon.
libshout - http://www.icecast.org/
For streaming to an Icecast or Shoutcast server.
You also need an encoder: either libvorbisenc (ogg), or liblame (mp3).
OpenAL - http://kcat.strangesoft.net/openal.html
Open Audio Library
Optional Input Dependencies
---------------------------
You will need at least one of these to compile MPD.
Most of these are available as packages on major distributions. Be sure to
install both the library package as well as the development package.
MAD - http://www.underbit.com/products/mad/
For MP3 support. You will need libmad, and optionally libid3tag if you want
ID3 tag support.
libmpg123 - http://www.mpg123.de/
Alternative for MP3 support.
Ogg Vorbis - http://www.xiph.org/ogg/vorbis/
For Ogg Vorbis support. You will need libogg and libvorbis.
libopus - http://www.opus-codec.org/
Opus codec support
FLAC - http://flac.sourceforge.net/
For FLAC support. You will need version 1.2 or higher of libFLAC.
Audio File - http://www.68k.org/~michael/audiofile/
For WAVE, AIFF, and AU support. You will need libaudiofile.
FAAD2 - http://www.audiocoding.com/
For MP4/AAC support.
libmpcdec - http://www.musepack.net/
For Musepack support.
MikMod - http://mikmod.raphnet.net/
For MOD support. You will need libmikmod.
libavcodec, libavformat (ffmpeg or libav) - http://ffmpeg.mplayerhq.hu/ http://libav.org/
Multi-codec library.
libsidplay2 - http://sidplay2.sourceforge.net/
For C64 SID support.
libfluidsynth - http://fluidsynth.resonance.org/
For MIDI support.
libwildmidi 0.2.3 - http://wildmidi.sourceforge.net/
For MIDI support.
libsndfile - http://www.mega-nerd.com/libsndfile/
WAVE, AIFF, and many others.
libwavpack - http://www.wavpack.com/
For WavPack playback.
libadplug - http://adplug.sourceforge.net/
For AdLib playback.
Optional Miscellaneous Dependencies
-----------------------------------
Avahi - http://www.avahi.org/
For Zeroconf support.
libsamplerate - http://www.mega-nerd.com/SRC/
For advanced samplerate conversions.
libcurl - http://curl.haxx.se/
For playing HTTP streams.
libmms - https://launchpad.net/libmms
For playing MMS streams.
SQLite - http://www.sqlite.org/
For the sticker database.
libcdio - http://www.gnu.org/software/libcdio/
For playing audio CDs.
libsystemd-daemon - http://freedesktop.org/wiki/Software/systemd/
For systemd activation.
pkg-config
----------
MPD uses pkg-config to locate most external libraries. If you do not
have pkg-config, or if your version of the library does not ship the
".pc" file, you have to provide the library's build options in
environment variables. These variables are documented in "./configure
--help". Example:
FLAC_CFLAGS=-I/usr/include/FLAC FLAC_LIBS=-lFLAC ./configure
Download
--------
Get the latest release from of MPD from <http://www.musicpd.org/>.
Compile
-------
1) unpack the archive
$ tar xf mpd-x.x.x.tar.xz
2) change to directory created
$ cd mpd-x.x.x
3) Run configure script (this will determine what dependencies you have)
$ ./configure
4) Compile
$ make
Install (Optional)
-------
(as root)
$ make install
Run
---
1) run mpd:
$ mpd <config file>
First default is $XDG_CONFIG_HOME/mpd/mpd.conf then ~/.mpdconf then
~/.mpd/mpd.conf then /etc/mpd.conf. If neither of these exist a mpd
configuration file must be specified at runtime.
A sample config file is included with the source of MPD, mpdconf.example.
The first time MPD is run it will attempt to discover all music in your
music root, recursively. This can be affected by the symbolic link
options specified in the example mpd.conf.
Using MPD
---------
You can download many different interfaces for MPD at
http://www.musicpd.org/clients/

View File

@@ -243,6 +243,7 @@ CURL_SOURCES = \
src/lib/curl/Slist.hxx src/lib/curl/Slist.hxx
UPNP_SOURCES = \ UPNP_SOURCES = \
src/lib/upnp/Compat.hxx \
src/lib/upnp/Init.cxx src/lib/upnp/Init.hxx \ src/lib/upnp/Init.cxx src/lib/upnp/Init.hxx \
src/lib/upnp/ClientInit.cxx src/lib/upnp/ClientInit.hxx \ src/lib/upnp/ClientInit.cxx src/lib/upnp/ClientInit.hxx \
src/lib/upnp/Device.cxx src/lib/upnp/Device.hxx \ src/lib/upnp/Device.cxx src/lib/upnp/Device.hxx \
@@ -518,6 +519,8 @@ libevent_a_SOURCES = \
# UTF-8 library # UTF-8 library
libicu_a_SOURCES = \ libicu_a_SOURCES = \
src/lib/icu/CaseFold.cxx src/lib/icu/CaseFold.hxx \
src/lib/icu/Compare.cxx src/lib/icu/Compare.hxx \
src/lib/icu/Collate.cxx src/lib/icu/Collate.hxx \ src/lib/icu/Collate.cxx src/lib/icu/Collate.hxx \
src/lib/icu/Converter.cxx src/lib/icu/Converter.hxx src/lib/icu/Converter.cxx src/lib/icu/Converter.hxx
@@ -923,6 +926,7 @@ libtag_a_SOURCES =\
src/tag/ReplayGain.cxx src/tag/ReplayGain.hxx \ src/tag/ReplayGain.cxx src/tag/ReplayGain.hxx \
src/tag/MixRamp.cxx src/tag/MixRamp.hxx \ src/tag/MixRamp.cxx src/tag/MixRamp.hxx \
src/tag/Generic.cxx src/tag/Generic.hxx \ src/tag/Generic.cxx src/tag/Generic.hxx \
src/tag/Id3MusicBrainz.cxx src/tag/Id3MusicBrainz.hxx \
src/tag/ApeLoader.cxx src/tag/ApeLoader.hxx \ src/tag/ApeLoader.cxx src/tag/ApeLoader.hxx \
src/tag/ApeReplayGain.cxx src/tag/ApeReplayGain.hxx \ src/tag/ApeReplayGain.cxx src/tag/ApeReplayGain.hxx \
src/tag/ApeTag.cxx src/tag/ApeTag.hxx src/tag/ApeTag.cxx src/tag/ApeTag.hxx
@@ -1489,6 +1493,8 @@ liboutput_plugins_a_SOURCES += \
src/output/plugins/OSXOutputPlugin.cxx \ src/output/plugins/OSXOutputPlugin.cxx \
src/output/plugins/OSXOutputPlugin.hxx src/output/plugins/OSXOutputPlugin.hxx
endif endif
libmixer_plugins_a_SOURCES += \
src/mixer/plugins/OSXMixerPlugin.cxx
if ENABLE_PULSE if ENABLE_PULSE
liboutput_plugins_a_SOURCES += \ liboutput_plugins_a_SOURCES += \

52
NEWS
View File

@@ -1,3 +1,55 @@
ver 0.20.12 (2017/11/25)
* database
- upnp: adapt to libupnp 1.8 API changes
* input
- cdio_paranoia, ffmpeg, file, smbclient: reduce lock contention,
fixing lots of xrun problems
- curl: fix seeking
* decoder
- ffmpeg: fix GCC 8 warning
- vorbis: fix Tremor support
* player
- log message when decoder is too slow
* encoder
- vorbis: default to quality 3
* output
- fix hanging playback with soxr resampler
- httpd: flush encoder after tag; fixes corrupt Vorbis stream
ver 0.20.11 (2017/10/18)
* storage
- curl: support Content-Type application/xml
* decoder
- ffmpeg: more reliable song duration
- gme: fix track numbering
* improve random song order when switching songs manually
* fix case insensitive search without libicu
* fix Unicode file names in playlists on Windows
* fix endless loop when accessing malformed file names in ZIP files
ver 0.20.10 (2017/08/24)
* decoder
- ffmpeg: support MusicBrainz ID3v2 tags
* tags
- aiff: fix FORM chunk size endianess (is big-endian)
* mixer
- osx: add a mixer for OSX.
* fix crash when resuming playback before decoder is ready
* fix crash on Windows
ver 0.20.9 (2017/06/04)
* decoder
- ffmpeg: support *.adx
* fix byte order detection on FreeBSD/aarch64
* fix more random crashes when compiled with clang
ver 0.20.8 (2017/05/19)
* output
- osx: fix build failure due to missing "noexcept"
* playlist
- m3u: support MIME type `audio/mpegurl`
* fix build failure with GCC 4.x
ver 0.20.7 (2017/05/15) ver 0.20.7 (2017/05/15)
* database * database
- simple: fix false positive directory loop detection with NFS - simple: fix false positive directory loop detection with NFS

View File

@@ -7,7 +7,8 @@ server's audio device. The daemon stores info about all available music,
and this info can be easily searched and retrieved. Player control, info and this info can be easily searched and retrieved. Player control, info
retrieval, and playlist management can all be managed remotely. retrieval, and playlist management can all be managed remotely.
For basic installation information see the INSTALL file. For basic installation instructions
[read the manual](https://www.musicpd.org/doc/user/install.html).
# Users # Users

View File

@@ -154,5 +154,9 @@ configure = [
] + configure_args ] + configure_args
from build.cmdline import concatenate_cmdline_variables
configure = concatenate_cmdline_variables(configure,
set(('CFLAGS', 'CXXFLAGS', 'CPPFLAGS', 'LDFLAGS', 'LIBS')))
subprocess.check_call(configure, env=toolchain.env) subprocess.check_call(configure, env=toolchain.env)
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], env=toolchain.env) subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], env=toolchain.env)

View File

@@ -1,10 +1,10 @@
AC_PREREQ(2.60) AC_PREREQ(2.60)
AC_INIT(mpd, 0.20.7, musicpd-dev-team@lists.sourceforge.net) AC_INIT(mpd, 0.20.12, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0 VERSION_MAJOR=0
VERSION_MINOR=20 VERSION_MINOR=20
VERSION_REVISION=7 VERSION_REVISION=12
VERSION_EXTRA=0 VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx]) AC_CONFIG_SRCDIR([src/Main.cxx])
@@ -241,6 +241,7 @@ AC_CHECK_FUNCS(getpwnam_r getpwuid_r)
AC_CHECK_FUNCS(initgroups) AC_CHECK_FUNCS(initgroups)
AC_CHECK_FUNCS(fnmatch) AC_CHECK_FUNCS(fnmatch)
AC_CHECK_FUNCS(strndup) AC_CHECK_FUNCS(strndup)
AC_CHECK_FUNCS(strcasestr)
if test x$host_is_linux = xyes; then if test x$host_is_linux = xyes; then
MPD_OPTIONAL_FUNC(eventfd, eventfd, USE_EVENTFD) MPD_OPTIONAL_FUNC(eventfd, eventfd, USE_EVENTFD)
@@ -1384,6 +1385,11 @@ then
AX_APPEND_COMPILE_FLAGS([-Wcast-qual]) AX_APPEND_COMPILE_FLAGS([-Wcast-qual])
AX_APPEND_COMPILE_FLAGS([-Wwrite-strings]) AX_APPEND_COMPILE_FLAGS([-Wwrite-strings])
AX_APPEND_COMPILE_FLAGS([-Wsign-compare]) AX_APPEND_COMPILE_FLAGS([-Wsign-compare])
dnl This GCC8 warning for C++17 ABI compatibility is of no
dnl interest for us, because we're not a shared library.
AX_APPEND_COMPILE_FLAGS([-Wno-noexcept-type])
AC_LANG_POP AC_LANG_POP
fi fi

View File

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

View File

@@ -80,13 +80,40 @@
cd mpd-version</programlisting> cd mpd-version</programlisting>
<para> <para>
Make sure that all the required libraries and build tools are In any case, you need:
installed. The <filename>INSTALL</filename> file has a list. </para>
<itemizedlist>
<listitem>
<para>
a C++14 compiler (e.g. <ulink
url="http://gcc.gnu.org/">gcc 4.9</ulink> or <ulink
url="http://clang.llvm.org/">clang 3.9</ulink>)
</para>
</listitem>
<listitem>
<para>
<ulink url="http://www.boost.org/">Boost 1.46</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="https://www.freedesktop.org/wiki/Software/pkg-config/">pkg-config</ulink>
</para>
</listitem>
</itemizedlist>
<para>
Each plugin usually needs a codec library, which you also need
to install. Check the plugin reference for details about
required libraries.
</para> </para>
<para> <para>
For example, the following installs a fairly complete list of For example, the following installs a fairly complete list of
build dependencies on Debian Wheezy: build dependencies on Debian Jessie:
</para> </para>
<programlisting> <programlisting>
@@ -98,19 +125,20 @@ apt-get install g++ \
libmpcdec-dev libwavpack-dev libwildmidi-dev \ libmpcdec-dev libwavpack-dev libwildmidi-dev \
libsidplay2-dev libsidutils-dev libresid-builder-dev \ libsidplay2-dev libsidutils-dev libresid-builder-dev \
libavcodec-dev libavformat-dev \ libavcodec-dev libavformat-dev \
libmp3lame-dev \ libmp3lame-dev libtwolame-dev libshine-dev \
libsamplerate0-dev libsoxr-dev \ libsamplerate0-dev libsoxr-dev \
libbz2-dev libcdio-paranoia-dev libiso9660-dev libmms-dev \ libbz2-dev libcdio-paranoia-dev libiso9660-dev libmms-dev \
libzzip-dev \ libzzip-dev \
libcurl4-gnutls-dev libyajl-dev libexpat-dev \ libcurl4-gnutls-dev libyajl-dev libexpat-dev \
libasound2-dev libao-dev libjack-jackd2-dev libopenal-dev \ libasound2-dev libao-dev libjack-jackd2-dev libopenal-dev \
libpulse-dev libroar-dev libshout3-dev \ libpulse-dev libroar-dev libshout3-dev \
libsndio-dev \
libmpdclient-dev \ libmpdclient-dev \
libnfs-dev libsmbclient-dev \ libnfs-dev libsmbclient-dev \
libupnp-dev \ libupnp-dev \
libavahi-client-dev \ libavahi-client-dev \
libsqlite3-dev \ libsqlite3-dev \
libsystemd-daemon-dev libwrap0-dev \ libsystemd-dev libwrap0-dev \
libcppunit-dev xmlto \ libcppunit-dev xmlto \
libboost-dev \ libboost-dev \
libicu-dev libicu-dev
@@ -1035,6 +1063,40 @@ systemctl start mpd.socket</programlisting>
</informaltable> </informaltable>
</section> </section>
<section id="stickers">
<title>The Sticker Database</title>
<para>
"Stickers" are pieces of information attached to songs.
Some clients use them to store ratings and other volatile
data. This feature requires <ulink
url="http://www.sqlite.org/">SQLite</ulink>, compile-time
configure option <parameter>--enable-sqlite</parameter>.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>sticker_file</varname>
<parameter>PATH</parameter>
</entry>
<entry>
The location of the sticker database.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section> <section>
<title>Resource Limitations</title> <title>Resource Limitations</title>
@@ -1165,6 +1227,55 @@ systemctl start mpd.socket</programlisting>
</tgroup> </tgroup>
</informaltable> </informaltable>
</section> </section>
<section id="zeroconf">
<title>Zeroconf</title>
<para>
If Zeroconf support (<ulink
url="http://avahi.org/">Avahi</ulink> or Apple's Bonjour)
was enabled at compile time with
<parameter>--with-zeroconf=...</parameter>, MPD can announce
its presence on the network. The following settings control
this feature:
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>zeroconf_enabled</varname>
<parameter>yes|no</parameter>
</entry>
<entry>
Enables or disables this feature. Default is
<parameter>yes</parameter>.
</entry>
</row>
<row>
<entry>
<varname>zeroconf_name</varname>
<parameter>NAME</parameter>
</entry>
<entry>
The service name to publish via Zeroconf. The
default is "<parameter>Music Player</parameter>".
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
</section> </section>
</chapter> </chapter>
@@ -2078,7 +2189,9 @@ run</programlisting>
<title><varname>cdio_paranoia</varname></title> <title><varname>cdio_paranoia</varname></title>
<para> <para>
Plays audio CDs. The URI has the form: Plays audio CDs using <ulink
url="http://www.gnu.org/software/libcdio/"><filename>libcdio</filename></ulink>.
The URI has the form:
"<filename>cdda://[DEVICE][/TRACK]</filename>". The "<filename>cdda://[DEVICE][/TRACK]</filename>". The
simplest form <filename>cdda://</filename> plays the whole simplest form <filename>cdda://</filename> plays the whole
disc in the default drive. disc in the default drive.
@@ -2114,7 +2227,8 @@ run</programlisting>
<title><varname>curl</varname></title> <title><varname>curl</varname></title>
<para> <para>
Opens remote files or streams over HTTP. Opens remote files or streams over HTTP using <ulink
url="http://curl.haxx.se/"><filename>libcurl</filename></ulink>.
</para> </para>
<para> <para>
@@ -2207,7 +2321,8 @@ run</programlisting>
<title><varname>mms</varname></title> <title><varname>mms</varname></title>
<para> <para>
Plays streams with the MMS protocol. Plays streams with the MMS protocol using <ulink
url="https://launchpad.net/libmms"><filename>libmms</filename></ulink>.
</para> </para>
</section> </section>
@@ -2263,7 +2378,8 @@ run</programlisting>
<title><varname>adplug</varname></title> <title><varname>adplug</varname></title>
<para> <para>
Decodes AdLib files. Decodes AdLib files using <ulink
url="http://adplug.sourceforge.net/">libadplug</ulink>.
</para> </para>
<informaltable> <informaltable>
@@ -2294,8 +2410,8 @@ run</programlisting>
<title><varname>audiofile</varname></title> <title><varname>audiofile</varname></title>
<para> <para>
Decodes WAV and AIFF files using Decodes WAV and AIFF files using <ulink
<filename>libaudiofile</filename>. url="http://audiofile.68k.org/"><filename>libaudiofile</filename></ulink>.
</para> </para>
</section> </section>
@@ -2303,7 +2419,8 @@ run</programlisting>
<title><varname>faad</varname></title> <title><varname>faad</varname></title>
<para> <para>
Decodes AAC files using <filename>libfaad</filename>. Decodes AAC files using <ulink
url="http://www.audiocoding.com/"><filename>libfaad</filename></ulink>.
</para> </para>
</section> </section>
@@ -2311,8 +2428,8 @@ run</programlisting>
<title><varname>ffmpeg</varname></title> <title><varname>ffmpeg</varname></title>
<para> <para>
Decodes various codecs using Decodes various codecs using <ulink
<application>FFmpeg</application>. url="https://ffmpeg.org/"><application>FFmpeg</application></ulink>.
</para> </para>
<informaltable> <informaltable>
@@ -2363,7 +2480,7 @@ run</programlisting>
<para> <para>
Decodes FLAC files using Decodes FLAC files using
<application>libFLAC</application>. <ulink url="https://xiph.org/flac/"><application>libFLAC</application></ulink>.
</para> </para>
</section> </section>
@@ -2483,7 +2600,8 @@ run</programlisting>
<title><varname>mad</varname></title> <title><varname>mad</varname></title>
<para> <para>
Decodes MP3 files using <application>libmad</application>. Decodes MP3 files using <ulink
url="http://www.underbit.com/products/mad/"><application>libmad</application></ulink>.
</para> </para>
</section> </section>
@@ -2563,8 +2681,8 @@ run</programlisting>
<title><varname>mpcdec</varname></title> <title><varname>mpcdec</varname></title>
<para> <para>
Decodes Musepack files using Decodes Musepack files using <ulink
<application>libmpcdec</application>. url="http://www.musepack.net/"><application>libmpcdec</application></ulink>.
</para> </para>
</section> </section>
@@ -2572,7 +2690,17 @@ run</programlisting>
<title><varname>mpg123</varname></title> <title><varname>mpg123</varname></title>
<para> <para>
Decodes MP3 files using <application>libmpg123</application>. Decodes MP3 files using <ulink
url="http://www.mpg123.de/"><application>libmpg123</application></ulink>.
</para>
</section>
<section id="opus_decoder">
<title><varname>opus</varname></title>
<para>
Decodes Opus files using <ulink
url="http://www.opus-codec.org/"><application>libopus</application></ulink>.
</para> </para>
</section> </section>
@@ -2592,8 +2720,8 @@ run</programlisting>
<title><varname>sidplay</varname></title> <title><varname>sidplay</varname></title>
<para> <para>
C64 SID decoder based on C64 SID decoder based on <ulink
<application>libsidplay</application>. url="http://sidplay2.sourceforge.net/"><application>libsidplay</application></ulink>.
</para> </para>
<informaltable> <informaltable>
@@ -2649,8 +2777,8 @@ run</programlisting>
<title><varname>sndfile</varname></title> <title><varname>sndfile</varname></title>
<para> <para>
Decodes WAV and AIFF files using Decodes WAV and AIFF files using <ulink
<filename>libsndfile</filename>. url="http://www.mega-nerd.com/libsndfile/"><filename>libsndfile</filename></ulink>.
</para> </para>
</section> </section>
@@ -2658,8 +2786,8 @@ run</programlisting>
<title><varname>vorbis</varname></title> <title><varname>vorbis</varname></title>
<para> <para>
Decodes Ogg-Vorbis files using Decodes Ogg-Vorbis files using <ulink
<application>libvorbis</application>. url="http://www.xiph.org/ogg/vorbis/"><application>libvorbis</application></ulink>.
</para> </para>
</section> </section>
@@ -2668,7 +2796,7 @@ run</programlisting>
<para> <para>
Decodes WavPack files using Decodes WavPack files using
<application>libwavpack</application>. <ulink url="http://www.wavpack.com/"><application>libwavpack</application></ulink>.
</para> </para>
</section> </section>
@@ -2863,6 +2991,60 @@ run</programlisting>
</informaltable> </informaltable>
</section> </section>
<section id="opus_encoder">
<title><varname>opus</varname></title>
<para>
Encodes into <ulink
url="http://www.opus-codec.org/">Ogg Opus</ulink>.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>bitrate</varname>
</entry>
<entry>
Sets the data rate in bit per second. The special
value "auto" lets <application>libopus</application>
choose a rate (which is the default), and "max" uses
the maximum possible data rate.
</entry>
</row>
<row>
<entry>
<varname>complexity</varname>
</entry>
<entry>
Sets the <ulink
url="https://wiki.xiph.org/OpusFAQ#What_is_the_complexity_of_Opus.3F">Opus
complexity</ulink>.
</entry>
</row>
<row>
<entry>
<varname>signal</varname>
</entry>
<entry>
Sets the Opus signal type. Valid values are "auto"
(the default), "voice" and "music".
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section id="vorbis_encoder"> <section id="vorbis_encoder">
<title><varname>vorbis</varname></title> <title><varname>vorbis</varname></title>
@@ -2886,8 +3068,8 @@ run</programlisting>
</entry> </entry>
<entry> <entry>
Sets the quality for VBR. -1 is the lowest quality, Sets the quality for VBR. -1 is the lowest quality,
10 is the highest quality. Cannot be used with 10 is the highest quality. Defaults to 3. Cannot
<varname>bitrate</varname>. be used with <varname>bitrate</varname>.
</entry> </entry>
</row> </row>
<row> <row>
@@ -3492,7 +3674,7 @@ run</programlisting>
</informaltable> </informaltable>
</section> </section>
<section> <section id="jack_output">
<title><varname>jack</varname></title> <title><varname>jack</varname></title>
<para> <para>
@@ -3772,7 +3954,7 @@ run</programlisting>
</informaltable> </informaltable>
</section> </section>
<section> <section id="openal_output">
<title><varname>openal</varname></title> <title><varname>openal</varname></title>
<para> <para>
@@ -3923,7 +4105,7 @@ run</programlisting>
<para> <para>
The <varname>pulse</varname> plugin connects to a <ulink The <varname>pulse</varname> plugin connects to a <ulink
url="http://www.freedesktop.org/wiki/Software/PulseAudio/"><application>PulseAudio</application></ulink> url="http://www.freedesktop.org/wiki/Software/PulseAudio/"><application>PulseAudio</application></ulink>
server. server. Requires <filename>libpulse</filename>.
</para> </para>
<informaltable> <informaltable>
@@ -4111,7 +4293,8 @@ run</programlisting>
url="http://www.shoutcast.com/"><application>ShoutCast</application></ulink> url="http://www.shoutcast.com/"><application>ShoutCast</application></ulink>
or <ulink or <ulink
url="http://icecast.org/"><application>IceCast</application></ulink> url="http://icecast.org/"><application>IceCast</application></ulink>
server. It forwards tags to this server. server using <filename>libshout</filename>. It forwards
tags to this server.
</para> </para>
<para> <para>

29
python/build/cmdline.py Normal file
View File

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

View File

@@ -19,8 +19,8 @@ libvorbis = AutotoolsProject(
) )
opus = AutotoolsProject( opus = AutotoolsProject(
'http://downloads.xiph.org/releases/opus/opus-1.1.4.tar.gz', 'https://archive.mozilla.org/pub/opus/opus-1.2.1.tar.gz',
'9122b6b380081dd2665189f97bfd777f04f92dc3ab6698eea1dbb27ad59d8692', 'cfafd339ccd9c5ef8d6ab15d7e1a412c054bf4cb4ecbbbcc78c12ef2def70732',
'lib/libopus.a', 'lib/libopus.a',
['--disable-shared', '--enable-static'], ['--disable-shared', '--enable-static'],
) )
@@ -57,9 +57,20 @@ libmad = AutotoolsProject(
autogen=True, autogen=True,
) )
liblame = AutotoolsProject(
'http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz',
'24346b4158e4af3bd9f2e194bb23eb473c75fb7377011523353196b19b9a23ff',
'lib/libmp3lame.a',
[
'--disable-shared', '--enable-static',
'--disable-gtktest', '--disable-analyzer-hooks',
'--disable-decoder', '--disable-frontend',
],
)
ffmpeg = FfmpegProject( ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-3.3.tar.xz', 'http://ffmpeg.org/releases/ffmpeg-3.3.3.tar.xz',
'599e7f7c017221c22011c4037b88bdcd1c47cd40c1e466838bc3c465f3e9569d', 'd2a9002cdc6b533b59728827186c044ad02ba64841f1b7cd6c21779875453a1e',
'lib/libavcodec.a', 'lib/libavcodec.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
@@ -82,8 +93,8 @@ ffmpeg = FfmpegProject(
) )
curl = AutotoolsProject( curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.54.0.tar.lzma', 'http://curl.haxx.se/download/curl-7.55.1.tar.xz',
'cd6aa6039f13e0b06e0a93e1b93754f6dc07f444812bb6c32be75a8f28c4070a', '3eafca6e84ecb4af5f35795dee84e643d5428287e88c041122bb8dac18676bb7',
'lib/libcurl.a', 'lib/libcurl.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
@@ -103,7 +114,7 @@ curl = AutotoolsProject(
) )
boost = BoostProject( boost = BoostProject(
'http://downloads.sourceforge.net/project/boost/boost/1.64.0/boost_1_64_0.tar.bz2', 'http://downloads.sourceforge.net/project/boost/boost/1.65.0/boost_1_65_0.tar.bz2',
'7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332', 'ea26712742e2fb079c2a566a31f3266973b76e38222b9f88b387e3c8b2f9902c',
'include/boost/version.hpp', 'include/boost/version.hpp',
) )

View File

@@ -127,7 +127,7 @@ struct AudioFormat {
void ApplyMask(AudioFormat mask) noexcept; void ApplyMask(AudioFormat mask) noexcept;
gcc_pure gcc_pure
AudioFormat WithMask(AudioFormat mask) const { AudioFormat WithMask(AudioFormat mask) const noexcept {
AudioFormat result = *this; AudioFormat result = *this;
result.ApplyMask(mask); result.ApplyMask(mask);
return result; return result;

View File

@@ -152,7 +152,7 @@ public:
bool IsRemote() const noexcept; bool IsRemote() const noexcept;
gcc_pure gcc_pure
bool IsFile() const { bool IsFile() const noexcept {
return !IsRemote(); return !IsRemote();
} }
@@ -162,11 +162,11 @@ public:
gcc_pure gcc_pure
bool IsInDatabase() const noexcept; bool IsInDatabase() const noexcept;
const Tag &GetTag() const { const Tag &GetTag() const noexcept {
return tag; return tag;
} }
Tag &WritableTag() { Tag &WritableTag() noexcept {
return tag; return tag;
} }

View File

@@ -121,8 +121,16 @@ static constexpr size_t KILOBYTE = 1024;
static constexpr size_t MEGABYTE = 1024 * KILOBYTE; static constexpr size_t MEGABYTE = 1024 * KILOBYTE;
static constexpr size_t DEFAULT_BUFFER_SIZE = 4 * MEGABYTE; static constexpr size_t DEFAULT_BUFFER_SIZE = 4 * MEGABYTE;
static constexpr size_t MIN_BUFFER_SIZE = std::max(CHUNK_SIZE * 32,
64 * KILOBYTE); static
#if GCC_OLDER_THAN(5,0)
/* gcc 4.x has no "constexpr" for std::max() */
const
#else
constexpr
#endif
size_t MIN_BUFFER_SIZE = std::max(CHUNK_SIZE * 32,
64 * KILOBYTE);
static constexpr unsigned DEFAULT_BUFFER_BEFORE_PLAY = 10; static constexpr unsigned DEFAULT_BUFFER_BEFORE_PLAY = 10;

View File

@@ -31,45 +31,45 @@ class MixRampInfo {
public: public:
MixRampInfo() = default; MixRampInfo() = default;
void Clear() { void Clear() noexcept {
start.clear(); start.clear();
end.clear(); end.clear();
} }
gcc_pure gcc_pure
bool IsDefined() const { bool IsDefined() const noexcept {
return !start.empty() || !end.empty(); return !start.empty() || !end.empty();
} }
gcc_pure gcc_pure
const char *GetStart() const { const char *GetStart() const noexcept {
return start.empty() ? nullptr : start.c_str(); return start.empty() ? nullptr : start.c_str();
} }
gcc_pure gcc_pure
const char *GetEnd() const { const char *GetEnd() const noexcept {
return end.empty() ? nullptr : end.c_str(); return end.empty() ? nullptr : end.c_str();
} }
void SetStart(const char *new_value) { void SetStart(const char *new_value) noexcept {
if (new_value == nullptr) if (new_value == nullptr)
start.clear(); start.clear();
else else
start = new_value; start = new_value;
} }
void SetStart(std::string &&new_value) { void SetStart(std::string &&new_value) noexcept {
start = std::move(new_value); start = std::move(new_value);
} }
void SetEnd(const char *new_value) { void SetEnd(const char *new_value) noexcept {
if (new_value == nullptr) if (new_value == nullptr)
end.clear(); end.clear();
else else
end = new_value; end = new_value;
} }
void SetEnd(std::string &&new_value) { void SetEnd(std::string &&new_value) noexcept {
end = std::move(new_value); end = std::move(new_value);
} }
}; };

View File

@@ -60,7 +60,7 @@ public:
* music_buffer_new(). * music_buffer_new().
*/ */
gcc_pure gcc_pure
unsigned GetSize() const { unsigned GetSize() const noexcept {
return buffer.GetCapacity(); return buffer.GetCapacity();
} }

View File

@@ -39,7 +39,7 @@ MusicPipe::Contains(const MusicChunk *chunk) const noexcept
#endif #endif
MusicChunk * MusicChunk *
MusicPipe::Shift() MusicPipe::Shift() noexcept
{ {
const std::lock_guard<Mutex> protect(mutex); const std::lock_guard<Mutex> protect(mutex);
@@ -73,7 +73,7 @@ MusicPipe::Shift()
} }
void void
MusicPipe::Clear(MusicBuffer &buffer) MusicPipe::Clear(MusicBuffer &buffer) noexcept
{ {
MusicChunk *chunk; MusicChunk *chunk;
@@ -82,7 +82,7 @@ MusicPipe::Clear(MusicBuffer &buffer)
} }
void void
MusicPipe::Push(MusicChunk *chunk) MusicPipe::Push(MusicChunk *chunk) noexcept
{ {
assert(!chunk->IsEmpty()); assert(!chunk->IsEmpty());
assert(chunk->length == 0 || chunk->audio_format.IsValid()); assert(chunk->length == 0 || chunk->audio_format.IsValid());

View File

@@ -77,7 +77,7 @@ public:
* audio_format. * audio_format.
*/ */
gcc_pure gcc_pure
bool CheckFormat(AudioFormat other) const { bool CheckFormat(AudioFormat other) const noexcept {
return !audio_format.IsDefined() || return !audio_format.IsDefined() ||
audio_format == other; audio_format == other;
} }
@@ -94,37 +94,37 @@ public:
* nullptr if the pipe is empty. * nullptr if the pipe is empty.
*/ */
gcc_pure gcc_pure
const MusicChunk *Peek() const { const MusicChunk *Peek() const noexcept {
return head; return head;
} }
/** /**
* Removes the first chunk from the head, and returns it. * Removes the first chunk from the head, and returns it.
*/ */
MusicChunk *Shift(); MusicChunk *Shift() noexcept;
/** /**
* Clears the whole pipe and returns the chunks to the buffer. * Clears the whole pipe and returns the chunks to the buffer.
* *
* @param buffer the buffer object to return the chunks to * @param buffer the buffer object to return the chunks to
*/ */
void Clear(MusicBuffer &buffer); void Clear(MusicBuffer &buffer) noexcept;
/** /**
* Pushes a chunk to the tail of the pipe. * Pushes a chunk to the tail of the pipe.
*/ */
void Push(MusicChunk *chunk); void Push(MusicChunk *chunk) noexcept;
/** /**
* Returns the number of chunks currently in this pipe. * Returns the number of chunks currently in this pipe.
*/ */
gcc_pure gcc_pure
unsigned GetSize() const { unsigned GetSize() const noexcept {
return size; return size;
} }
gcc_pure gcc_pure
bool IsEmpty() const { bool IsEmpty() const noexcept {
return GetSize() == 0; return GetSize() == 0;
} }
}; };

View File

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

View File

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

View File

@@ -27,7 +27,7 @@
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/TimeParser.hxx" #include "util/TimeParser.hxx"
#include "util/UriUtil.hxx" #include "util/UriUtil.hxx"
#include "lib/icu/Collate.hxx" #include "lib/icu/CaseFold.hxx"
#include <stdexcept> #include <stdexcept>
@@ -57,17 +57,10 @@ locate_parse_type(const char *str) noexcept
return tag_name_parse_i(str); return tag_name_parse_i(str);
} }
static AllocatedString<>
ImportString(const char *p, bool fold_case)
{
return fold_case
? IcuCaseFold(p)
: AllocatedString<>::Duplicate(p);
}
SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case) SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
:tag(_tag), fold_case(_fold_case), :tag(_tag),
value(ImportString(_value, _fold_case)) value(AllocatedString<>::Duplicate(_value)),
fold_case(_fold_case ? IcuCompare(value.c_str()) : IcuCompare())
{ {
} }
@@ -87,9 +80,7 @@ SongFilter::Item::StringMatch(const char *s) const noexcept
assert(tag != LOCATE_TAG_MODIFIED_SINCE); assert(tag != LOCATE_TAG_MODIFIED_SINCE);
if (fold_case) { if (fold_case) {
const auto folded = IcuCaseFold(s); return fold_case.IsIn(s);
assert(!folded.IsNull());
return StringFind(folded.c_str(), value.c_str()) != nullptr;
} else { } else {
return StringIsEqual(s, value.c_str()); return StringIsEqual(s, value.c_str());
} }

View File

@@ -20,6 +20,7 @@
#ifndef MPD_SONG_FILTER_HXX #ifndef MPD_SONG_FILTER_HXX
#define MPD_SONG_FILTER_HXX #define MPD_SONG_FILTER_HXX
#include "lib/icu/Compare.hxx"
#include "util/AllocatedString.hxx" #include "util/AllocatedString.hxx"
#include "Compiler.h" #include "Compiler.h"
@@ -48,10 +49,13 @@ public:
class Item { class Item {
uint8_t tag; uint8_t tag;
bool fold_case;
AllocatedString<> value; AllocatedString<> value;
/**
* This value is only set if case folding is enabled.
*/
IcuCompare fold_case;
/** /**
* For #LOCATE_TAG_MODIFIED_SINCE * For #LOCATE_TAG_MODIFIED_SINCE
*/ */

View File

@@ -100,7 +100,7 @@ public:
} }
gcc_pure gcc_pure
bool IsExpired() const { bool IsExpired() const noexcept {
return !FullyBufferedSocket::IsDefined(); return !FullyBufferedSocket::IsDefined();
} }

View File

@@ -45,68 +45,57 @@ public:
: default_value; : default_value;
} }
gcc_pure
int ParseInt(unsigned idx) const { int ParseInt(unsigned idx) const {
assert(idx < size); assert(idx < size);
return ParseCommandArgInt(data[idx]); return ParseCommandArgInt(data[idx]);
} }
gcc_pure
int ParseInt(unsigned idx, int min_value, int max_value) const { int ParseInt(unsigned idx, int min_value, int max_value) const {
assert(idx < size); assert(idx < size);
return ParseCommandArgInt(data[idx], min_value, max_value); return ParseCommandArgInt(data[idx], min_value, max_value);
} }
gcc_pure
int ParseUnsigned(unsigned idx) const { int ParseUnsigned(unsigned idx) const {
assert(idx < size); assert(idx < size);
return ParseCommandArgUnsigned(data[idx]); return ParseCommandArgUnsigned(data[idx]);
} }
gcc_pure
int ParseUnsigned(unsigned idx, unsigned max_value) const { int ParseUnsigned(unsigned idx, unsigned max_value) const {
assert(idx < size); assert(idx < size);
return ParseCommandArgUnsigned(data[idx], max_value); return ParseCommandArgUnsigned(data[idx], max_value);
} }
gcc_pure
bool ParseBool(unsigned idx) const { bool ParseBool(unsigned idx) const {
assert(idx < size); assert(idx < size);
return ParseCommandArgBool(data[idx]); return ParseCommandArgBool(data[idx]);
} }
gcc_pure
RangeArg ParseRange(unsigned idx) const { RangeArg ParseRange(unsigned idx) const {
assert(idx < size); assert(idx < size);
return ParseCommandArgRange(data[idx]); return ParseCommandArgRange(data[idx]);
} }
gcc_pure
float ParseFloat(unsigned idx) const { float ParseFloat(unsigned idx) const {
assert(idx < size); assert(idx < size);
return ParseCommandArgFloat(data[idx]); return ParseCommandArgFloat(data[idx]);
} }
gcc_pure
SongTime ParseSongTime(unsigned idx) const { SongTime ParseSongTime(unsigned idx) const {
assert(idx < size); assert(idx < size);
return ParseCommandArgSongTime(data[idx]); return ParseCommandArgSongTime(data[idx]);
} }
gcc_pure
SignedSongTime ParseSignedSongTime(unsigned idx) const { SignedSongTime ParseSignedSongTime(unsigned idx) const {
assert(idx < size); assert(idx < size);
return ParseCommandArgSignedSongTime(data[idx]); return ParseCommandArgSignedSongTime(data[idx]);
} }
gcc_pure
int ParseOptional(unsigned idx, int default_value) const { int ParseOptional(unsigned idx, int default_value) const {
return idx < size return idx < size
? ParseInt(idx) ? ParseInt(idx)
: default_value; : default_value;
} }
gcc_pure
RangeArg ParseOptional(unsigned idx, RangeArg default_value) const { RangeArg ParseOptional(unsigned idx, RangeArg default_value) const {
return idx < size return idx < size
? ParseRange(idx) ? ParseRange(idx)

View File

@@ -82,12 +82,12 @@ struct ConfigBlock {
* object that was synthesized and not loaded from a * object that was synthesized and not loaded from a
* configuration file. * configuration file.
*/ */
bool IsNull() const { bool IsNull() const noexcept {
return line < 0; return line < 0;
} }
gcc_pure gcc_pure
bool IsEmpty() const { bool IsEmpty() const noexcept {
return block_params.empty(); return block_params.empty();
} }

View File

@@ -29,7 +29,7 @@
struct StringLess { struct StringLess {
gcc_pure gcc_pure
bool operator()(const char *a, const char *b) const { bool operator()(const char *a, const char *b) const noexcept {
return strcmp(a, b) < 0; return strcmp(a, b) < 0;
} }
}; };

View File

@@ -106,7 +106,6 @@ public:
TagType tag_type, tag_mask_t group_mask, TagType tag_type, tag_mask_t group_mask,
VisitTag visit_tag) const = 0; VisitTag visit_tag) const = 0;
gcc_pure
virtual DatabaseStats GetStats(const DatabaseSelection &selection) const = 0; virtual DatabaseStats GetStats(const DatabaseSelection &selection) const = 0;
/** /**
@@ -127,7 +126,7 @@ public:
* Returns 0 if that is not not known/available. * Returns 0 if that is not not known/available.
*/ */
gcc_pure gcc_pure
virtual time_t GetUpdateStamp() const = 0; virtual time_t GetUpdateStamp() const noexcept = 0;
}; };
#endif #endif

View File

@@ -44,16 +44,16 @@ struct LightDirectory {
constexpr LightDirectory(const char *_uri, time_t _mtime) constexpr LightDirectory(const char *_uri, time_t _mtime)
:uri(_uri), mtime(_mtime) {} :uri(_uri), mtime(_mtime) {}
static constexpr LightDirectory Root() { static constexpr LightDirectory Root() noexcept {
return LightDirectory("", 0); return LightDirectory("", 0);
} }
bool IsRoot() const { bool IsRoot() const noexcept {
return *uri == 0; return *uri == 0;
} }
gcc_pure gcc_pure
const char *GetPath() const { const char *GetPath() const noexcept {
return uri; return uri;
} }
}; };

View File

@@ -45,7 +45,7 @@ struct PlaylistInfo {
constexpr CompareName(const char *_name):name(_name) {} constexpr CompareName(const char *_name):name(_name) {}
gcc_pure gcc_pure
bool operator()(const PlaylistInfo &pi) const { bool operator()(const PlaylistInfo &pi) const noexcept {
return pi.name.compare(name) == 0; return pi.name.compare(name) == 0;
} }
}; };

View File

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

View File

@@ -127,7 +127,7 @@ public:
unsigned Update(const char *uri_utf8, bool discard) override; unsigned Update(const char *uri_utf8, bool discard) override;
time_t GetUpdateStamp() const override { time_t GetUpdateStamp() const noexcept override {
return update_stamp; return update_stamp;
} }

View File

@@ -187,7 +187,7 @@ public:
} }
gcc_pure gcc_pure
const char *GetPath() const { const char *GetPath() const noexcept {
return path.c_str(); return path.c_str();
} }

View File

@@ -76,7 +76,7 @@ public:
const ConfigBlock &block); const ConfigBlock &block);
gcc_pure gcc_pure
Directory &GetRoot() { Directory &GetRoot() noexcept {
assert(root != NULL); assert(root != NULL);
return *root; return *root;
@@ -125,7 +125,7 @@ public:
DatabaseStats GetStats(const DatabaseSelection &selection) const override; DatabaseStats GetStats(const DatabaseSelection &selection) const override;
time_t GetUpdateStamp() const override { time_t GetUpdateStamp() const noexcept override {
return mtime; return mtime;
} }

View File

@@ -60,7 +60,7 @@ public:
* Parent's ObjectId * Parent's ObjectId
*/ */
std::string parent_id; std::string parent_id;
std::string url; std::string url;
/** /**
@@ -80,7 +80,7 @@ public:
UPnPDirObject &operator=(UPnPDirObject &&) = default; UPnPDirObject &operator=(UPnPDirObject &&) = default;
void Clear() { void Clear() noexcept {
id.clear(); id.clear();
parent_id.clear(); parent_id.clear();
url.clear(); url.clear();
@@ -90,7 +90,7 @@ public:
} }
gcc_pure gcc_pure
bool Check() const { bool Check() const noexcept {
return !id.empty() && !parent_id.empty() && !name.empty() && return !id.empty() && !parent_id.empty() && !name.empty() &&
(type != UPnPDirObject::Type::ITEM || (type != UPnPDirObject::Type::ITEM ||
item_class != UPnPDirObject::ItemClass::UNKNOWN); item_class != UPnPDirObject::ItemClass::UNKNOWN);

View File

@@ -93,7 +93,7 @@ public:
DatabaseStats GetStats(const DatabaseSelection &selection) const override; DatabaseStats GetStats(const DatabaseSelection &selection) const override;
time_t GetUpdateStamp() const override { time_t GetUpdateStamp() const noexcept override {
return 0; return 0;
} }

View File

@@ -36,7 +36,7 @@
#include <string.h> #include <string.h>
bool bool
ExcludeList::LoadFile(Path path_fs) ExcludeList::LoadFile(Path path_fs) noexcept
try { try {
#ifdef HAVE_CLASS_GLOB #ifdef HAVE_CLASS_GLOB
TextFile file(path_fs); TextFile file(path_fs);
@@ -67,7 +67,7 @@ try {
} }
bool bool
ExcludeList::Check(Path name_fs) const ExcludeList::Check(Path name_fs) const noexcept
{ {
assert(!name_fs.IsNull()); assert(!name_fs.IsNull());

View File

@@ -50,7 +50,7 @@ public:
:parent(&_parent) {} :parent(&_parent) {}
gcc_pure gcc_pure
bool IsEmpty() const { bool IsEmpty() const noexcept {
#ifdef HAVE_CLASS_GLOB #ifdef HAVE_CLASS_GLOB
return ((parent == nullptr) || parent->IsEmpty()) && patterns.empty(); return ((parent == nullptr) || parent->IsEmpty()) && patterns.empty();
#else #else
@@ -62,13 +62,13 @@ public:
/** /**
* Loads and parses a .mpdignore file. * Loads and parses a .mpdignore file.
*/ */
bool LoadFile(Path path_fs); bool LoadFile(Path path_fs) noexcept;
/** /**
* Checks whether one of the patterns in the .mpdignore file matches * Checks whether one of the patterns in the .mpdignore file matches
* the specified file name. * the specified file name.
*/ */
bool Check(Path name_fs) const; bool Check(Path name_fs) const noexcept;
}; };

View File

@@ -277,7 +277,7 @@ DecoderBridge::Ready(const AudioFormat audio_format,
} }
DecoderCommand DecoderCommand
DecoderBridge::GetCommand() DecoderBridge::GetCommand() noexcept
{ {
return LockGetVirtualCommand(); return LockGetVirtualCommand();
} }
@@ -326,7 +326,7 @@ DecoderBridge::CommandFinished()
} }
SongTime SongTime
DecoderBridge::GetSeekTime() DecoderBridge::GetSeekTime() noexcept
{ {
assert(dc.pipe != nullptr); assert(dc.pipe != nullptr);
@@ -341,7 +341,7 @@ DecoderBridge::GetSeekTime()
} }
uint64_t uint64_t
DecoderBridge::GetSeekFrame() DecoderBridge::GetSeekFrame() noexcept
{ {
return GetSeekTime().ToScale<uint64_t>(dc.in_audio_format.sample_rate); return GetSeekTime().ToScale<uint64_t>(dc.in_audio_format.sample_rate);
} }

View File

@@ -134,10 +134,10 @@ public:
/* virtual methods from DecoderClient */ /* virtual methods from DecoderClient */
void Ready(AudioFormat audio_format, void Ready(AudioFormat audio_format,
bool seekable, SignedSongTime duration) override; bool seekable, SignedSongTime duration) override;
DecoderCommand GetCommand() override; DecoderCommand GetCommand() noexcept override;
void CommandFinished() override; void CommandFinished() override;
SongTime GetSeekTime() override; SongTime GetSeekTime() noexcept override;
uint64_t GetSeekFrame() override; uint64_t GetSeekFrame() noexcept override;
void SeekError() override; void SeekError() override;
InputStreamPtr OpenUri(const char *uri) override; InputStreamPtr OpenUri(const char *uri) override;
size_t Read(InputStream &is, void *buffer, size_t length) override; size_t Read(InputStream &is, void *buffer, size_t length) override;

View File

@@ -58,7 +58,7 @@ public:
* command pending * command pending
*/ */
gcc_pure gcc_pure
virtual DecoderCommand GetCommand() = 0; virtual DecoderCommand GetCommand() noexcept = 0;
/** /**
* Called by the decoder when it has performed the requested command * Called by the decoder when it has performed the requested command
@@ -73,7 +73,7 @@ public:
* @return the destination position for the seek in milliseconds * @return the destination position for the seek in milliseconds
*/ */
gcc_pure gcc_pure
virtual SongTime GetSeekTime() = 0; virtual SongTime GetSeekTime() noexcept = 0;
/** /**
* Call this when you have received the DecoderCommand::SEEK command. * Call this when you have received the DecoderCommand::SEEK command.
@@ -81,7 +81,7 @@ public:
* @return the destination position for the seek in frames * @return the destination position for the seek in frames
*/ */
gcc_pure gcc_pure
virtual uint64_t GetSeekFrame() = 0; virtual uint64_t GetSeekFrame() noexcept = 0;
/** /**
* Call this instead of CommandFinished() when seeking has * Call this instead of CommandFinished() when seeking has

View File

@@ -54,11 +54,11 @@ public:
size_t _size) size_t _size)
:client(_client), is(_is), buffer(_size) {} :client(_client), is(_is), buffer(_size) {}
const InputStream &GetStream() const { const InputStream &GetStream() const noexcept {
return is; return is;
} }
void Clear() { void Clear() noexcept {
buffer.Clear(); buffer.Clear();
} }
@@ -75,7 +75,7 @@ public:
* How many bytes are stored in the buffer? * How many bytes are stored in the buffer?
*/ */
gcc_pure gcc_pure
size_t GetAvailable() const { size_t GetAvailable() const noexcept {
return buffer.GetAvailable(); return buffer.GetAvailable();
} }
@@ -84,7 +84,7 @@ public:
* you have to call Consume() to do that. The returned buffer * you have to call Consume() to do that. The returned buffer
* becomes invalid after a Fill() or a Consume() call. * becomes invalid after a Fill() or a Consume() call.
*/ */
ConstBuffer<void> Read() const { ConstBuffer<void> Read() const noexcept {
auto r = buffer.Read(); auto r = buffer.Read();
return { r.data, r.size }; return { r.data, r.size };
} }
@@ -102,7 +102,7 @@ public:
* *
* @param nbytes the number of bytes to consume * @param nbytes the number of bytes to consume
*/ */
void Consume(size_t nbytes) { void Consume(size_t nbytes) noexcept {
buffer.Consume(nbytes); buffer.Consume(nbytes);
} }

View File

@@ -227,29 +227,29 @@ struct DecoderControl {
} }
gcc_pure gcc_pure
bool LockIsIdle() const { bool LockIsIdle() const noexcept {
const std::lock_guard<Mutex> protect(mutex); const std::lock_guard<Mutex> protect(mutex);
return IsIdle(); return IsIdle();
} }
bool IsStarting() const { bool IsStarting() const noexcept {
return state == DecoderState::START; return state == DecoderState::START;
} }
gcc_pure gcc_pure
bool LockIsStarting() const { bool LockIsStarting() const noexcept {
const std::lock_guard<Mutex> protect(mutex); const std::lock_guard<Mutex> protect(mutex);
return IsStarting(); return IsStarting();
} }
bool HasFailed() const { bool HasFailed() const noexcept {
assert(command == DecoderCommand::NONE); assert(command == DecoderCommand::NONE);
return state == DecoderState::ERROR; return state == DecoderState::ERROR;
} }
gcc_pure gcc_pure
bool LockHasFailed() const { bool LockHasFailed() const noexcept {
const std::lock_guard<Mutex> protect(mutex); const std::lock_guard<Mutex> protect(mutex);
return HasFailed(); return HasFailed();
} }

View File

@@ -508,6 +508,7 @@ try {
decoder_run_song(dc, song, uri_utf8, path_fs); decoder_run_song(dc, song, uri_utf8, path_fs);
} catch (...) { } catch (...) {
dc.state = DecoderState::ERROR; dc.state = DecoderState::ERROR;
dc.command = DecoderCommand::NONE;
dc.error = std::current_exception(); dc.error = std::current_exception();
dc.client_cond.signal(); dc.client_cond.signal();
} }

View File

@@ -258,7 +258,7 @@ FfmpegSendFrame(DecoderClient &client, InputStream &is,
try { try {
output_buffer = copy_interleave_frame(codec_context, frame, output_buffer = copy_interleave_frame(codec_context, frame,
buffer); buffer);
} catch (const std::exception e) { } catch (const std::exception &e) {
/* this must be a serious error, e.g. OOM */ /* this must be a serious error, e.g. OOM */
LogError(e); LogError(e);
return DecoderCommand::STOP; return DecoderCommand::STOP;
@@ -712,7 +712,9 @@ FfmpegDecode(DecoderClient &client, InputStream &input,
#endif #endif
const SignedSongTime total_time = const SignedSongTime total_time =
FromFfmpegTimeChecked(av_stream.duration, av_stream.time_base); av_stream.duration != (int64_t)AV_NOPTS_VALUE
? FromFfmpegTimeChecked(av_stream.duration, av_stream.time_base)
: FromFfmpegTimeChecked(format_context.duration, AV_TIME_BASE_Q);
client.Ready(audio_format, input.IsSeekable(), total_time); client.Ready(audio_format, input.IsSeekable(), total_time);
@@ -842,6 +844,10 @@ FfmpegScanStream(AVFormatContext &format_context,
tag_handler_invoke_duration(handler, handler_ctx, tag_handler_invoke_duration(handler, handler_ctx,
FromFfmpegTime(stream.duration, FromFfmpegTime(stream.duration,
stream.time_base)); stream.time_base));
else if (format_context.duration != (int64_t)AV_NOPTS_VALUE)
tag_handler_invoke_duration(handler, handler_ctx,
FromFfmpegTime(format_context.duration,
AV_TIME_BASE_Q));
FfmpegScanMetadata(format_context, audio_stream, handler, handler_ctx); FfmpegScanMetadata(format_context, audio_stream, handler, handler_ctx);
@@ -881,7 +887,8 @@ ffmpeg_scan_stream(InputStream &is,
* more formats. * more formats.
*/ */
static const char *const ffmpeg_suffixes[] = { static const char *const ffmpeg_suffixes[] = {
"16sv", "3g2", "3gp", "4xm", "8svx", "aa3", "aac", "ac3", "afc", "aif", "16sv", "3g2", "3gp", "4xm", "8svx",
"aa3", "aac", "ac3", "adx", "afc", "aif",
"aifc", "aiff", "al", "alaw", "amr", "anim", "apc", "ape", "asf", "aifc", "aiff", "al", "alaw", "amr", "anim", "apc", "ape", "asf",
"atrac", "au", "aud", "avi", "avm2", "avs", "bap", "bfi", "c93", "cak", "atrac", "au", "aud", "avi", "avm2", "avs", "bap", "bfi", "c93", "cak",
"cin", "cmv", "cpk", "daud", "dct", "divx", "dts", "dv", "dvd", "dxa", "cin", "cmv", "cpk", "daud", "dct", "divx", "dts", "dv", "dvd", "dxa",
@@ -934,6 +941,7 @@ static const char *const ffmpeg_mime_types[] = {
"audio/x-16sv", "audio/x-16sv",
"audio/x-aac", "audio/x-aac",
"audio/x-ac3", "audio/x-ac3",
"audio/x-adx",
"audio/x-aiff" "audio/x-aiff"
"audio/x-alaw", "audio/x-alaw",
"audio/x-au", "audio/x-au",

View File

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

View File

@@ -42,25 +42,27 @@ public:
return chain; return chain;
} }
bool Read(const char *path) { bool Read(const char *path) noexcept {
return ::FLAC__metadata_chain_read(chain, path); return ::FLAC__metadata_chain_read(chain, path);
} }
bool Read(FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) { bool Read(FLAC__IOHandle handle,
FLAC__IOCallbacks callbacks) noexcept {
return ::FLAC__metadata_chain_read_with_callbacks(chain, return ::FLAC__metadata_chain_read_with_callbacks(chain,
handle, handle,
callbacks); callbacks);
} }
bool Read(InputStream &is) { bool Read(InputStream &is) noexcept {
return Read(::ToFlacIOHandle(is), ::GetFlacIOCallbacks(is)); return Read(::ToFlacIOHandle(is), ::GetFlacIOCallbacks(is));
} }
bool ReadOgg(const char *path) { bool ReadOgg(const char *path) noexcept {
return ::FLAC__metadata_chain_read_ogg(chain, path); return ::FLAC__metadata_chain_read_ogg(chain, path);
} }
bool ReadOgg(FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) { bool ReadOgg(FLAC__IOHandle handle,
FLAC__IOCallbacks callbacks) noexcept {
return ::FLAC__metadata_chain_read_ogg_with_callbacks(chain, return ::FLAC__metadata_chain_read_ogg_with_callbacks(chain,
handle, handle,
callbacks); callbacks);
@@ -71,12 +73,12 @@ public:
} }
gcc_pure gcc_pure
FLAC__Metadata_ChainStatus GetStatus() const { FLAC__Metadata_ChainStatus GetStatus() const noexcept {
return ::FLAC__metadata_chain_status(chain); return ::FLAC__metadata_chain_status(chain);
} }
gcc_pure gcc_pure
const char *GetStatusString() const { const char *GetStatusString() const noexcept {
return FLAC__Metadata_ChainStatusString[GetStatus()]; return FLAC__Metadata_ChainStatusString[GetStatus()];
} }
@@ -99,12 +101,12 @@ public:
::FLAC__metadata_iterator_delete(iterator); ::FLAC__metadata_iterator_delete(iterator);
} }
bool Next() { bool Next() noexcept {
return ::FLAC__metadata_iterator_next(iterator); return ::FLAC__metadata_iterator_next(iterator);
} }
gcc_pure gcc_pure
FLAC__StreamMetadata *GetBlock() { FLAC__StreamMetadata *GetBlock() noexcept {
return ::FLAC__metadata_iterator_get_block(iterator); return ::FLAC__metadata_iterator_get_block(iterator);
} }
}; };

View File

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

View File

@@ -52,14 +52,14 @@ static constexpr unsigned opus_output_buffer_frames = opus_sample_rate / 4;
gcc_pure gcc_pure
static bool static bool
IsOpusHead(const ogg_packet &packet) IsOpusHead(const ogg_packet &packet) noexcept
{ {
return packet.bytes >= 8 && memcmp(packet.packet, "OpusHead", 8) == 0; return packet.bytes >= 8 && memcmp(packet.packet, "OpusHead", 8) == 0;
} }
gcc_pure gcc_pure
static bool static bool
IsOpusTags(const ogg_packet &packet) IsOpusTags(const ogg_packet &packet) noexcept
{ {
return packet.bytes >= 8 && memcmp(packet.packet, "OpusTags", 8) == 0; return packet.bytes >= 8 && memcmp(packet.packet, "OpusTags", 8) == 0;
} }

View File

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

View File

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

View File

@@ -199,7 +199,7 @@ public:
* Are we currently running inside this EventLoop's thread? * Are we currently running inside this EventLoop's thread?
*/ */
gcc_pure gcc_pure
bool IsInside() const { bool IsInside() const noexcept {
assert(!thread.IsNull()); assert(!thread.IsNull());
return thread.IsInside(); return thread.IsInside();
@@ -207,7 +207,7 @@ public:
#ifndef NDEBUG #ifndef NDEBUG
gcc_pure gcc_pure
bool IsInsideOrVirgin() const { bool IsInsideOrVirgin() const noexcept {
return virgin || IsInside(); return virgin || IsInside();
} }
#endif #endif
@@ -219,7 +219,7 @@ public:
* are not yet/anymore handled. * are not yet/anymore handled.
*/ */
gcc_pure gcc_pure
bool IsInsideOrNull() const { bool IsInsideOrNull() const noexcept {
return thread.IsNull() || thread.IsInside(); return thread.IsNull() || thread.IsInside();
} }
}; };

View File

@@ -77,12 +77,12 @@ public:
* @see IsNull() * @see IsNull()
*/ */
gcc_const gcc_const
static AllocatedPath Null() { static AllocatedPath Null() noexcept {
return AllocatedPath(nullptr); return AllocatedPath(nullptr);
} }
gcc_pure gcc_pure
operator Path() const { operator Path() const noexcept {
return Path::FromFS(c_str()); return Path::FromFS(c_str());
} }
@@ -90,36 +90,39 @@ public:
* Join two path components with the path separator. * Join two path components with the path separator.
*/ */
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static AllocatedPath Build(const_pointer_type a, const_pointer_type b) { static AllocatedPath Build(const_pointer_type a,
const_pointer_type b) noexcept {
return Build(a, PathTraitsFS::GetLength(a), return Build(a, PathTraitsFS::GetLength(a),
b, PathTraitsFS::GetLength(b)); b, PathTraitsFS::GetLength(b));
} }
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static AllocatedPath Build(Path a, const_pointer_type b) { static AllocatedPath Build(Path a, const_pointer_type b) noexcept {
return Build(a.c_str(), b); return Build(a.c_str(), b);
} }
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static AllocatedPath Build(Path a, Path b) { static AllocatedPath Build(Path a, Path b) noexcept {
return Build(a, b.c_str()); return Build(a, b.c_str());
} }
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static AllocatedPath Build(const_pointer_type a, const AllocatedPath &b) { static AllocatedPath Build(const_pointer_type a,
const AllocatedPath &b) noexcept {
return Build(a, PathTraitsFS::GetLength(a), return Build(a, PathTraitsFS::GetLength(a),
b.value.c_str(), b.value.size()); b.value.c_str(), b.value.size());
} }
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static AllocatedPath Build(const AllocatedPath &a, const_pointer_type b) { static AllocatedPath Build(const AllocatedPath &a,
const_pointer_type b) noexcept {
return Build(a.value.c_str(), a.value.size(), return Build(a.value.c_str(), a.value.size(),
b, PathTraitsFS::GetLength(b)); b, PathTraitsFS::GetLength(b));
} }
gcc_pure gcc_pure
static AllocatedPath Build(const AllocatedPath &a, static AllocatedPath Build(const AllocatedPath &a,
const AllocatedPath &b) { const AllocatedPath &b) noexcept {
return Build(a.value.c_str(), a.value.size(), return Build(a.value.c_str(), a.value.size(),
b.value.c_str(), b.value.size()); b.value.c_str(), b.value.size());
} }
@@ -129,13 +132,13 @@ public:
* character set to a #Path instance. * character set to a #Path instance.
*/ */
gcc_pure gcc_pure
static AllocatedPath FromFS(const_pointer_type fs) { static AllocatedPath FromFS(const_pointer_type fs) noexcept {
return AllocatedPath(fs); return AllocatedPath(fs);
} }
gcc_pure gcc_pure
static AllocatedPath FromFS(const_pointer_type _begin, static AllocatedPath FromFS(const_pointer_type _begin,
const_pointer_type _end) { const_pointer_type _end) noexcept {
return AllocatedPath(_begin, _end); return AllocatedPath(_begin, _end);
} }
@@ -144,7 +147,7 @@ public:
* character set to a #Path instance. * character set to a #Path instance.
*/ */
gcc_pure gcc_pure
static AllocatedPath FromFS(string &&fs) { static AllocatedPath FromFS(string &&fs) noexcept {
return AllocatedPath(std::move(fs)); return AllocatedPath(std::move(fs));
} }
@@ -176,12 +179,12 @@ public:
} }
gcc_pure gcc_pure
bool operator==(const AllocatedPath &other) const { bool operator==(const AllocatedPath &other) const noexcept {
return value == other.value; return value == other.value;
} }
gcc_pure gcc_pure
bool operator!=(const AllocatedPath &other) const { bool operator!=(const AllocatedPath &other) const noexcept {
return value != other.value; return value != other.value;
} }
@@ -197,7 +200,7 @@ public:
* Check if this is a "nulled" instance. A "nulled" instance * Check if this is a "nulled" instance. A "nulled" instance
* must not be used. * must not be used.
*/ */
bool IsNull() const { bool IsNull() const noexcept {
return value.empty(); return value.empty();
} }
@@ -206,7 +209,7 @@ public:
* *
* @see IsNull() * @see IsNull()
*/ */
void SetNull() { void SetNull() noexcept {
value.clear(); value.clear();
} }
@@ -215,7 +218,7 @@ public:
* elements (which may not be the number of characters). * elements (which may not be the number of characters).
*/ */
gcc_pure gcc_pure
size_t length() const { size_t length() const noexcept {
return value.length(); return value.length();
} }
@@ -225,7 +228,7 @@ public:
* instance ends. * instance ends.
*/ */
gcc_pure gcc_pure
const_pointer_type c_str() const { const_pointer_type c_str() const noexcept {
return value.c_str(); return value.c_str();
} }
@@ -234,7 +237,7 @@ public:
* null-terminated. * null-terminated.
*/ */
gcc_pure gcc_pure
const_pointer_type data() const { const_pointer_type data() const noexcept {
return value.data(); return value.data();
} }

View File

@@ -54,7 +54,7 @@ public:
#endif #endif
gcc_pure gcc_pure
bool Check(const char *name_fs) const { bool Check(const char *name_fs) const noexcept {
#ifdef HAVE_FNMATCH #ifdef HAVE_FNMATCH
return fnmatch(pattern.c_str(), name_fs, 0) == 0; return fnmatch(pattern.c_str(), name_fs, 0) == 0;
#elif defined(WIN32) #elif defined(WIN32)

View File

@@ -92,7 +92,7 @@ public:
* elements (which may not be the number of characters). * elements (which may not be the number of characters).
*/ */
gcc_pure gcc_pure
size_t length() const { size_t length() const noexcept {
assert(!IsNull()); assert(!IsNull());
return PathTraitsFS::GetLength(c_str()); return PathTraitsFS::GetLength(c_str());
@@ -104,7 +104,7 @@ public:
* instance ends. * instance ends.
*/ */
gcc_pure gcc_pure
const_pointer_type c_str() const { const_pointer_type c_str() const noexcept {
return Base::c_str(); return Base::c_str();
} }
@@ -113,7 +113,7 @@ public:
* null-terminated. * null-terminated.
*/ */
gcc_pure gcc_pure
const_pointer_type data() const { const_pointer_type data() const noexcept {
return c_str(); return c_str();
} }

View File

@@ -63,7 +63,7 @@ struct PathTraitsFS {
static constexpr const_pointer_type CURRENT_DIRECTORY = PATH_LITERAL("."); static constexpr const_pointer_type CURRENT_DIRECTORY = PATH_LITERAL(".");
static constexpr bool IsSeparator(value_type ch) { static constexpr bool IsSeparator(value_type ch) noexcept {
return return
#ifdef WIN32 #ifdef WIN32
ch == '/' || ch == '/' ||
@@ -72,7 +72,7 @@ struct PathTraitsFS {
} }
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static const_pointer_type FindLastSeparator(const_pointer_type p) { static const_pointer_type FindLastSeparator(const_pointer_type p) noexcept {
#if !CLANG_CHECK_VERSION(3,6) #if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */ /* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr); assert(p != nullptr);
@@ -90,13 +90,13 @@ struct PathTraitsFS {
#ifdef WIN32 #ifdef WIN32
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static constexpr bool IsDrive(const_pointer_type p) { static constexpr bool IsDrive(const_pointer_type p) noexcept {
return IsAlphaASCII(p[0]) && p[1] == ':'; return IsAlphaASCII(p[0]) && p[1] == ':';
} }
#endif #endif
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static bool IsAbsolute(const_pointer_type p) { static bool IsAbsolute(const_pointer_type p) noexcept {
#if !CLANG_CHECK_VERSION(3,6) #if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */ /* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr); assert(p != nullptr);
@@ -110,12 +110,12 @@ struct PathTraitsFS {
} }
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static size_t GetLength(const_pointer_type p) { static size_t GetLength(const_pointer_type p) noexcept {
return StringLength(p); return StringLength(p);
} }
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static const_pointer_type Find(const_pointer_type p, value_type ch) { static const_pointer_type Find(const_pointer_type p, value_type ch) noexcept {
return StringFind(p, ch); return StringFind(p, ch);
} }
@@ -179,7 +179,7 @@ struct PathTraitsUTF8 {
} }
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static const_pointer_type FindLastSeparator(const_pointer_type p) { static const_pointer_type FindLastSeparator(const_pointer_type p) noexcept {
#if !CLANG_CHECK_VERSION(3,6) #if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */ /* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr); assert(p != nullptr);
@@ -190,13 +190,13 @@ struct PathTraitsUTF8 {
#ifdef WIN32 #ifdef WIN32
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static constexpr bool IsDrive(const_pointer_type p) { static constexpr bool IsDrive(const_pointer_type p) noexcept {
return IsAlphaASCII(p[0]) && p[1] == ':'; return IsAlphaASCII(p[0]) && p[1] == ':';
} }
#endif #endif
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static bool IsAbsolute(const_pointer_type p) { static bool IsAbsolute(const_pointer_type p) noexcept {
#if !CLANG_CHECK_VERSION(3,6) #if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */ /* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr); assert(p != nullptr);
@@ -210,12 +210,12 @@ struct PathTraitsUTF8 {
} }
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static size_t GetLength(const_pointer_type p) { static size_t GetLength(const_pointer_type p) noexcept {
return StringLength(p); return StringLength(p);
} }
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static const_pointer_type Find(const_pointer_type p, value_type ch) { static const_pointer_type Find(const_pointer_type p, value_type ch) noexcept {
return StringFind(p, ch); return StringFind(p, ch);
} }
@@ -255,7 +255,7 @@ struct PathTraitsUTF8 {
const_pointer_type b, size_t b_size) noexcept; const_pointer_type b, size_t b_size) noexcept;
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static string Build(const_pointer_type a, const_pointer_type b) { static string Build(const_pointer_type a, const_pointer_type b) noexcept {
return Build(a, GetLength(a), b, GetLength(b)); return Build(a, GetLength(a), b, GetLength(b));
} }
}; };

View File

@@ -67,7 +67,7 @@ BufferedReader::ReadFull(size_t size)
} }
size_t size_t
BufferedReader::ReadFromBuffer(WritableBuffer<void> dest) BufferedReader::ReadFromBuffer(WritableBuffer<void> dest) noexcept
{ {
auto src = Read(); auto src = Read();
size_t nbytes = std::min(src.size, dest.size); size_t nbytes = std::min(src.size, dest.size);

View File

@@ -47,7 +47,7 @@ public:
* Reset the internal state. Should be called after rewinding * Reset the internal state. Should be called after rewinding
* the underlying #Reader. * the underlying #Reader.
*/ */
void Reset() { void Reset() noexcept {
buffer.Clear(); buffer.Clear();
eof = false; eof = false;
line_number = 0; line_number = 0;
@@ -56,7 +56,7 @@ public:
bool Fill(bool need_more); bool Fill(bool need_more);
gcc_pure gcc_pure
WritableBuffer<void> Read() const { WritableBuffer<void> Read() const noexcept {
return buffer.Read().ToVoid(); return buffer.Read().ToVoid();
} }
@@ -67,7 +67,7 @@ public:
*/ */
void *ReadFull(size_t size); void *ReadFull(size_t size);
void Consume(size_t n) { void Consume(size_t n) noexcept {
buffer.Consume(n); buffer.Consume(n);
} }
@@ -75,7 +75,7 @@ public:
* Read (and consume) data from the input buffer into the * Read (and consume) data from the input buffer into the
* given buffer. Does not attempt to refill the buffer. * given buffer. Does not attempt to refill the buffer.
*/ */
size_t ReadFromBuffer(WritableBuffer<void> dest); size_t ReadFromBuffer(WritableBuffer<void> dest) noexcept;
/** /**
* Read data into the given buffer and consume it from our * Read data into the given buffer and consume it from our
@@ -86,7 +86,7 @@ public:
char *ReadLine(); char *ReadLine();
unsigned GetLineNumber() const { unsigned GetLineNumber() const noexcept {
return line_number; return line_number;
} }
}; };

View File

@@ -52,14 +52,14 @@ AsyncInputStream::~AsyncInputStream()
} }
void void
AsyncInputStream::SetTag(Tag *_tag) AsyncInputStream::SetTag(Tag *_tag) noexcept
{ {
delete tag; delete tag;
tag = _tag; tag = _tag;
} }
void void
AsyncInputStream::Pause() AsyncInputStream::Pause() noexcept
{ {
assert(io_thread_inside()); assert(io_thread_inside());
@@ -141,7 +141,7 @@ AsyncInputStream::Seek(offset_type new_offset)
} }
void void
AsyncInputStream::SeekDone() AsyncInputStream::SeekDone() noexcept
{ {
assert(io_thread_inside()); assert(io_thread_inside());
assert(IsSeekPending()); assert(IsSeekPending());
@@ -201,7 +201,7 @@ AsyncInputStream::Read(void *ptr, size_t read_size)
} }
void void
AsyncInputStream::CommitWriteBuffer(size_t nbytes) AsyncInputStream::CommitWriteBuffer(size_t nbytes) noexcept
{ {
buffer.Append(nbytes); buffer.Append(nbytes);
@@ -212,7 +212,7 @@ AsyncInputStream::CommitWriteBuffer(size_t nbytes)
} }
void void
AsyncInputStream::AppendToBuffer(const void *data, size_t append_size) AsyncInputStream::AppendToBuffer(const void *data, size_t append_size) noexcept
{ {
auto w = buffer.Write(); auto w = buffer.Write();
assert(!w.IsEmpty()); assert(!w.IsEmpty());
@@ -238,7 +238,7 @@ AsyncInputStream::AppendToBuffer(const void *data, size_t append_size)
} }
void void
AsyncInputStream::DeferredResume() AsyncInputStream::DeferredResume() noexcept
{ {
const std::lock_guard<Mutex> protect(mutex); const std::lock_guard<Mutex> protect(mutex);
@@ -251,7 +251,7 @@ AsyncInputStream::DeferredResume()
} }
void void
AsyncInputStream::DeferredSeek() AsyncInputStream::DeferredSeek() noexcept
{ {
const std::lock_guard<Mutex> protect(mutex); const std::lock_guard<Mutex> protect(mutex);
if (seek_state != SeekState::SCHEDULED) if (seek_state != SeekState::SCHEDULED)

View File

@@ -92,15 +92,15 @@ protected:
/** /**
* Pass an tag from the I/O thread to the client thread. * Pass an tag from the I/O thread to the client thread.
*/ */
void SetTag(Tag *_tag); void SetTag(Tag *_tag) noexcept;
void ClearTag() { void ClearTag() noexcept {
SetTag(nullptr); SetTag(nullptr);
} }
void Pause(); void Pause() noexcept;
bool IsPaused() const { bool IsPaused() const noexcept {
return paused; return paused;
} }
@@ -109,15 +109,15 @@ protected:
* continue feeding Read() calls from the buffer until it runs * continue feeding Read() calls from the buffer until it runs
* empty. * empty.
*/ */
void SetClosed() { void SetClosed() noexcept {
open = false; open = false;
} }
bool IsBufferEmpty() const { bool IsBufferEmpty() const noexcept {
return buffer.IsEmpty(); return buffer.IsEmpty();
} }
bool IsBufferFull() const { bool IsBufferFull() const noexcept {
return buffer.IsFull(); return buffer.IsFull();
} }
@@ -125,21 +125,21 @@ protected:
* Determine how many bytes can be added to the buffer. * Determine how many bytes can be added to the buffer.
*/ */
gcc_pure gcc_pure
size_t GetBufferSpace() const { size_t GetBufferSpace() const noexcept {
return buffer.GetSpace(); return buffer.GetSpace();
} }
CircularBuffer<uint8_t>::Range PrepareWriteBuffer() { CircularBuffer<uint8_t>::Range PrepareWriteBuffer() noexcept {
return buffer.Write(); return buffer.Write();
} }
void CommitWriteBuffer(size_t nbytes); void CommitWriteBuffer(size_t nbytes) noexcept;
/** /**
* Append data to the buffer. The size must fit into the * Append data to the buffer. The size must fit into the
* buffer; see GetBufferSpace(). * buffer; see GetBufferSpace().
*/ */
void AppendToBuffer(const void *data, size_t append_size); void AppendToBuffer(const void *data, size_t append_size) noexcept;
/** /**
* Implement code here that will resume the stream after it * Implement code here that will resume the stream after it
@@ -154,7 +154,7 @@ protected:
*/ */
virtual void DoSeek(offset_type new_offset) = 0; virtual void DoSeek(offset_type new_offset) = 0;
bool IsSeekPending() const { bool IsSeekPending() const noexcept {
return seek_state == SeekState::PENDING; return seek_state == SeekState::PENDING;
} }
@@ -162,14 +162,14 @@ protected:
* Call this after seeking has finished. It will notify the * Call this after seeking has finished. It will notify the
* client thread. * client thread.
*/ */
void SeekDone(); void SeekDone() noexcept;
private: private:
void Resume(); void Resume();
/* for DeferredCall */ /* for DeferredCall */
void DeferredResume(); void DeferredResume() noexcept;
void DeferredSeek(); void DeferredSeek() noexcept;
}; };
#endif #endif

View File

@@ -184,20 +184,20 @@ public:
void LockWaitReady(); void LockWaitReady();
gcc_pure gcc_pure
bool HasMimeType() const { bool HasMimeType() const noexcept {
assert(ready); assert(ready);
return !mime.empty(); return !mime.empty();
} }
gcc_pure gcc_pure
const char *GetMimeType() const { const char *GetMimeType() const noexcept {
assert(ready); assert(ready);
return mime.empty() ? nullptr : mime.c_str(); return mime.empty() ? nullptr : mime.c_str();
} }
void ClearMimeType() { void ClearMimeType() noexcept {
mime.clear(); mime.clear();
} }
@@ -215,35 +215,35 @@ public:
} }
gcc_pure gcc_pure
bool KnownSize() const { bool KnownSize() const noexcept {
assert(ready); assert(ready);
return size != UNKNOWN_SIZE; return size != UNKNOWN_SIZE;
} }
gcc_pure gcc_pure
offset_type GetSize() const { offset_type GetSize() const noexcept {
assert(ready); assert(ready);
assert(KnownSize()); assert(KnownSize());
return size; return size;
} }
void AddOffset(offset_type delta) { void AddOffset(offset_type delta) noexcept {
assert(ready); assert(ready);
offset += delta; offset += delta;
} }
gcc_pure gcc_pure
offset_type GetOffset() const { offset_type GetOffset() const noexcept {
assert(ready); assert(ready);
return offset; return offset;
} }
gcc_pure gcc_pure
offset_type GetRest() const { offset_type GetRest() const noexcept {
assert(ready); assert(ready);
assert(KnownSize()); assert(KnownSize());
@@ -251,7 +251,7 @@ public:
} }
gcc_pure gcc_pure
bool IsSeekable() const { bool IsSeekable() const noexcept {
assert(ready); assert(ready);
return seekable; return seekable;

View File

@@ -270,7 +270,10 @@ CdioParanoiaInputStream::Seek(offset_type new_offset)
lsn_relofs = new_offset / CDIO_CD_FRAMESIZE_RAW; lsn_relofs = new_offset / CDIO_CD_FRAMESIZE_RAW;
offset = new_offset; offset = new_offset;
cdio_paranoia_seek(para, lsn_from + lsn_relofs, SEEK_SET); {
const ScopeUnlock unlock(mutex);
cdio_paranoia_seek(para, lsn_from + lsn_relofs, SEEK_SET);
}
} }
size_t size_t
@@ -292,6 +295,8 @@ CdioParanoiaInputStream::Read(void *ptr, size_t length)
//current sector was changed ? //current sector was changed ?
if (lsn_relofs != buffer_lsn) { if (lsn_relofs != buffer_lsn) {
const ScopeUnlock unlock(mutex);
rbuf = cdio_paranoia_read(para, nullptr); rbuf = cdio_paranoia_read(para, nullptr);
s_err = cdda_errors(drv); s_err = cdda_errors(drv);

View File

@@ -64,7 +64,6 @@ static const size_t CURL_RESUME_AT = 384 * 1024;
struct CurlInputStream final : public AsyncInputStream, CurlResponseHandler { struct CurlInputStream final : public AsyncInputStream, CurlResponseHandler {
/* some buffers which were passed to libcurl, which we have /* some buffers which were passed to libcurl, which we have
too free */ too free */
char range[32];
CurlSlist request_headers; CurlSlist request_headers;
CurlRequest *request = nullptr; CurlRequest *request = nullptr;
@@ -86,8 +85,19 @@ struct CurlInputStream final : public AsyncInputStream, CurlResponseHandler {
static InputStream *Open(const char *url, Mutex &mutex, Cond &cond); static InputStream *Open(const char *url, Mutex &mutex, Cond &cond);
/**
* Create and initialize a new #CurlRequest instance. After
* this, you may add more request headers and set options. To
* actually start the request, call StartRequest().
*/
void InitEasy(); void InitEasy();
/**
* Start the request after having called InitEasy(). After
* this, you must not set any CURL options.
*/
void StartRequest();
/** /**
* Frees the current "libcurl easy" handle, and everything * Frees the current "libcurl easy" handle, and everything
* associated with it. * associated with it.
@@ -372,6 +382,11 @@ CurlInputStream::InitEasy()
request_headers.Clear(); request_headers.Clear();
request_headers.Append("Icy-Metadata: 1"); request_headers.Append("Icy-Metadata: 1");
}
void
CurlInputStream::StartRequest()
{
request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get()); request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get());
request->Start(); request->Start();
@@ -398,6 +413,7 @@ CurlInputStream::SeekInternal(offset_type new_offset)
/* send the "Range" header */ /* send the "Range" header */
if (offset > 0) { if (offset > 0) {
char range[32];
#ifdef WIN32 #ifdef WIN32
// TODO: what can we use on Windows to format 64 bit? // TODO: what can we use on Windows to format 64 bit?
sprintf(range, "%lu-", (long)offset); sprintf(range, "%lu-", (long)offset);
@@ -406,6 +422,8 @@ CurlInputStream::SeekInternal(offset_type new_offset)
#endif #endif
request->SetOption(CURLOPT_RANGE, range); request->SetOption(CURLOPT_RANGE, range);
} }
StartRequest();
} }
void void
@@ -428,6 +446,7 @@ CurlInputStream::Open(const char *url, Mutex &mutex, Cond &cond)
try { try {
BlockingCall(io_thread_get(), [c](){ BlockingCall(io_thread_get(), [c](){
c->InitEasy(); c->InitEasy();
c->StartRequest();
}); });
} catch (...) { } catch (...) {
delete c; delete c;

View File

@@ -104,7 +104,13 @@ input_ffmpeg_open(const char *uri,
size_t size_t
FfmpegInputStream::Read(void *ptr, size_t read_size) FfmpegInputStream::Read(void *ptr, size_t read_size)
{ {
auto result = avio_read(h, (unsigned char *)ptr, read_size); int result;
{
const ScopeUnlock unlock(mutex);
result = avio_read(h, (unsigned char *)ptr, read_size);
}
if (result <= 0) { if (result <= 0) {
if (result < 0) if (result < 0)
throw MakeFfmpegError(result, "avio_read() failed"); throw MakeFfmpegError(result, "avio_read() failed");
@@ -126,7 +132,12 @@ FfmpegInputStream::IsEOF() noexcept
void void
FfmpegInputStream::Seek(offset_type new_offset) FfmpegInputStream::Seek(offset_type new_offset)
{ {
auto result = avio_seek(h, new_offset, SEEK_SET); int64_t result;
{
const ScopeUnlock unlock(mutex);
result = avio_seek(h, new_offset, SEEK_SET);
}
if (result < 0) if (result < 0)
throw MakeFfmpegError(result, "avio_seek() failed"); throw MakeFfmpegError(result, "avio_seek() failed");

View File

@@ -87,14 +87,24 @@ input_file_open(gcc_unused const char *filename,
void void
FileInputStream::Seek(offset_type new_offset) FileInputStream::Seek(offset_type new_offset)
{ {
reader.Seek((off_t)new_offset); {
const ScopeUnlock unlock(mutex);
reader.Seek((off_t)new_offset);
}
offset = new_offset; offset = new_offset;
} }
size_t size_t
FileInputStream::Read(void *ptr, size_t read_size) FileInputStream::Read(void *ptr, size_t read_size)
{ {
size_t nbytes = reader.Read(ptr, read_size); size_t nbytes;
{
const ScopeUnlock unlock(mutex);
nbytes = reader.Read(ptr, read_size);
}
offset += nbytes; offset += nbytes;
return nbytes; return nbytes;
} }

View File

@@ -125,9 +125,14 @@ input_smbclient_open(const char *uri,
size_t size_t
SmbclientInputStream::Read(void *ptr, size_t read_size) SmbclientInputStream::Read(void *ptr, size_t read_size)
{ {
smbclient_mutex.lock(); ssize_t nbytes;
ssize_t nbytes = smbc_read(fd, ptr, read_size);
smbclient_mutex.unlock(); {
const ScopeUnlock unlock(mutex);
const std::lock_guard<Mutex> lock(smbclient_mutex);
nbytes = smbc_read(fd, ptr, read_size);
}
if (nbytes < 0) if (nbytes < 0)
throw MakeErrno("smbc_read() failed"); throw MakeErrno("smbc_read() failed");
@@ -138,9 +143,14 @@ SmbclientInputStream::Read(void *ptr, size_t read_size)
void void
SmbclientInputStream::Seek(offset_type new_offset) SmbclientInputStream::Seek(offset_type new_offset)
{ {
smbclient_mutex.lock(); off_t result;
off_t result = smbc_lseek(fd, new_offset, SEEK_SET);
smbclient_mutex.unlock(); {
const ScopeUnlock unlock(mutex);
const std::lock_guard<Mutex> lock(smbclient_mutex);
result = smbc_lseek(fd, new_offset, SEEK_SET);
}
if (result < 0) if (result < 0)
throw MakeErrno("smbc_lseek() failed"); throw MakeErrno("smbc_lseek() failed");

View File

@@ -63,7 +63,7 @@ public:
*/ */
static int SocketFunction(CURL *easy, static int SocketFunction(CURL *easy,
curl_socket_t s, int action, curl_socket_t s, int action,
void *userp, void *socketp); void *userp, void *socketp) noexcept;
virtual bool OnSocketReady(unsigned flags) override; virtual bool OnSocketReady(unsigned flags) override;
@@ -75,7 +75,7 @@ private:
} }
gcc_const gcc_const
static unsigned CurlPollToFlags(int action) { static unsigned CurlPollToFlags(int action) noexcept {
switch (action) { switch (action) {
case CURL_POLL_NONE: case CURL_POLL_NONE:
return 0; return 0;
@@ -108,7 +108,7 @@ CurlGlobal::CurlGlobal(EventLoop &_loop)
int int
CurlSocket::SocketFunction(gcc_unused CURL *easy, CurlSocket::SocketFunction(gcc_unused CURL *easy,
curl_socket_t s, int action, curl_socket_t s, int action,
void *userp, void *socketp) { void *userp, void *socketp) noexcept {
auto &global = *(CurlGlobal *)userp; auto &global = *(CurlGlobal *)userp;
CurlSocket *cs = (CurlSocket *)socketp; CurlSocket *cs = (CurlSocket *)socketp;

102
src/lib/icu/CaseFold.cxx Normal file
View File

@@ -0,0 +1,102 @@
/*
* Copyright 2003-2017 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "CaseFold.hxx"
#ifdef HAVE_ICU_CASE_FOLD
#include "util/AllocatedString.hxx"
#ifdef HAVE_ICU
#include "Util.hxx"
#include "util/AllocatedArray.hxx"
#include "util/ConstBuffer.hxx"
#include <unicode/ucol.h>
#include <unicode/ustring.h>
#else
#include <algorithm>
#include <ctype.h>
#endif
#ifdef WIN32
#include "Win32.hxx"
#include <windows.h>
#endif
#include <memory>
#include <stdexcept>
#include <assert.h>
#include <string.h>
AllocatedString<>
IcuCaseFold(const char *src) noexcept
try {
#ifdef HAVE_ICU
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(src != nullptr);
#endif
const auto u = UCharFromUTF8(src);
if (u.IsNull())
return AllocatedString<>::Duplicate(src);
AllocatedArray<UChar> folded(u.size() * 2u);
UErrorCode error_code = U_ZERO_ERROR;
size_t folded_length = u_strFoldCase(folded.begin(), folded.size(),
u.begin(), u.size(),
U_FOLD_CASE_DEFAULT,
&error_code);
if (folded_length == 0 || error_code != U_ZERO_ERROR)
return AllocatedString<>::Duplicate(src);
folded.SetSize(folded_length);
return UCharToUTF8({folded.begin(), folded.size()});
#elif defined(WIN32)
const auto u = MultiByteToWideChar(CP_UTF8, src);
const int size = LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, nullptr, 0,
nullptr, nullptr, 0);
if (size <= 0)
return AllocatedString<>::Duplicate(src);
std::unique_ptr<wchar_t[]> buffer(new wchar_t[size]);
if (LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, buffer.get(), size,
nullptr, nullptr, 0) <= 0)
return AllocatedString<>::Duplicate(src);
return WideCharToMultiByte(CP_UTF8, buffer.get());
#else
#error not implemented
#endif
} catch (const std::runtime_error &) {
return AllocatedString<>::Duplicate(src);
}
#endif /* HAVE_ICU_CASE_FOLD */

38
src/lib/icu/CaseFold.hxx Normal file
View File

@@ -0,0 +1,38 @@
/*
* Copyright 2003-2017 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_ICU_CASE_FOLD_HXX
#define MPD_ICU_CASE_FOLD_HXX
#include "check.h"
#if defined(HAVE_ICU) || defined(_WIN32)
#define HAVE_ICU_CASE_FOLD
#include "Compiler.h"
template<typename T> class AllocatedString;
gcc_nonnull_all
AllocatedString<char>
IcuCaseFold(const char *src) noexcept;
#endif
#endif

View File

@@ -23,8 +23,6 @@
#ifdef HAVE_ICU #ifdef HAVE_ICU
#include "Util.hxx" #include "Util.hxx"
#include "util/AllocatedArray.hxx"
#include "util/ConstBuffer.hxx"
#include "util/RuntimeError.hxx" #include "util/RuntimeError.hxx"
#include <unicode/ucol.h> #include <unicode/ucol.h>
@@ -141,70 +139,3 @@ IcuCollate(const char *a, const char *b) noexcept
return strcoll(a, b); return strcoll(a, b);
#endif #endif
} }
AllocatedString<>
IcuCaseFold(const char *src)
try {
#ifdef HAVE_ICU
assert(collator != nullptr);
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(src != nullptr);
#endif
const auto u = UCharFromUTF8(src);
if (u.IsNull())
return AllocatedString<>::Duplicate(src);
AllocatedArray<UChar> folded(u.size() * 2u);
UErrorCode error_code = U_ZERO_ERROR;
size_t folded_length = u_strFoldCase(folded.begin(), folded.size(),
u.begin(), u.size(),
U_FOLD_CASE_DEFAULT,
&error_code);
if (folded_length == 0 || error_code != U_ZERO_ERROR)
return AllocatedString<>::Duplicate(src);
folded.SetSize(folded_length);
return UCharToUTF8({folded.begin(), folded.size()});
#elif defined(WIN32)
const auto u = MultiByteToWideChar(CP_UTF8, src);
const int size = LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, nullptr, 0,
nullptr, nullptr, 0);
if (size <= 0)
return AllocatedString<>::Duplicate(src);
std::unique_ptr<wchar_t[]> buffer(new wchar_t[size]);
if (LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, buffer.get(), size,
nullptr, nullptr, 0) <= 0)
return AllocatedString<>::Duplicate(src);
return WideCharToMultiByte(CP_UTF8, buffer.get());
#else
size_t size = strlen(src) + 1;
std::unique_ptr<char[]> buffer(new char[size]);
size_t nbytes = strxfrm(buffer.get(), src, size);
if (nbytes >= size) {
/* buffer too small - reallocate and try again */
buffer.reset();
size = nbytes + 1;
buffer.reset(new char[size]);
nbytes = strxfrm(buffer.get(), src, size);
}
assert(nbytes < size);
assert(buffer[nbytes] == 0);
return AllocatedString<>::Donate(buffer.release());
#endif
} catch (const std::runtime_error &) {
return AllocatedString<>::Duplicate(src);
}

View File

@@ -23,8 +23,6 @@
#include "check.h" #include "check.h"
#include "Compiler.h" #include "Compiler.h"
template<typename T> class AllocatedString;
/** /**
* Throws #std::runtime_error on error. * Throws #std::runtime_error on error.
*/ */
@@ -38,8 +36,4 @@ gcc_pure gcc_nonnull_all
int int
IcuCollate(const char *a, const char *b) noexcept; IcuCollate(const char *a, const char *b) noexcept;
gcc_nonnull_all
AllocatedString<char>
IcuCaseFold(const char *src);
#endif #endif

66
src/lib/icu/Compare.cxx Normal file
View File

@@ -0,0 +1,66 @@
/*
* Copyright 2003-2017 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "Compare.hxx"
#include "CaseFold.hxx"
#include "util/StringAPI.hxx"
#include <string.h>
#ifdef HAVE_ICU_CASE_FOLD
IcuCompare::IcuCompare(const char *_needle) noexcept
:needle(IcuCaseFold(_needle)) {}
#else
IcuCompare::IcuCompare(const char *_needle) noexcept
:needle(AllocatedString<>::Duplicate(_needle)) {}
#endif
bool
IcuCompare::operator==(const char *haystack) const noexcept
{
#ifdef HAVE_ICU_CASE_FOLD
return StringIsEqual(IcuCaseFold(haystack).c_str(), needle.c_str());
#else
return strcasecmp(haystack, needle.c_str());
#endif
}
bool
IcuCompare::IsIn(const char *haystack) const noexcept
{
#ifdef HAVE_ICU_CASE_FOLD
return StringFind(IcuCaseFold(haystack).c_str(),
needle.c_str()) != nullptr;
#elif defined(HAVE_STRCASESTR)
return strcasestr(haystack, needle.c_str()) != nullptr;
#else
/* poor man's strcasestr() */
for (const size_t length = strlen(needle.c_str());
*haystack != 0; ++haystack)
if (strncasecmp(haystack, needle.c_str(), length) == 0)
return true;
return false;
#endif
}

55
src/lib/icu/Compare.hxx Normal file
View File

@@ -0,0 +1,55 @@
/*
* Copyright 2003-2017 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_ICU_COMPARE_HXX
#define MPD_ICU_COMPARE_HXX
#include "check.h"
#include "Compiler.h"
#include "util/AllocatedString.hxx"
/**
* This class can compare one string ("needle") with lots of other
* strings ("haystacks") efficiently, ignoring case. With some
* configurations, it can prepare a case-folded version of the needle.
*/
class IcuCompare {
AllocatedString<> needle;
public:
IcuCompare():needle(nullptr) {}
explicit IcuCompare(const char *needle) noexcept;
IcuCompare(IcuCompare &&) = default;
IcuCompare &operator=(IcuCompare &&) = default;
gcc_pure
operator bool() const noexcept {
return !needle.IsNull();
}
gcc_pure
bool operator==(const char *haystack) const noexcept;
gcc_pure
bool IsIn(const char *haystack) const noexcept;
};
#endif

View File

@@ -147,16 +147,16 @@ public:
~NfsConnection(); ~NfsConnection();
gcc_pure gcc_pure
const char *GetServer() const { const char *GetServer() const noexcept {
return server.c_str(); return server.c_str();
} }
gcc_pure gcc_pure
const char *GetExportName() const { const char *GetExportName() const noexcept {
return export_name.c_str(); return export_name.c_str();
} }
EventLoop &GetEventLoop() { EventLoop &GetEventLoop() noexcept {
return SocketMonitor::GetEventLoop(); return SocketMonitor::GetEventLoop();
} }

View File

@@ -40,7 +40,7 @@ public:
return *(UpnpCallback *)cookie; return *(UpnpCallback *)cookie;
} }
virtual int Invoke(Upnp_EventType et, void *evp) = 0; virtual int Invoke(Upnp_EventType et, const void *evp) = 0;
}; };
#endif #endif

View File

@@ -33,7 +33,12 @@ static unsigned upnp_client_ref;
static UpnpClient_Handle upnp_client_handle; static UpnpClient_Handle upnp_client_handle;
static int static int
UpnpClientCallback(Upnp_EventType et, void *evp, void *cookie) UpnpClientCallback(Upnp_EventType et,
#if UPNP_VERSION >= 10800
const
#endif
void *evp,
void *cookie)
{ {
if (cookie == nullptr) if (cookie == nullptr)
/* this is the cookie passed to UpnpRegisterClient(); /* this is the cookie passed to UpnpRegisterClient();

69
src/lib/upnp/Compat.hxx Normal file
View File

@@ -0,0 +1,69 @@
/*
* Copyright 2003-2017 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_UPNP_COMPAT_HXX
#define MPD_UPNP_COMPAT_HXX
#include <upnp/upnp.h>
#if UPNP_VERSION < 10800
#include "Compiler.h"
/* emulate the libupnp 1.8 API with older versions */
using UpnpDiscovery = Upnp_Discovery;
gcc_pure
static inline int
UpnpDiscovery_get_Expires(const UpnpDiscovery *disco) noexcept
{
return disco->Expires;
}
gcc_pure
static inline const char *
UpnpDiscovery_get_DeviceID_cstr(const UpnpDiscovery *disco) noexcept
{
return disco->DeviceId;
}
gcc_pure
static inline const char *
UpnpDiscovery_get_DeviceType_cstr(const UpnpDiscovery *disco) noexcept
{
return disco->DeviceType;
}
gcc_pure
static inline const char *
UpnpDiscovery_get_ServiceType_cstr(const UpnpDiscovery *disco) noexcept
{
return disco->ServiceType;
}
gcc_pure
static inline const char *
UpnpDiscovery_get_Location_cstr(const UpnpDiscovery *disco) noexcept
{
return disco->Location;
}
#endif
#endif

View File

@@ -114,12 +114,12 @@ public:
std::list<std::string> getSearchCapabilities(UpnpClient_Handle handle) const; std::list<std::string> getSearchCapabilities(UpnpClient_Handle handle) const;
gcc_pure gcc_pure
std::string GetURI() const { std::string GetURI() const noexcept {
return "upnp://" + m_deviceId + "/" + m_serviceType; return "upnp://" + m_deviceId + "/" + m_serviceType;
} }
/** Retrieve the "friendly name" for this server, useful for display. */ /** Retrieve the "friendly name" for this server, useful for display. */
const char *getFriendlyName() const { const char *getFriendlyName() const noexcept {
return m_friendlyName.c_str(); return m_friendlyName.c_str();
} }
}; };

View File

@@ -153,10 +153,10 @@ UPnPDeviceDirectory::Explore(void *ctx)
} }
inline int inline int
UPnPDeviceDirectory::OnAlive(Upnp_Discovery *disco) UPnPDeviceDirectory::OnAlive(const UpnpDiscovery *disco)
{ {
if (isMSDevice(disco->DeviceType) || if (isMSDevice(UpnpDiscovery_get_DeviceType_cstr(disco)) ||
isCDService(disco->ServiceType)) { isCDService(UpnpDiscovery_get_ServiceType_cstr(disco))) {
DiscoveredTask *tp = new DiscoveredTask(disco); DiscoveredTask *tp = new DiscoveredTask(disco);
if (queue.put(tp)) if (queue.put(tp))
return UPNP_E_FINISH; return UPNP_E_FINISH;
@@ -166,12 +166,12 @@ UPnPDeviceDirectory::OnAlive(Upnp_Discovery *disco)
} }
inline int inline int
UPnPDeviceDirectory::OnByeBye(Upnp_Discovery *disco) UPnPDeviceDirectory::OnByeBye(const UpnpDiscovery *disco)
{ {
if (isMSDevice(disco->DeviceType) || if (isMSDevice(UpnpDiscovery_get_DeviceType_cstr(disco)) ||
isCDService(disco->ServiceType)) { isCDService(UpnpDiscovery_get_ServiceType_cstr(disco))) {
// Device signals it is going off. // Device signals it is going off.
LockRemove(disco->DeviceId); LockRemove(UpnpDiscovery_get_DeviceID_cstr(disco));
} }
return UPNP_E_SUCCESS; return UPNP_E_SUCCESS;
@@ -182,19 +182,19 @@ UPnPDeviceDirectory::OnByeBye(Upnp_Discovery *disco)
// Example: ContentDirectories appearing and disappearing from the network // Example: ContentDirectories appearing and disappearing from the network
// We queue a task for our worker thread(s) // We queue a task for our worker thread(s)
int int
UPnPDeviceDirectory::Invoke(Upnp_EventType et, void *evp) UPnPDeviceDirectory::Invoke(Upnp_EventType et, const void *evp)
{ {
switch (et) { switch (et) {
case UPNP_DISCOVERY_SEARCH_RESULT: case UPNP_DISCOVERY_SEARCH_RESULT:
case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
{ {
Upnp_Discovery *disco = (Upnp_Discovery *)evp; auto *disco = (const UpnpDiscovery *)evp;
return OnAlive(disco); return OnAlive(disco);
} }
case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
{ {
Upnp_Discovery *disco = (Upnp_Discovery *)evp; auto *disco = (const UpnpDiscovery *)evp;
return OnByeBye(disco); return OnByeBye(disco);
} }

View File

@@ -20,6 +20,7 @@
#ifndef _UPNPPDISC_H_X_INCLUDED_ #ifndef _UPNPPDISC_H_X_INCLUDED_
#define _UPNPPDISC_H_X_INCLUDED_ #define _UPNPPDISC_H_X_INCLUDED_
#include "Compat.hxx"
#include "Callback.hxx" #include "Callback.hxx"
#include "Device.hxx" #include "Device.hxx"
#include "WorkQueue.hxx" #include "WorkQueue.hxx"
@@ -34,6 +35,10 @@
#include <memory> #include <memory>
#include <chrono> #include <chrono>
#if UPNP_VERSION < 10800
#define UpnpDiscovery Upnp_Discovery
#endif
class ContentDirectoryService; class ContentDirectoryService;
class UPnPDiscoveryListener { class UPnPDiscoveryListener {
@@ -59,10 +64,10 @@ class UPnPDeviceDirectory final : UpnpCallback {
std::string device_id; std::string device_id;
std::chrono::steady_clock::duration expires; std::chrono::steady_clock::duration expires;
DiscoveredTask(const Upnp_Discovery *disco) DiscoveredTask(const UpnpDiscovery *disco)
:url(disco->Location), :url(UpnpDiscovery_get_Location_cstr(disco)),
device_id(disco->DeviceId), device_id(UpnpDiscovery_get_DeviceID_cstr(disco)),
expires(std::chrono::seconds(disco->Expires)) {} expires(std::chrono::seconds(UpnpDiscovery_get_Expires(disco))) {}
}; };
/** /**
@@ -153,11 +158,11 @@ private:
static void *Explore(void *); static void *Explore(void *);
void Explore(); void Explore();
int OnAlive(Upnp_Discovery *disco); int OnAlive(const UpnpDiscovery *disco);
int OnByeBye(Upnp_Discovery *disco); int OnByeBye(const UpnpDiscovery *disco);
/* virtual methods from class UpnpCallback */ /* virtual methods from class UpnpCallback */
virtual int Invoke(Upnp_EventType et, void *evp) override; virtual int Invoke(Upnp_EventType et, const void *evp) override;
}; };

View File

@@ -84,7 +84,6 @@ public:
* @return the current volume (0..100 including) or -1 if * @return the current volume (0..100 including) or -1 if
* unavailable * unavailable
*/ */
gcc_pure
virtual int GetVolume() = 0; virtual int GetVolume() = 0;
/** /**

View File

@@ -32,6 +32,7 @@ extern const MixerPlugin software_mixer_plugin;
extern const MixerPlugin alsa_mixer_plugin; extern const MixerPlugin alsa_mixer_plugin;
extern const MixerPlugin haiku_mixer_plugin; extern const MixerPlugin haiku_mixer_plugin;
extern const MixerPlugin oss_mixer_plugin; extern const MixerPlugin oss_mixer_plugin;
extern const MixerPlugin osx_mixer_plugin;
extern const MixerPlugin roar_mixer_plugin; extern const MixerPlugin roar_mixer_plugin;
extern const MixerPlugin pulse_mixer_plugin; extern const MixerPlugin pulse_mixer_plugin;
extern const MixerPlugin winmm_mixer_plugin; extern const MixerPlugin winmm_mixer_plugin;

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2003-2017 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "mixer/MixerInternal.hxx"
#include "output/plugins/OSXOutputPlugin.hxx"
class OSXMixer final : public Mixer {
OSXOutput &output;
public:
OSXMixer(OSXOutput &_output, MixerListener &_listener)
:Mixer(osx_mixer_plugin, _listener),
output(_output)
{
}
/* virtual methods from class Mixer */
void Open() override {
}
void Close() override {
}
int GetVolume() override;
void SetVolume(unsigned volume) override;
};
int
OSXMixer::GetVolume()
{
return osx_output_get_volume(output);
}
void
OSXMixer::SetVolume(unsigned new_volume)
{
osx_output_set_volume(output, new_volume);
}
static Mixer *
osx_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao,
MixerListener &listener,
gcc_unused const ConfigBlock &block)
{
OSXOutput &osxo = (OSXOutput &)ao;
return new OSXMixer(osxo, listener);
}
const MixerPlugin osx_mixer_plugin = {
osx_mixer_init,
true,
};

View File

@@ -48,12 +48,12 @@ class SmbclientNeighborExplorer final : public NeighborExplorer {
Server(const Server &) = delete; Server(const Server &) = delete;
gcc_pure gcc_pure
bool operator==(const Server &other) const { bool operator==(const Server &other) const noexcept {
return name == other.name; return name == other.name;
} }
gcc_pure gcc_pure
NeighborInfo Export() const { NeighborInfo Export() const noexcept {
return { "smb://" + name + "/", comment }; return { "smb://" + name + "/", comment };
} }
}; };

View File

@@ -43,12 +43,12 @@ class UpnpNeighborExplorer final
Server(const Server &) = delete; Server(const Server &) = delete;
gcc_pure gcc_pure
bool operator==(const Server &other) const { bool operator==(const Server &other) const noexcept {
return name == other.name; return name == other.name;
} }
gcc_pure gcc_pure
NeighborInfo Export() const { NeighborInfo Export() const noexcept {
return { "smb://" + name + "/", comment }; return { "smb://" + name + "/", comment };
} }
}; };

View File

@@ -76,47 +76,47 @@ public:
AllocatedSocketAddress &operator=(const AllocatedSocketAddress &) = delete; AllocatedSocketAddress &operator=(const AllocatedSocketAddress &) = delete;
AllocatedSocketAddress &operator=(AllocatedSocketAddress &&src) { AllocatedSocketAddress &operator=(AllocatedSocketAddress &&src) noexcept {
std::swap(address, src.address); std::swap(address, src.address);
std::swap(size, src.size); std::swap(size, src.size);
return *this; return *this;
} }
gcc_pure gcc_pure
bool operator==(SocketAddress other) const { bool operator==(SocketAddress other) const noexcept {
return (SocketAddress)*this == other; return (SocketAddress)*this == other;
} }
bool operator!=(SocketAddress &other) const { bool operator!=(SocketAddress &other) const noexcept {
return !(*this == other); return !(*this == other);
} }
gcc_const gcc_const
static AllocatedSocketAddress Null() { static AllocatedSocketAddress Null() noexcept {
return AllocatedSocketAddress(nullptr, 0); return AllocatedSocketAddress(nullptr, 0);
} }
bool IsNull() const { bool IsNull() const noexcept {
return address == nullptr; return address == nullptr;
} }
size_type GetSize() const { size_type GetSize() const noexcept {
return size; return size;
} }
const struct sockaddr *GetAddress() const { const struct sockaddr *GetAddress() const noexcept {
return address; return address;
} }
operator SocketAddress() const { operator SocketAddress() const noexcept {
return SocketAddress(address, size); return SocketAddress(address, size);
} }
operator const struct sockaddr *() const { operator const struct sockaddr *() const noexcept {
return address; return address;
} }
int GetFamily() const { int GetFamily() const noexcept {
return address->sa_family; return address->sa_family;
} }
@@ -124,11 +124,11 @@ public:
* Does the object have a well-defined address? Check !IsNull() * Does the object have a well-defined address? Check !IsNull()
* before calling this method. * before calling this method.
*/ */
bool IsDefined() const { bool IsDefined() const noexcept {
return GetFamily() != AF_UNSPEC; return GetFamily() != AF_UNSPEC;
} }
void Clear() { void Clear() noexcept {
free(address); free(address);
address = nullptr; address = nullptr;
size = 0; size = 0;

View File

@@ -51,52 +51,52 @@ public:
StaticSocketAddress &operator=(SocketAddress other) noexcept; StaticSocketAddress &operator=(SocketAddress other) noexcept;
operator SocketAddress() const { operator SocketAddress() const noexcept {
return SocketAddress(reinterpret_cast<const struct sockaddr *>(&address), return SocketAddress(reinterpret_cast<const struct sockaddr *>(&address),
size); size);
} }
struct sockaddr *GetAddress() { struct sockaddr *GetAddress() noexcept {
return reinterpret_cast<struct sockaddr *>(&address); return reinterpret_cast<struct sockaddr *>(&address);
} }
const struct sockaddr *GetAddress() const { const struct sockaddr *GetAddress() const noexcept {
return reinterpret_cast<const struct sockaddr *>(&address); return reinterpret_cast<const struct sockaddr *>(&address);
} }
constexpr size_type GetCapacity() const { constexpr size_type GetCapacity() const noexcept {
return sizeof(address); return sizeof(address);
} }
size_type GetSize() const { size_type GetSize() const noexcept {
return size; return size;
} }
void SetSize(size_type _size) { void SetSize(size_type _size) noexcept {
assert(_size > 0); assert(_size > 0);
assert(size_t(_size) <= sizeof(address)); assert(size_t(_size) <= sizeof(address));
size = _size; size = _size;
} }
int GetFamily() const { int GetFamily() const noexcept {
return address.ss_family; return address.ss_family;
} }
bool IsDefined() const { bool IsDefined() const noexcept {
return GetFamily() != AF_UNSPEC; return GetFamily() != AF_UNSPEC;
} }
void Clear() { void Clear() noexcept {
address.ss_family = AF_UNSPEC; address.ss_family = AF_UNSPEC;
} }
gcc_pure gcc_pure
bool operator==(SocketAddress other) const { bool operator==(SocketAddress other) const noexcept {
return (SocketAddress)*this == other; return (SocketAddress)*this == other;
} }
bool operator!=(SocketAddress &other) const { bool operator!=(SocketAddress &other) const noexcept {
return !(*this == other); return !(*this == other);
} }
}; };

View File

@@ -85,7 +85,7 @@ public:
* those which are disabled right now. * those which are disabled right now.
*/ */
gcc_pure gcc_pure
unsigned Size() const { unsigned Size() const noexcept {
return outputs.size(); return outputs.size();
} }
@@ -186,7 +186,7 @@ public:
* finished yet. * finished yet.
*/ */
gcc_pure gcc_pure
SignedSongTime GetElapsedTime() const { SignedSongTime GetElapsedTime() const noexcept {
return elapsed_time; return elapsed_time;
} }

View File

@@ -271,16 +271,15 @@ try {
inline bool inline bool
AudioOutput::PlayChunk() AudioOutput::PlayChunk()
{ {
if (tags) { // ensure pending tags are flushed in all cases
const auto *tag = source.ReadTag(); const auto *tag = source.ReadTag();
if (tag != nullptr) { if (tags && tag != nullptr) {
const ScopeUnlock unlock(mutex); const ScopeUnlock unlock(mutex);
try { try {
ao_plugin_send_tag(this, *tag); ao_plugin_send_tag(this, *tag);
} catch (const std::runtime_error &e) { } catch (const std::runtime_error &e) {
FormatError(e, "Failed to send tag to \"%s\" [%s]", FormatError(e, "Failed to send tag to \"%s\" [%s]",
name, plugin.name); name, plugin.name);
}
} }
} }

View File

@@ -132,7 +132,7 @@ public:
} }
gcc_pure gcc_pure
const char *GetDevice() { const char *GetDevice() const noexcept {
return device.empty() ? default_device : device.c_str(); return device.empty() ? default_device : device.c_str();
} }

View File

@@ -79,7 +79,7 @@ public:
size_t Play(const void *chunk, size_t size); size_t Play(const void *chunk, size_t size);
void Cancel(); void Cancel();
std::chrono::steady_clock::duration Delay(); std::chrono::steady_clock::duration Delay() noexcept;
void FillBuffer(void* _buffer, size_t size, void FillBuffer(void* _buffer, size_t size,
gcc_unused const media_raw_audio_format& _format); gcc_unused const media_raw_audio_format& _format);
@@ -309,7 +309,7 @@ HaikuOutput::Play(const void *chunk, size_t size)
} }
inline std::chrono::steady_clock::duration inline std::chrono::steady_clock::duration
HaikuOutput::Delay() HaikuOutput::Delay() noexcept
{ {
unsigned delay = buffer_filled ? 0 : buffer_delay; unsigned delay = buffer_filled ? 0 : buffer_delay;

View File

@@ -20,6 +20,7 @@
#include "config.h" #include "config.h"
#include "OSXOutputPlugin.hxx" #include "OSXOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "mixer/MixerList.hxx"
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
#include "util/RuntimeError.hxx" #include "util/RuntimeError.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
@@ -53,6 +54,9 @@ struct OSXOutput {
boost::lockfree::spsc_queue<uint8_t> *ring_buffer; boost::lockfree::spsc_queue<uint8_t> *ring_buffer;
OSXOutput(const ConfigBlock &block); OSXOutput(const ConfigBlock &block);
int GetVolume();
void SetVolume(unsigned new_volume);
}; };
static constexpr Domain osx_output_domain("osx_output"); static constexpr Domain osx_output_domain("osx_output");
@@ -103,6 +107,44 @@ OSXOutput::OSXOutput(const ConfigBlock &block)
sync_sample_rate = block.GetBlockValue("sync_sample_rate", false); sync_sample_rate = block.GetBlockValue("sync_sample_rate", false);
} }
int
OSXOutput::GetVolume()
{
AudioUnitParameterValue dvolume;
char errormsg[1024];
OSStatus status = AudioUnitGetParameter(au, kHALOutputParam_Volume,
kAudioUnitScope_Global, 0, &dvolume);
if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
throw FormatRuntimeError("unable to get volume: %s", errormsg);
}
/* see the explanation in SetVolume, below */
return static_cast<int>(dvolume * dvolume * 100.0);
}
void
OSXOutput::SetVolume(unsigned new_volume) {
char errormsg[1024];
/* The scaling below makes shifts in volume greater at the lower end
* of the scale. This mimics the "feel" of physical volume levers. This is
* generally what users of audio software expect.
*/
AudioUnitParameterValue scaled_volume =
sqrt(static_cast<AudioUnitParameterValue>(new_volume) / 100.0);
OSStatus status = AudioUnitSetParameter(au, kHALOutputParam_Volume,
kAudioUnitScope_Global, 0, scaled_volume, 0);
if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
throw FormatRuntimeError( "unable to set new volume %u: %s",
new_volume, errormsg);
}
}
static AudioOutput * static AudioOutput *
osx_output_init(const ConfigBlock &block) osx_output_init(const ConfigBlock &block)
{ {
@@ -670,7 +712,7 @@ osx_output_play(AudioOutput *ao, const void *chunk, size_t size)
} }
static std::chrono::steady_clock::duration static std::chrono::steady_clock::duration
osx_output_delay(AudioOutput *ao) osx_output_delay(AudioOutput *ao) noexcept
{ {
OSXOutput *od = (OSXOutput *)ao; OSXOutput *od = (OSXOutput *)ao;
return od->ring_buffer->write_available() return od->ring_buffer->write_available()
@@ -678,6 +720,18 @@ osx_output_delay(AudioOutput *ao)
: std::chrono::milliseconds(25); : std::chrono::milliseconds(25);
} }
int
osx_output_get_volume(OSXOutput &output)
{
return output.GetVolume();
}
void
osx_output_set_volume(OSXOutput &output, unsigned new_volume)
{
return output.SetVolume(new_volume);
}
const struct AudioOutputPlugin osx_output_plugin = { const struct AudioOutputPlugin osx_output_plugin = {
"osx", "osx",
osx_output_test_default_device, osx_output_test_default_device,
@@ -693,5 +747,6 @@ const struct AudioOutputPlugin osx_output_plugin = {
nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr,
nullptr,
&osx_mixer_plugin,
}; };

View File

@@ -20,6 +20,14 @@
#ifndef MPD_OSX_OUTPUT_PLUGIN_HXX #ifndef MPD_OSX_OUTPUT_PLUGIN_HXX
#define MPD_OSX_OUTPUT_PLUGIN_HXX #define MPD_OSX_OUTPUT_PLUGIN_HXX
struct OSXOutput;
extern const struct AudioOutputPlugin osx_output_plugin; extern const struct AudioOutputPlugin osx_output_plugin;
int
osx_output_get_volume(OSXOutput &output);
void
osx_output_set_volume(OSXOutput &output, unsigned new_volume);
#endif #endif

View File

@@ -73,19 +73,19 @@ class OpenALOutput {
private: private:
gcc_pure gcc_pure
ALint GetSourceI(ALenum param) const { ALint GetSourceI(ALenum param) const noexcept {
ALint value; ALint value;
alGetSourcei(source, param, &value); alGetSourcei(source, param, &value);
return value; return value;
} }
gcc_pure gcc_pure
bool HasProcessed() const { bool HasProcessed() const noexcept {
return GetSourceI(AL_BUFFERS_PROCESSED) > 0; return GetSourceI(AL_BUFFERS_PROCESSED) > 0;
} }
gcc_pure gcc_pure
bool IsPlaying() const { bool IsPlaying() const noexcept {
return GetSourceI(AL_SOURCE_STATE) == AL_PLAYING; return GetSourceI(AL_SOURCE_STATE) == AL_PLAYING;
} }

View File

@@ -100,7 +100,7 @@ public:
void Open(AudioFormat &audio_format); void Open(AudioFormat &audio_format);
void Close(); void Close();
std::chrono::steady_clock::duration Delay(); std::chrono::steady_clock::duration Delay() noexcept;
size_t Play(const void *chunk, size_t size); size_t Play(const void *chunk, size_t size);
void Cancel(); void Cancel();
bool Pause(); bool Pause();
@@ -740,7 +740,7 @@ PulseOutput::StreamPause(bool pause)
} }
inline std::chrono::steady_clock::duration inline std::chrono::steady_clock::duration
PulseOutput::Delay() PulseOutput::Delay() noexcept
{ {
Pulse::LockGuard lock(mainloop); Pulse::LockGuard lock(mainloop);

View File

@@ -97,7 +97,7 @@ class RecorderOutput {
private: private:
gcc_pure gcc_pure
bool HasDynamicPath() const { bool HasDynamicPath() const noexcept {
return !format_path.empty(); return !format_path.empty();
} }

View File

@@ -50,7 +50,7 @@ class SndioOutput {
AudioOutput base; AudioOutput base;
const char *const device; const char *const device;
const unsigned buffer_time; /* in ms */ const unsigned buffer_time; /* in ms */
struct sio_hdl *sio_hdl; struct sio_hdl *hdl;
public: public:
SndioOutput(const ConfigBlock &block); SndioOutput(const ConfigBlock &block);
@@ -80,16 +80,14 @@ SndioOutput::Create(const ConfigBlock &block)
static bool static bool
sndio_test_default_device() sndio_test_default_device()
{ {
struct sio_hdl *sio_hdl; auto *hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0);
if (!hdl) {
sio_hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0);
if (!sio_hdl) {
FormatError(sndio_output_domain, FormatError(sndio_output_domain,
"Error opening default sndio device"); "Error opening default sndio device");
return false; return false;
} }
sio_close(sio_hdl); sio_close(hdl);
return true; return true;
} }
@@ -99,8 +97,8 @@ SndioOutput::Open(AudioFormat &audio_format)
struct sio_par par; struct sio_par par;
unsigned bits, rate, chans; unsigned bits, rate, chans;
sio_hdl = sio_open(device, SIO_PLAY, 0); hdl = sio_open(device, SIO_PLAY, 0);
if (!sio_hdl) if (!hdl)
throw std::runtime_error("Failed to open default sndio device"); throw std::runtime_error("Failed to open default sndio device");
switch (audio_format.format) { switch (audio_format.format) {
@@ -130,9 +128,9 @@ SndioOutput::Open(AudioFormat &audio_format)
par.le = SIO_LE_NATIVE; par.le = SIO_LE_NATIVE;
par.appbufsz = rate * buffer_time / 1000; par.appbufsz = rate * buffer_time / 1000;
if (!sio_setpar(sio_hdl, &par) || if (!sio_setpar(hdl, &par) ||
!sio_getpar(sio_hdl, &par)) { !sio_getpar(hdl, &par)) {
sio_close(sio_hdl); sio_close(hdl);
throw std::runtime_error("Failed to set/get audio params"); throw std::runtime_error("Failed to set/get audio params");
} }
@@ -142,12 +140,12 @@ SndioOutput::Open(AudioFormat &audio_format)
par.pchan != chans || par.pchan != chans ||
par.sig != 1 || par.sig != 1 ||
par.le != SIO_LE_NATIVE) { par.le != SIO_LE_NATIVE) {
sio_close(sio_hdl); sio_close(hdl);
throw std::runtime_error("Requested audio params cannot be satisfied"); throw std::runtime_error("Requested audio params cannot be satisfied");
} }
if (!sio_start(sio_hdl)) { if (!sio_start(hdl)) {
sio_close(sio_hdl); sio_close(hdl);
throw std::runtime_error("Failed to start audio device"); throw std::runtime_error("Failed to start audio device");
} }
} }
@@ -155,7 +153,7 @@ SndioOutput::Open(AudioFormat &audio_format)
void void
SndioOutput::Close() SndioOutput::Close()
{ {
sio_close(sio_hdl); sio_close(hdl);
} }
size_t size_t
@@ -163,8 +161,8 @@ SndioOutput::Play(const void *chunk, size_t size)
{ {
size_t n; size_t n;
n = sio_write(sio_hdl, chunk, size); n = sio_write(hdl, chunk, size);
if (n == 0 && sio_eof(sio_hdl) != 0) if (n == 0 && sio_eof(hdl) != 0)
throw std::runtime_error("sndio write failed"); throw std::runtime_error("sndio write failed");
return n; return n;
} }

View File

@@ -191,7 +191,7 @@ public:
* Caller must lock the mutex. * Caller must lock the mutex.
*/ */
gcc_pure gcc_pure
bool HasClients() const { bool HasClients() const noexcept {
return !clients.empty(); return !clients.empty();
} }
@@ -199,7 +199,7 @@ public:
* Check whether there is at least one client. * Check whether there is at least one client.
*/ */
gcc_pure gcc_pure
bool LockHasClients() const { bool LockHasClients() const noexcept {
const std::lock_guard<Mutex> protect(mutex); const std::lock_guard<Mutex> protect(mutex);
return HasClients(); return HasClients();
} }

View File

@@ -468,6 +468,7 @@ HttpdOutput::SendTag(const Tag &tag)
try { try {
encoder->SendTag(tag); encoder->SendTag(tag);
encoder->Flush();
} catch (const std::runtime_error &) { } catch (const std::runtime_error &) {
/* ignore */ /* ignore */
} }

View File

@@ -96,7 +96,7 @@ public:
void Open(AudioFormat &audio_format); void Open(AudioFormat &audio_format);
void Close(); void Close();
std::chrono::steady_clock::duration Delay() { std::chrono::steady_clock::duration Delay() noexcept {
return pause && !cancel return pause && !cancel
? std::chrono::milliseconds(100) ? std::chrono::milliseconds(100)
: std::chrono::steady_clock::duration::zero(); : std::chrono::steady_clock::duration::zero();

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