Compare commits

...

74 Commits

Author SHA1 Message Date
Max Kellermann
566787f041 release v0.21.26 2020-09-21 15:14:43 +02:00
Max Kellermann
79b2366387 archive/iso9660: fix odd seeking bug (assertion failure)
Skip the beginning of a sector if the last seek was odd, and clear the
buffer on seek.
2020-09-21 15:11:21 +02:00
Max Kellermann
5acea014b0 archive/iso9660: remove unused macro CEILING() 2020-09-21 15:11:17 +02:00
Max Kellermann
b72801abf3 util/ByteOrder: add FromLE16S() 2020-09-21 11:15:45 +02:00
Desuwa
23d5a2b862 Support opus header gain tags and match opus playback volume to other tracks when ReplayGain is enabled. 2020-09-21 10:51:06 +02:00
Rosen Penev
7715311117 fix double promotions
Found with -Wdouble-promotion

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2020-09-21 09:13:51 +02:00
Max Kellermann
4c1cfca95b db/update/InotifyUpdate: pass path by value to recursive_watch_subdirectories() 2020-09-17 14:18:29 +02:00
Max Kellermann
e113ce9621 db/update/InotifyUpdate: obey .mpdignore files
Closes https://github.com/MusicPlayerDaemon/MPD/issues/846
2020-09-17 14:17:17 +02:00
Max Kellermann
e8213220e2 db/update/InotifyUpdate: split the WatchDirectory constructor 2020-09-16 21:08:22 +02:00
Max Kellermann
83f9d2a963 db/update/InotifyUpdate: use class DirectoryReader 2020-09-16 21:02:07 +02:00
Max Kellermann
bf97ebf89f db/update/InotifyUpdate: convert pointer to reference 2020-09-16 20:59:41 +02:00
Max Kellermann
5b22d27cbb db/update/InotifyUpdate: remove commented log call 2020-09-16 20:59:40 +02:00
Max Kellermann
e907ff43ae command/file, storage/{nfs,smbclient}: use PathTraitsFS::IsSpecialFilename()
Eliminate some duplicate code.
2020-09-16 20:57:46 +02:00
Max Kellermann
b18fc3a8d0 db/update/InotifySource: use auto 2020-09-16 20:40:27 +02:00
Max Kellermann
a8e23c4140 db/update/InotifySource: add noexcept 2020-09-16 20:40:06 +02:00
Max Kellermann
fc3861b421 db/update/InotifyQueue: add noexcept 2020-09-16 20:40:03 +02:00
Max Kellermann
e81bb5d8f1 db/update/Inotify*: include cleanup 2020-09-16 20:39:44 +02:00
Max Kellermann
32f4f15831 player/Thread: call OnPlayerSync() in SeekDecoder()
This fixes a spurious "single" mode bug which occurs when using "play"
or "seek" to start playback on the song that is currently paused: in
that case, the main thread never queues the next song, and at the end
of the song, the player thread exits Run(), stopping playback, and
after that, the main thread starts the next song without considering
"single" mode.

By calling OnPlayerSync(), we ensure that the main thread gets a
chance to queue the next song before the player thread exits the Run()
loop.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/850
2020-09-16 20:36:19 +02:00
Max Kellermann
e29c06b718 player/Thread: add another code comment explaining OnPlayerSync() 2020-09-16 20:12:52 +02:00
Max Kellermann
d9d511f33e player/Thread: update function name in comment 2020-09-16 20:12:05 +02:00
Max Kellermann
c61a3b8d13 LogBackend: change the initial log_threshold to DEFAULT
The log levels have always been very confusing (and badly named), but
this was most confusing: if there's a log level called "default", why
is it not the default?

Closes https://github.com/MusicPlayerDaemon/MPD/issues/926
2020-09-16 17:17:34 +02:00
Max Kellermann
e10b867fe6 decoder/ffmpeg: add "hls+http://" to the list of supported protocols 2020-09-16 16:36:07 +02:00
Max Kellermann
43e230f543 decoder/ffmpeg: remove "rtsp://" from the list of supported protocols
FFmpeg implements RTSP as a demuxer, not as a protocol handler.  Thus,
avio_open() cannot be used, and our input plugin cannot handle RTSP.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/930
2020-09-16 16:32:31 +02:00
Max Kellermann
b2ae5298a7 archive/iso9660: implement seeking 2020-09-07 21:13:28 +02:00
Max Kellermann
17dd21ac7f archive/iso9660: fix unaligned reads
Oh the horror!  This plugin cannot possibly ever have worked.  It was
broken from the start, when it was added in commit 37796699cf nearly
twelve (!) years ago.

The plugin would always read at sector boundaries, so it could only
ever work at multiples of 2 kB.
2020-09-07 21:08:46 +02:00
Max Kellermann
1a5e0ef7c9 test/test_archive_iso9660.sh: use an odd chunk size to trigger bug
This makes the unit test fail.  D'oh!
2020-09-07 20:53:46 +02:00
Max Kellermann
979a7a1dcc test/run_input: add option --chunk-size 2020-09-07 20:52:37 +02:00
Max Kellermann
962cf32ba7 test/run_input: pass FileDescriptor to dump_input_stream() 2020-09-07 20:14:39 +02:00
Max Kellermann
ae23682372 system/FileDescriptor: add method FullWrite() 2020-09-07 20:13:43 +02:00
Max Kellermann
540919f256 *: use nullptr instead of NULL 2020-09-07 20:08:27 +02:00
Max Kellermann
398281cd76 io/FileDescriptor: add method FullRead() 2020-09-07 20:07:47 +02:00
Max Kellermann
88446ccde9 test/run_filter: use Filter::Flush() 2020-09-07 20:07:40 +02:00
Max Kellermann
6238cc0734 test/run_filter: pass ConstBuffer<void> to FullWrite() 2020-09-07 20:07:33 +02:00
Max Kellermann
fd4823c507 test/run_filter: fix error message 2020-09-07 20:07:29 +02:00
Max Kellermann
68bcfd8bf0 test/run_filter: check for partial writes 2020-09-07 20:07:24 +02:00
Max Kellermann
1d332746af test/run_filter: move code to WriteOrThrow() 2020-09-07 20:07:18 +02:00
Max Kellermann
f3e133c617 test/run_filter: use class FileDescriptor 2020-09-07 20:07:13 +02:00
Max Kellermann
1678a6eb59 test/run_filter: ensure that partial frames will not get passed to the filter 2020-09-07 20:07:08 +02:00
Max Kellermann
b4dc2c07d5 test/run_filter: move the buffer into the loop 2020-09-07 20:07:03 +02:00
Max Kellermann
d7838950d8 test/run_input: use WithBufferedOutputStream() 2020-09-07 20:04:13 +02:00
Max Kellermann
2e93a83dd5 test/run_input: convert pointer to reference 2020-09-07 20:02:12 +02:00
Max Kellermann
db8b419b8c archive/iso9660: free iso9660_stat_t as early as possible 2020-09-04 18:17:24 +02:00
Max Kellermann
990f631cbc archive/bzip2: make variables more local 2020-09-04 18:02:22 +02:00
Max Kellermann
db46d84458 archive/bzip2: move the eof check out of the ScopeUnlock 2020-09-04 18:01:29 +02:00
Max Kellermann
9e6c4f8d80 archive/bzip2: throw on unexpected input EOF
Don't silently return 0 when there is no more data, because this may
crash the caller.  And flush output even if input EOF has been reached.
2020-09-04 17:54:53 +02:00
Max Kellermann
41b47f95c5 archive/bzip2: simplify bz_stream initializer 2020-09-04 17:52:04 +02:00
Max Kellermann
15939fd87c archive/bzip2: fold Open() into constructor 2020-09-04 17:51:41 +02:00
Max Kellermann
f63c343f68 archive/bzip2: reorder fields to improve packing 2020-09-04 17:51:22 +02:00
Max Kellermann
1a516e7744 archive/bzip2: add override 2020-09-04 17:51:21 +02:00
Max Kellermann
5c9d97775f python/build/libs.py: update Boost to 1.74.0 2020-09-04 14:49:11 +02:00
Max Kellermann
64aadcd13f python/build/libs.py: update CURL to 7.72.0 2020-09-04 14:48:40 +02:00
Max Kellermann
1f6a7d6462 archive/zzip: fix crash on corrupt ZIP file
Sometimes, zzip_file_read() returns 0 even though the end of the file
was not reached.  This causes assertion failures in
DecoderBridge::Read().

Closes https://github.com/MusicPlayerDaemon/MPD/issues/935
2020-09-04 14:34:54 +02:00
Max Kellermann
e44b953d9a archive/zzip: use zzip_ssize_t to avoid integer overflows 2020-09-04 14:33:44 +02:00
Max Kellermann
6c85020630 archive/zzip: add override 2020-09-04 14:33:44 +02:00
Max Kellermann
9d910320f3 archive/zzip: pass std::shared_ptr as template parameter
This eliminates a tiny amount of overhead because the compiler can
choose how to pass the parameter.
2020-09-04 14:33:44 +02:00
Max Kellermann
c53074efc9 archive/zzip: add explicit 2020-09-04 14:33:11 +02:00
Max Kellermann
3b51c53eca win32/build.py: add -D_FORTIFY_SOURCE=0
This fixes the Windows build.  Linking failed because some packages
(e.g. libFLAC) default to enabling `_FORTIFY_SOURCE`, which is broken
in recent mingw versions
(https://github.com/msys2/MINGW-packages/issues/5803).
2020-09-04 14:33:11 +02:00
Max Kellermann
0aa0ffb67b decoder/sndfile: allow partial reads at end of file
While libsndfile doesn't like partial reads in the middle of a file
(see commit 95ac6071b9), it allows partial reads at the end of a file.
It doesn't pay attention to the file size when issuing a read.

Commit ecb67a1ed1 (MPD 0.18.12) was a regression: previously,
partial reads at the end of a file were possible, but switching to
decoder_read_full() made this an error condition.  This way, a portion
at the end of each file was lost, leading to corruption with gapless
playback (https://github.com/MusicPlayerDaemon/MPD/issues/936).

This fix switches to the newly introduced function
decoder_read_much(), which does the same as the code before commit
ecb67a1ed1.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/936
2020-09-04 13:35:00 +02:00
Max Kellermann
33f70931dd decoder/API: add decoder_read_much() 2020-09-04 13:35:00 +02:00
Max Kellermann
8830ea319f decoder/API: add noexcept 2020-09-04 13:35:00 +02:00
Max Kellermann
cbcdc73f9a system/ByteOrder: add noexcept 2020-08-14 16:36:24 +02:00
Max Kellermann
4f6c54ecb3 output/osx: catch kAudioDevicePropertyHogMode errors
Our AudioObjectGetPropertyDataT() wrapper throws exception on error,
and calling it from OSXOutput::Disable() can cause MPD crash due to
std::terminate().

Closes https://github.com/MusicPlayerDaemon/MPD/issues/932
2020-08-14 16:33:43 +02:00
Max Kellermann
2bdf1b2284 test/meson.build: add explicit dependency from run_output on libevent.a
We could exclude that feature if neither ALSA nor httpd are enabled,
but that's too complicated for this small debug program.
2020-08-14 14:40:39 +02:00
Rosen Penev
c876d6a51c lib/icu: fix build without libc iconv support
Need to check for it in iconv.h. Otherwise meson prefixes a __builtin variant in the check.
2020-07-23 14:09:43 +02:00
Max Kellermann
3c745b4bc6 neighbor/smbclient: remove obsolete commented code 2020-07-20 18:13:38 +02:00
Max Kellermann
3a08a6ad72 doc/plugins.rst: document sample formats for OpenSLES 2020-07-20 15:27:41 +02:00
Max Kellermann
448b397cb8 output/sles: support floating point samples
According to https://developer.android.com/ndk/guides/audio/opensl/android-extensions

This feature was mentioned in https://github.com/MusicPlayerDaemon/MPD/issues/922
2020-07-20 15:23:50 +02:00
Max Kellermann
64a1386eb6 output/sles: move SampleFormat selection to switch/case block 2020-07-20 14:47:36 +02:00
Max Kellermann
77c2efe171 python/build/libs.py: update CURL to 7.71.1 2020-07-20 12:37:54 +02:00
Max Kellermann
587c0f6232 python/build/libs.py: update FFmpeg to 4.3.1 2020-07-20 12:37:16 +02:00
Max Kellermann
64e8abf203 python/build/libs.py: update libvorbis to 1.3.7 2020-07-20 12:34:16 +02:00
Max Kellermann
6c40d2a656 python/build/libs.py: update libmpdclient to 2.19 2020-07-20 12:32:19 +02:00
Max Kellermann
cf674e9273 input/Init: downgrade PluginUnconfigured to LogLevel::DEBUG
`LogLevel::INFO` is logged by default, but this message shall only
appear with `--verbose`.

This finally solves https://github.com/MusicPlayerDaemon/MPD/issues/430
2020-07-16 13:19:14 +02:00
Max Kellermann
9bda0379af increment version number to 0.21.26 2020-07-16 12:53:22 +02:00
73 changed files with 675 additions and 327 deletions

20
NEWS
View File

@@ -1,3 +1,23 @@
ver 0.21.26 (2020/09/21)
* database
- inotify: obey ".mpdignore" files
* output
- osx: fix crash bug
- sles: support floating point samples
* archive
- bzip2: fix crash on corrupt bzip2 file
- bzip2: flush output at end of input file
- iso9660: fix unaligned reads
- iso9660: support seeking
- zzip: fix crash on corrupt ZIP file
* decoder
- ffmpeg: remove "rtsp://" from the list of supported protocols
- ffmpeg: add "hls+http://" to the list of supported protocols
- opus: support the gain value from the Opus header
- sndfile: fix lost samples at end of file
* fix "single" mode bug after resuming playback
* the default log_level is "default", not "info"
ver 0.21.25 (2020/07/06)
* protocol:
- fix crash when using "rangeid" while playing

View File

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

View File

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

View File

@@ -1072,7 +1072,8 @@ sles
Plugin using the `OpenSL ES <https://www.khronos.org/opensles/>`__
audio API. Its primary use is local playback on Android, where
:ref:`ALSA <alsa_plugin>` is not available.
:ref:`ALSA <alsa_plugin>` is not available. It supports 16 bit and
floating point samples.
solaris

View File

@@ -1,7 +1,7 @@
project(
'mpd',
['c', 'cpp'],
version: '0.21.25',
version: '0.21.26',
meson_version: '>= 0.49.0',
default_options: [
'c_std=c99',

View File

@@ -10,8 +10,8 @@ from build.ffmpeg import FfmpegProject
from build.boost import BoostProject
libmpdclient = MesonProject(
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.18.tar.xz',
'4cb01e1f567e0169aca94875fb6e1200e7f5ce35b63a4df768ec1591fb1081fa',
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.19.tar.xz',
'158aad4c2278ab08e76a3f2b0166c99b39fae00ee17231bd225c5a36e977a189',
'lib/libmpdclient.a',
)
@@ -25,8 +25,8 @@ libogg = AutotoolsProject(
)
libvorbis = AutotoolsProject(
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.6.tar.xz',
'af00bb5a784e7c9e69f56823de4637c350643deedaf333d0fa86ecdba6fcb415',
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.7.tar.xz',
'b33cc4934322bcbf6efcbacf49e3ca01aadbea4114ec9589d1b1e9d20f72954b',
'lib/libvorbis.a',
[
'--disable-shared', '--enable-static',
@@ -148,8 +148,8 @@ gme = CmakeProject(
)
ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-4.2.3.tar.xz',
'9df6c90aed1337634c1fb026fb01c154c29c82a64ea71291ff2da9aacb9aad31',
'http://ffmpeg.org/releases/ffmpeg-4.3.1.tar.xz',
'ad009240d46e307b4e03a213a0f49c11b650e445b1f8be0dda2a9212b34d2ffb',
'lib/libavcodec.a',
[
'--disable-shared', '--enable-static',
@@ -377,8 +377,8 @@ ffmpeg = FfmpegProject(
)
curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.70.0.tar.xz',
'032f43f2674008c761af19bf536374128c16241fb234699a55f9fb603fcfbae7',
'http://curl.haxx.se/download/curl-7.72.0.tar.xz',
'0ded0808c4d85f2ee0db86980ae610cc9d165e9ca9da466196cc73c346513713',
'lib/libcurl.a',
[
'--disable-shared', '--enable-static',
@@ -433,7 +433,7 @@ libnfs = AutotoolsProject(
)
boost = BoostProject(
'https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.bz2',
'4eb3b8d442b426dc35346235c8733b5ae35ba431690e38c6a8263dce9fcbb402',
'https://dl.bintray.com/boostorg/release/1.74.0/source/boost_1_74_0.tar.bz2',
'83bfc1507731a0906e387fc28b7ef5417d591429e51e788417fe9ff025e116b1',
'include/boost/version.hpp',
)

View File

@@ -61,7 +61,7 @@ ToAndroidLogLevel(LogLevel log_level) noexcept
#else
static LogLevel log_threshold = LogLevel::INFO;
static LogLevel log_threshold = LogLevel::DEFAULT;
static bool enable_timestamp;

View File

@@ -101,7 +101,7 @@ initPermissions(const ConfigData &config)
const char *separator = strchr(param.value.c_str(),
PERMISSION_PASSWORD_CHAR);
if (separator == NULL)
if (separator == nullptr)
throw FormatRuntimeError("\"%c\" not found in password string "
"\"%s\", line %i",
PERMISSION_PASSWORD_CHAR,

View File

@@ -23,9 +23,9 @@
#include "config/Data.hxx"
#include "util/RuntimeError.hxx"
#include <assert.h>
#include <stdlib.h>
#include <math.h>
#include <cassert>
#include <cstdlib>
#include <cmath>
static float
ParsePreamp(const char *s)
@@ -33,14 +33,14 @@ ParsePreamp(const char *s)
assert(s != nullptr);
char *endptr;
float f = strtod(s, &endptr);
float f = std::strtof(s, &endptr);
if (endptr == s || *endptr != '\0')
throw std::invalid_argument("Not a numeric value");
if (f < -15 || f > 15)
if (f < -15.0f || f > 15.0f)
throw std::invalid_argument("Number must be between -15 and 15");
return pow(10, f / 20.0);
return std::pow(10.0f, f / 20.0f);
}
static float

View File

@@ -20,7 +20,7 @@
#include "ReplayGainInfo.hxx"
#include "ReplayGainConfig.hxx"
#include <math.h>
#include <cmath>
float
ReplayGainTuple::CalculateScale(const ReplayGainConfig &config) const noexcept
@@ -28,13 +28,13 @@ ReplayGainTuple::CalculateScale(const ReplayGainConfig &config) const noexcept
float scale;
if (IsDefined()) {
scale = pow(10.0, gain / 20.0);
scale = std::pow(10.0f, gain / 20.0f);
scale *= config.preamp;
if (scale > 15.0)
scale = 15.0;
if (scale > 15.0f)
scale = 15.0f;
if (config.limit && scale * peak > 1.0)
scale = 1.0 / peak;
if (config.limit && scale * peak > 1.0f)
scale = 1.0f / peak;
} else
scale = config.missing_preamp;

View File

@@ -58,9 +58,9 @@ public:
class Bzip2InputStream final : public InputStream {
std::shared_ptr<InputStream> input;
bool eof = false;
bz_stream bzstream{};
bz_stream bzstream;
bool eof = false;
char buffer[5000];
@@ -68,7 +68,7 @@ public:
Bzip2InputStream(const std::shared_ptr<InputStream> &_input,
const char *uri,
Mutex &mutex);
~Bzip2InputStream();
~Bzip2InputStream() noexcept override;
/* virtual methods from InputStream */
bool IsEOF() noexcept override;
@@ -79,25 +79,6 @@ private:
bool FillBuffer();
};
/* single archive handling allocation helpers */
inline void
Bzip2InputStream::Open()
{
bzstream.bzalloc = nullptr;
bzstream.bzfree = nullptr;
bzstream.opaque = nullptr;
bzstream.next_in = (char *)buffer;
bzstream.avail_in = 0;
int ret = BZ2_bzDecompressInit(&bzstream, 0, 0);
if (ret != BZ_OK)
throw std::runtime_error("BZ2_bzDecompressInit() has failed");
SetReady();
}
/* archive open && listing routine */
static std::unique_ptr<ArchiveFile>
@@ -116,10 +97,16 @@ Bzip2InputStream::Bzip2InputStream(const std::shared_ptr<InputStream> &_input,
:InputStream(_uri, _mutex),
input(_input)
{
Open();
bzstream.next_in = (char *)buffer;
int ret = BZ2_bzDecompressInit(&bzstream, 0, 0);
if (ret != BZ_OK)
throw std::runtime_error("BZ2_bzDecompressInit() has failed");
SetReady();
}
Bzip2InputStream::~Bzip2InputStream()
Bzip2InputStream::~Bzip2InputStream() noexcept
{
BZ2_bzDecompressEnd(&bzstream);
}
@@ -149,22 +136,18 @@ Bzip2InputStream::FillBuffer()
size_t
Bzip2InputStream::Read(void *ptr, size_t length)
{
const ScopeUnlock unlock(mutex);
int bz_result;
size_t nbytes = 0;
if (eof)
return 0;
const ScopeUnlock unlock(mutex);
bzstream.next_out = (char *)ptr;
bzstream.avail_out = length;
do {
if (!FillBuffer())
return 0;
const bool had_input = FillBuffer();
bz_result = BZ2_bzDecompress(&bzstream);
const int bz_result = BZ2_bzDecompress(&bzstream);
if (bz_result == BZ_STREAM_END) {
eof = true;
@@ -173,9 +156,12 @@ Bzip2InputStream::Read(void *ptr, size_t length)
if (bz_result != BZ_OK)
throw std::runtime_error("BZ2_bzDecompress() has failed");
if (!had_input && bzstream.avail_out == length)
throw std::runtime_error("Unexpected end of bzip2 file");
} while (bzstream.avail_out == length);
nbytes = length - bzstream.avail_out;
const size_t nbytes = length - bzstream.avail_out;
offset += nbytes;
return nbytes;

View File

@@ -29,14 +29,15 @@
#include "fs/Path.hxx"
#include "util/RuntimeError.hxx"
#include "util/StringCompare.hxx"
#include "util/WritableBuffer.hxx"
#include <cdio/iso9660.h>
#include <array>
#include <stdlib.h>
#include <string.h>
#define CEILING(x, y) ((x+(y-1))/y)
struct Iso9660 {
iso9660_t *const iso;
@@ -142,26 +143,86 @@ Iso9660ArchiveFile::Visit(ArchiveVisitor &visitor)
class Iso9660InputStream final : public InputStream {
std::shared_ptr<Iso9660> iso;
iso9660_stat_t *statbuf;
const lsn_t lsn;
/**
* libiso9660 can only read whole sectors at a time, and this
* buffer is used to store one whole sector and allow Read()
* to handle partial sector reads.
*/
class BlockBuffer {
size_t position = 0, fill = 0;
std::array<uint8_t, ISO_BLOCKSIZE> data;
public:
ConstBuffer<uint8_t> Read() const noexcept {
assert(fill <= data.size());
assert(position <= fill);
return {&data[position], &data[fill]};
}
void Consume(size_t nbytes) noexcept {
assert(nbytes <= Read().size);
position += nbytes;
}
WritableBuffer<uint8_t> Write() noexcept {
assert(Read().empty());
return {data.data(), data.size()};
}
void Append(size_t nbytes) noexcept {
assert(Read().empty());
assert(nbytes <= data.size());
fill = nbytes;
position = 0;
}
void Clear() noexcept {
position = fill = 0;
}
};
BlockBuffer buffer;
/**
* Skip this number of bytes of the first sector after filling
* the buffer next time. This is used for seeking into the
* middle of a sector.
*/
size_t skip = 0;
public:
Iso9660InputStream(const std::shared_ptr<Iso9660> &_iso,
const char *_uri,
Mutex &_mutex,
iso9660_stat_t *_statbuf)
lsn_t _lsn, offset_type _size)
:InputStream(_uri, _mutex),
iso(_iso), statbuf(_statbuf) {
size = statbuf->size;
iso(_iso),
lsn(_lsn)
{
size = _size;
seekable = true;
SetReady();
}
~Iso9660InputStream() {
free(statbuf);
}
/* virtual methods from InputStream */
bool IsEOF() noexcept override;
size_t Read(void *ptr, size_t size) override;
void Seek(offset_type new_offset) override {
if (new_offset > size)
throw std::runtime_error("Invalid seek offset");
skip = new_offset % ISO_BLOCKSIZE;
offset = new_offset - skip;
buffer.Clear();
}
};
InputStreamPtr
@@ -173,42 +234,78 @@ Iso9660ArchiveFile::OpenStream(const char *pathname,
throw FormatRuntimeError("not found in the ISO file: %s",
pathname);
const lsn_t lsn = statbuf->lsn;
const offset_type size = statbuf->size;
free(statbuf);
return std::make_unique<Iso9660InputStream>(iso, pathname, mutex,
statbuf);
lsn, size);
}
size_t
Iso9660InputStream::Read(void *ptr, size_t read_size)
{
const ScopeUnlock unlock(mutex);
int readed = 0;
int no_blocks, cur_block;
size_t left_bytes = statbuf->size - offset;
if (left_bytes < read_size) {
no_blocks = CEILING(left_bytes, ISO_BLOCKSIZE);
} else {
no_blocks = read_size / ISO_BLOCKSIZE;
}
if (no_blocks == 0)
const offset_type remaining = size - offset;
if (remaining == 0)
return 0;
cur_block = offset / ISO_BLOCKSIZE;
if (offset_type(read_size) > remaining)
read_size = remaining;
readed = iso->SeekRead(ptr, statbuf->lsn + cur_block, no_blocks);
auto r = buffer.Read();
if (readed != no_blocks * ISO_BLOCKSIZE)
throw FormatRuntimeError("error reading ISO file at lsn %lu",
(unsigned long)cur_block);
if (r.empty()) {
/* the buffer is empty - read more data from the ISO file */
if (left_bytes < read_size) {
readed = left_bytes;
assert(offset % ISO_BLOCKSIZE == 0);
const ScopeUnlock unlock(mutex);
const lsn_t read_lsn = lsn + offset / ISO_BLOCKSIZE;
if (read_size >= ISO_BLOCKSIZE) {
/* big read - read right into the caller's buffer */
auto nbytes = iso->SeekRead(ptr, read_lsn,
read_size / ISO_BLOCKSIZE);
if (nbytes <= 0)
throw std::runtime_error("Failed to read ISO9660 file");
offset += nbytes;
return nbytes;
}
/* fill the buffer */
auto w = buffer.Write();
auto nbytes = iso->SeekRead(w.data, read_lsn,
w.size / ISO_BLOCKSIZE);
if (nbytes <= 0)
throw std::runtime_error("Failed to read ISO9660 file");
buffer.Append(nbytes);
r = buffer.Read();
if (skip > 0) {
if (skip >= r.size)
throw std::runtime_error("Premature end of ISO9660 track");
buffer.Consume(skip);
skip = 0;
r = buffer.Read();
}
}
offset += readed;
return readed;
assert(!r.empty());
assert(skip == 0);
size_t nbytes = std::min(read_size, r.size);
memcpy(ptr, r.data, nbytes);
buffer.Consume(nbytes);
offset += nbytes;
return nbytes;
}
bool

View File

@@ -32,6 +32,8 @@
#include <zzip/zzip.h>
#include <inttypes.h> /* for PRIoffset (PRIu64) */
struct ZzipDir {
ZZIP_DIR *const dir;
@@ -54,10 +56,11 @@ class ZzipArchiveFile final : public ArchiveFile {
std::shared_ptr<ZzipDir> dir;
public:
ZzipArchiveFile(std::shared_ptr<ZzipDir> &&_dir)
:dir(std::move(_dir)) {}
template<typename D>
explicit ZzipArchiveFile(D &&_dir) noexcept
:dir(std::forward<D>(_dir)) {}
virtual void Visit(ArchiveVisitor &visitor) override;
void Visit(ArchiveVisitor &visitor) override;
InputStreamPtr OpenStream(const char *path,
Mutex &mutex) override;
@@ -91,11 +94,12 @@ class ZzipInputStream final : public InputStream {
ZZIP_FILE *const file;
public:
ZzipInputStream(const std::shared_ptr<ZzipDir> _dir, const char *_uri,
template<typename D>
ZzipInputStream(D &&_dir, const char *_uri,
Mutex &_mutex,
ZZIP_FILE *_file)
:InputStream(_uri, _mutex),
dir(_dir), file(_file) {
dir(std::forward<D>(_dir)), file(_file) {
//we are seekable (but its not recommendent to do so)
seekable = true;
@@ -106,7 +110,7 @@ public:
SetReady();
}
~ZzipInputStream() {
~ZzipInputStream() noexcept override {
zzip_file_close(file);
}
@@ -145,12 +149,17 @@ ZzipInputStream::Read(void *ptr, size_t read_size)
{
const ScopeUnlock unlock(mutex);
int ret = zzip_file_read(file, ptr, read_size);
if (ret < 0)
zzip_ssize_t nbytes = zzip_file_read(file, ptr, read_size);
if (nbytes < 0)
throw std::runtime_error("zzip_file_read() has failed");
if (nbytes == 0 && !IsEOF())
throw FormatRuntimeError("Unexpected end of file %s"
" at %" PRIoffset " of %" PRIoffset,
GetURI(), GetOffset(), GetSize());
offset = zzip_tell(file);
return ret;
return nbytes;
}
bool

View File

@@ -50,9 +50,7 @@ gcc_pure
static bool
SkipNameFS(PathTraitsFS::const_pointer_type name_fs) noexcept
{
return name_fs[0] == '.' &&
(name_fs[1] == 0 ||
(name_fs[1] == '.' && name_fs[2] == 0));
return PathTraitsFS::IsSpecialFilename(name_fs);
}
gcc_pure

View File

@@ -148,7 +148,7 @@ handle_status(Client &client, gcc_unused Request args, Response &r)
playlist.GetConsume(),
(unsigned long)playlist.GetVersion(),
playlist.GetLength(),
pc.GetMixRampDb(),
(double)pc.GetMixRampDb(),
state);
if (pc.GetCrossFade() > FloatDuration::zero())

View File

@@ -65,7 +65,7 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
IXML_Document *response;
int code = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(),
0 /*devUDN*/, request, &response);
nullptr /*devUDN*/, request, &response);
if (code != UPNP_E_SUCCESS)
throw FormatRuntimeError("UpnpSendAction() failed: %s",
UpnpGetErrorMessage(code));
@@ -124,7 +124,7 @@ ContentDirectoryService::search(UpnpClient_Handle hdl,
IXML_Document *_response;
auto code = UpnpSendAction(hdl, m_actionURL.c_str(),
m_serviceType.c_str(),
0 /*devUDN*/,
nullptr /*devUDN*/,
request.get(), &_response);
if (code != UPNP_E_SUCCESS)
throw FormatRuntimeError("UpnpSendAction() failed: %s",
@@ -170,7 +170,7 @@ ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
IXML_Document *_response;
auto code = UpnpSendAction(hdl, m_actionURL.c_str(),
m_serviceType.c_str(),
0 /*devUDN*/, request.get(), &_response);
nullptr /*devUDN*/, request.get(), &_response);
if (code != UPNP_E_SUCCESS)
throw FormatRuntimeError("UpnpSendAction() failed: %s",
UpnpGetErrorMessage(code));

View File

@@ -79,7 +79,7 @@ path_in(const char *path, const char *possible_parent) noexcept
}
void
InotifyQueue::Enqueue(const char *uri_utf8)
InotifyQueue::Enqueue(const char *uri_utf8) noexcept
{
delay_event.Schedule(INOTIFY_UPDATE_DELAY);

View File

@@ -35,11 +35,11 @@ class InotifyQueue final {
TimerEvent delay_event;
public:
InotifyQueue(EventLoop &_loop, UpdateService &_update)
InotifyQueue(EventLoop &_loop, UpdateService &_update) noexcept
:update(_update),
delay_event(_loop, BIND_THIS_METHOD(OnDelay)) {}
void Enqueue(const char *uri_utf8);
void Enqueue(const char *uri_utf8) noexcept;
private:
void OnDelay() noexcept;

View File

@@ -24,11 +24,11 @@
#include "system/Error.hxx"
#include "Log.hxx"
#include <cerrno>
#include <climits>
#include <cstdint>
#include <sys/inotify.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <limits.h>
bool
InotifySource::OnSocketReady(gcc_unused unsigned flags) noexcept
@@ -48,7 +48,7 @@ InotifySource::OnSocketReady(gcc_unused unsigned flags) noexcept
while (true) {
const size_t remaining = end - p;
const struct inotify_event *event =
const auto *event =
(const struct inotify_event *)p;
if (remaining < sizeof(*event) ||
remaining < sizeof(*event) + event->len)
@@ -98,7 +98,7 @@ InotifySource::Add(const char *path_fs, unsigned mask)
}
void
InotifySource::Remove(unsigned wd)
InotifySource::Remove(unsigned wd) noexcept
{
auto ifd = GetSocket().ToFileDescriptor();
int ret = inotify_rm_watch(ifd.Get(), wd);

View File

@@ -21,9 +21,6 @@
#define MPD_INOTIFY_SOURCE_HXX
#include "event/SocketMonitor.hxx"
#include "util/Compiler.h"
class FileDescriptor;
typedef void (*mpd_inotify_callback_t)(int wd, unsigned mask,
const char *name, void *ctx);
@@ -45,7 +42,7 @@ public:
InotifySource(EventLoop &_loop,
mpd_inotify_callback_t callback, void *ctx);
~InotifySource() {
~InotifySource() noexcept {
Close();
}
@@ -63,7 +60,7 @@ public:
*
* @param wd the watch descriptor returned by mpd_inotify_source_add()
*/
void Remove(unsigned wd);
void Remove(unsigned wd) noexcept;
private:
bool OnSocketReady(unsigned flags) noexcept override;

View File

@@ -21,19 +21,25 @@
#include "InotifySource.hxx"
#include "InotifyQueue.hxx"
#include "InotifyDomain.hxx"
#include "ExcludeList.hxx"
#include "storage/StorageInterface.hxx"
#include "input/InputStream.hxx"
#include "input/Error.hxx"
#include "fs/AllocatedPath.hxx"
#include "fs/DirectoryReader.hxx"
#include "fs/FileInfo.hxx"
#include "fs/Traits.hxx"
#include "thread/Mutex.hxx"
#include "util/Compiler.h"
#include "Log.hxx"
#include <string>
#include <map>
#include <cassert>
#include <cstring>
#include <forward_list>
#include <map>
#include <string>
#include <assert.h>
#include <sys/inotify.h>
#include <string.h>
#include <dirent.h>
static constexpr unsigned IN_MASK =
@@ -50,17 +56,28 @@ struct WatchDirectory {
int descriptor;
ExcludeList exclude_list;
std::forward_list<WatchDirectory> children;
template<typename N>
WatchDirectory(WatchDirectory *_parent, N &&_name,
WatchDirectory(N &&_name,
int _descriptor)
:parent(_parent), name(std::forward<N>(_name)),
:parent(nullptr), name(std::forward<N>(_name)),
descriptor(_descriptor) {}
template<typename N>
WatchDirectory(WatchDirectory &_parent, N &&_name,
int _descriptor)
:parent(&_parent), name(std::forward<N>(_name)),
descriptor(_descriptor),
exclude_list(_parent.exclude_list) {}
WatchDirectory(const WatchDirectory &) = delete;
WatchDirectory &operator=(const WatchDirectory &) = delete;
void LoadExcludeList(Path directory_path) noexcept;
gcc_pure
unsigned GetDepth() const noexcept;
@@ -68,6 +85,18 @@ struct WatchDirectory {
AllocatedPath GetUriFS() const noexcept;
};
void
WatchDirectory::LoadExcludeList(Path directory_path) noexcept
try {
Mutex mutex;
auto is = InputStream::OpenReady((directory_path / Path::FromFS(".mpdignore")).c_str(),
mutex);
exclude_list.Load(std::move(is));
} catch (...) {
if (!IsFileNotFound(std::current_exception()))
LogError(std::current_exception());
}
static InotifySource *inotify_source;
static InotifyQueue *inotify_queue;
@@ -145,20 +174,19 @@ WatchDirectory::GetUriFS() const noexcept
}
/* we don't look at "." / ".." nor files with newlines in their name */
static bool skip_path(const char *path)
gcc_pure
static bool
SkipFilename(Path name) noexcept
{
return PathTraitsFS::IsSpecialFilename(path) ||
strchr(path, '\n') != nullptr;
return PathTraitsFS::IsSpecialFilename(name.c_str()) ||
name.HasNewline();
}
static void
recursive_watch_subdirectories(WatchDirectory *directory,
const AllocatedPath &path_fs, unsigned depth)
{
DIR *dir;
struct dirent *ent;
assert(directory != nullptr);
recursive_watch_subdirectories(WatchDirectory &parent,
const Path path_fs,
unsigned depth)
try {
assert(depth <= inotify_max_depth);
assert(!path_fs.IsNull());
@@ -167,20 +195,17 @@ recursive_watch_subdirectories(WatchDirectory *directory,
if (depth > inotify_max_depth)
return;
dir = opendir(path_fs.c_str());
if (dir == nullptr) {
FormatErrno(inotify_domain,
"Failed to open directory %s", path_fs.c_str());
return;
}
while ((ent = readdir(dir))) {
DirectoryReader dir(path_fs);
while (dir.ReadEntry()) {
int ret;
if (skip_path(ent->d_name))
const Path name_fs = dir.GetEntry();
if (SkipFilename(name_fs))
continue;
if (parent.exclude_list.Check(name_fs))
continue;
const auto name_fs = Path::FromFS(ent->d_name);
const auto child_path_fs = path_fs / name_fs;
FileInfo fi;
@@ -209,17 +234,18 @@ recursive_watch_subdirectories(WatchDirectory *directory,
/* already being watched */
continue;
directory->children.emplace_front(directory,
name_fs,
ret);
child = &directory->children.front();
parent.children.emplace_front(parent,
name_fs,
ret);
child = &parent.children.front();
child->LoadExcludeList(child_path_fs);
tree_add_watch_directory(child);
recursive_watch_subdirectories(child, child_path_fs, depth);
recursive_watch_subdirectories(*child, child_path_fs, depth);
}
closedir(dir);
} catch (...) {
LogError(std::current_exception());
}
gcc_pure
@@ -240,8 +266,6 @@ mpd_inotify_callback(int wd, unsigned mask,
{
WatchDirectory *directory;
/*FormatDebug(inotify_domain, "wd=%d mask=0x%x name='%s'", wd, mask, name);*/
directory = tree_find_watch_directory(wd);
if (directory == nullptr)
return;
@@ -263,7 +287,7 @@ mpd_inotify_callback(int wd, unsigned mask,
? root
: (root / uri_fs);
recursive_watch_subdirectories(directory, path_fs,
recursive_watch_subdirectories(*directory, path_fs,
directory->GetDepth());
}
@@ -318,11 +342,12 @@ mpd_inotify_init(EventLoop &loop, Storage &storage, UpdateService &update,
return;
}
inotify_root = new WatchDirectory(nullptr, path, descriptor);
inotify_root = new WatchDirectory(path, descriptor);
inotify_root->LoadExcludeList(path);
tree_add_watch_directory(inotify_root);
recursive_watch_subdirectories(inotify_root, path, 0);
recursive_watch_subdirectories(*inotify_root, path, 0);
inotify_queue = new InotifyQueue(loop, update);

View File

@@ -20,8 +20,6 @@
#ifndef MPD_INOTIFY_UPDATE_HXX
#define MPD_INOTIFY_UPDATE_HXX
#include "util/Compiler.h"
class EventLoop;
class Storage;
class UpdateService;

View File

@@ -33,11 +33,11 @@
#include "util/ConstBuffer.hxx"
#include "util/StringBuffer.hxx"
#include <cmath>
#include <stdexcept>
#include <assert.h>
#include <string.h>
#include <math.h>
DecoderBridge::~DecoderBridge()
{
@@ -597,7 +597,7 @@ DecoderBridge::SubmitReplayGain(const ReplayGainInfo *new_replay_gain_info)
const auto &tuple = new_replay_gain_info->Get(rgm);
const auto scale =
tuple.CalculateScale(dc.replay_gain_config);
dc.replay_gain_db = 20.0 * log10f(scale);
dc.replay_gain_db = 20.0f * std::log10(scale);
}
replay_gain_info = *new_replay_gain_info;

View File

@@ -26,7 +26,7 @@
size_t
decoder_read(DecoderClient *client,
InputStream &is,
void *buffer, size_t length)
void *buffer, size_t length) noexcept
{
assert(buffer != nullptr);
@@ -42,9 +42,30 @@ decoder_read(DecoderClient *client,
}
}
size_t
decoder_read_much(DecoderClient *client, InputStream &is,
void *_buffer, size_t size) noexcept
{
uint8_t *buffer = (uint8_t *)_buffer;
size_t total = 0;
while (size > 0 && !is.LockIsEOF()) {
size_t nbytes = decoder_read(client, is, buffer, size);
if (nbytes == 0)
return false;
total += nbytes;
buffer += nbytes;
size -= nbytes;
}
return total;
}
bool
decoder_read_full(DecoderClient *client, InputStream &is,
void *_buffer, size_t size)
void *_buffer, size_t size) noexcept
{
uint8_t *buffer = (uint8_t *)_buffer;
@@ -61,7 +82,7 @@ decoder_read_full(DecoderClient *client, InputStream &is,
}
bool
decoder_skip(DecoderClient *client, InputStream &is, size_t size)
decoder_skip(DecoderClient *client, InputStream &is, size_t size) noexcept
{
while (size > 0) {
char buffer[1024];

View File

@@ -65,15 +65,27 @@ class StopDecoder {};
*/
size_t
decoder_read(DecoderClient *decoder, InputStream &is,
void *buffer, size_t length);
void *buffer, size_t length) noexcept;
static inline size_t
decoder_read(DecoderClient &decoder, InputStream &is,
void *buffer, size_t length)
void *buffer, size_t length) noexcept
{
return decoder_read(&decoder, is, buffer, length);
}
/**
* Blocking read from the input stream. Attempts to fill the buffer
* as much as possible, until either end-of-file is reached or an
* error occurs.
*
* @return the number of bytes read, or 0 if one of the following
* occurs: end of file; error; command (like SEEK or STOP).
*/
size_t
decoder_read_much(DecoderClient *decoder, InputStream &is,
void *buffer, size_t size) noexcept;
/**
* Blocking read from the input stream. Attempts to fill the buffer
* completely; there is no partial result.
@@ -83,7 +95,7 @@ decoder_read(DecoderClient &decoder, InputStream &is,
*/
bool
decoder_read_full(DecoderClient *decoder, InputStream &is,
void *buffer, size_t size);
void *buffer, size_t size) noexcept;
/**
* Skip data on the #InputStream.
@@ -91,6 +103,6 @@ decoder_read_full(DecoderClient *decoder, InputStream &is,
* @return true on success, false on error or command
*/
bool
decoder_skip(DecoderClient *decoder, InputStream &is, size_t size);
decoder_skip(DecoderClient *decoder, InputStream &is, size_t size) noexcept;
#endif

View File

@@ -617,8 +617,8 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen) noexcept
mad_bit_skip(ptr, 16);
lame->peak = mad_f_todouble(mad_bit_read(ptr, 32) << 5); /* peak */
FormatDebug(mad_domain, "LAME peak found: %f", lame->peak);
lame->peak = MAD_F(mad_bit_read(ptr, 32) << 5); /* peak */
FormatDebug(mad_domain, "LAME peak found: %f", double(lame->peak));
lame->track_gain = 0;
unsigned name = mad_bit_read(ptr, 3); /* gain name */
@@ -626,9 +626,9 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen) noexcept
unsigned sign = mad_bit_read(ptr, 1); /* sign bit */
int gain = mad_bit_read(ptr, 9); /* gain*10 */
if (gain && name == 1 && orig != 0) {
lame->track_gain = ((sign ? -gain : gain) / 10.0) + adj;
lame->track_gain = ((sign ? -gain : gain) / 10.0f) + adj;
FormatDebug(mad_domain, "LAME track gain found: %f",
lame->track_gain);
double(lame->track_gain));
}
/* tmz reports that this isn't currently written by any version of lame
@@ -644,7 +644,7 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen) noexcept
if (gain && name == 2 && orig != 0) {
lame->album_gain = ((sign ? -gain : gain) / 10.0) + adj;
FormatDebug(mad_domain, "LAME album gain found: %f",
lame->track_gain);
double(lame->track_gain));
}
#else
mad_bit_skip(ptr, 16);
@@ -778,7 +778,7 @@ MadDecoder::DecodeFirstFrame(Tag *tag) noexcept
/* Album gain isn't currently used. See comment in
* parse_lame() for details. -- jat */
if (client != nullptr && !found_replay_gain &&
lame.track_gain) {
lame.track_gain > 0.0f) {
ReplayGainInfo rgi;
rgi.Clear();
rgi.track.gain = lame.track_gain;

View File

@@ -75,6 +75,12 @@ class MPDOpusDecoder final : public OggDecoder {
OpusDecoder *opus_decoder = nullptr;
opus_int16 *output_buffer = nullptr;
/**
* The output gain from the Opus header. Initialized by
* OnOggBeginning().
*/
signed output_gain;
/**
* The pre-skip value from the Opus header. Initialized by
* OnOggBeginning().
@@ -164,7 +170,7 @@ MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet)
throw std::runtime_error("BOS packet must be OpusHead");
unsigned channels;
if (!ScanOpusHeader(packet.packet, packet.bytes, channels, pre_skip) ||
if (!ScanOpusHeader(packet.packet, packet.bytes, channels, output_gain, pre_skip) ||
!audio_valid_channel_count(channels))
throw std::runtime_error("Malformed BOS packet");
@@ -239,6 +245,15 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet)
ReplayGainInfo rgi;
rgi.Clear();
/**
* Output gain is a Q7.8 fixed point number in dB that should be,
* applied unconditionally, but is often used specifically for
* ReplayGain. Add 5dB to compensate for the different
* reference levels between ReplayGain (89dB) and EBU R128 (-23 LUFS).
*/
rgi.track.gain = float(output_gain) / 256.0f + 5;
rgi.album.gain = float(output_gain) / 256.0f + 5;
TagBuilder tag_builder;
AddTagHandler h(tag_builder);
@@ -384,14 +399,14 @@ mpd_opus_stream_decode(DecoderClient &client,
static bool
ReadAndParseOpusHead(OggSyncState &sync, OggStreamState &stream,
unsigned &channels, unsigned &pre_skip)
unsigned &channels, signed &output_gain, unsigned &pre_skip)
{
ogg_packet packet;
return OggReadPacket(sync, stream, packet) && packet.b_o_s &&
IsOpusHead(packet) &&
ScanOpusHeader(packet.packet, packet.bytes, channels,
pre_skip) &&
output_gain, pre_skip) &&
audio_valid_channel_count(channels);
}
@@ -436,7 +451,8 @@ mpd_opus_scan_stream(InputStream &is, TagHandler &handler)
OggStreamState os(first_page);
unsigned channels, pre_skip;
if (!ReadAndParseOpusHead(oy, os, channels, pre_skip) ||
signed output_gain;
if (!ReadAndParseOpusHead(oy, os, channels, output_gain, pre_skip) ||
!ReadAndVisitOpusTags(oy, os, handler))
return false;

View File

@@ -33,12 +33,14 @@ struct OpusHead {
bool
ScanOpusHeader(const void *data, size_t size, unsigned &channels_r,
unsigned &pre_skip_r)
signed &output_gain_r, unsigned &pre_skip_r)
{
const OpusHead *h = (const OpusHead *)data;
if (size < 19 || (h->version & 0xf0) != 0)
return false;
output_gain_r = FromLE16S(h->output_gain);
channels_r = h->channels;
pre_skip_r = FromLE16(h->pre_skip);
return true;

View File

@@ -24,6 +24,6 @@
bool
ScanOpusHeader(const void *data, size_t size, unsigned &channels_r,
unsigned &pre_skip_r);
signed &output_gain_r, unsigned &pre_skip_r);
#endif

View File

@@ -53,7 +53,7 @@ ScanOneOpusTag(const char *name, const char *value,
char *endptr;
long l = strtol(value, &endptr, 10);
if (endptr > value && *endptr == 0)
rgi->track.gain = double(l) / 256.;
rgi->track.gain += float(l) / 256.0f;
} else if (rgi != nullptr &&
StringEqualsCaseASCII(name, "R128_ALBUM_GAIN")) {
/* R128_ALBUM_GAIN is a Q7.8 fixed point number in
@@ -62,7 +62,7 @@ ScanOneOpusTag(const char *name, const char *value,
char *endptr;
long l = strtol(value, &endptr, 10);
if (endptr > value && *endptr == 0)
rgi->album.gain = double(l) / 256.;
rgi->album.gain += float(l) / 256.0f;
}
handler.OnPair(name, value);

View File

@@ -46,9 +46,7 @@ struct SndfileInputStream {
size_t Read(void *buffer, size_t size) {
/* libsndfile chokes on partial reads; therefore
always force full reads */
return decoder_read_full(client, is, buffer, size)
? size
: 0;
return decoder_read_much(client, is, buffer, size);
}
};

View File

@@ -77,9 +77,9 @@ PreparedLameEncoder::PreparedLameEncoder(const ConfigBlock &block)
if (value != nullptr) {
/* a quality was configured (VBR) */
quality = ParseDouble(value, &endptr);
quality = float(ParseDouble(value, &endptr));
if (*endptr != '\0' || quality < -1.0 || quality > 10.0)
if (*endptr != '\0' || quality < -1.0f || quality > 10.0f)
throw FormatRuntimeError("quality \"%s\" is not a number in the "
"range -1 to 10",
value);
@@ -111,13 +111,13 @@ static void
lame_encoder_setup(lame_global_flags *gfp, float quality, int bitrate,
const AudioFormat &audio_format)
{
if (quality >= -1.0) {
if (quality >= -1.0f) {
/* a quality was configured (VBR) */
if (0 != lame_set_VBR(gfp, vbr_rh))
throw std::runtime_error("error setting lame VBR mode");
if (0 != lame_set_VBR_q(gfp, quality))
if (0 != lame_set_VBR_q(gfp, int(quality)))
throw std::runtime_error("error setting lame VBR quality");
} else {
/* a bit rate was configured */

View File

@@ -95,9 +95,9 @@ PreparedTwolameEncoder::PreparedTwolameEncoder(const ConfigBlock &block)
if (value != nullptr) {
/* a quality was configured (VBR) */
quality = ParseDouble(value, &endptr);
quality = float(ParseDouble(value, &endptr));
if (*endptr != '\0' || quality < -1.0 || quality > 10.0)
if (*endptr != '\0' || quality < -1.0f || quality > 10.0f)
throw FormatRuntimeError("quality \"%s\" is not a number in the "
"range -1 to 10",
value);
@@ -132,7 +132,7 @@ static void
twolame_encoder_setup(twolame_options *options, float quality, int bitrate,
const AudioFormat &audio_format)
{
if (quality >= -1.0) {
if (quality >= -1.0f) {
/* a quality was configured (VBR) */
if (0 != twolame_set_VBR(options, true))

View File

@@ -84,7 +84,7 @@ PreparedVorbisEncoder::PreparedVorbisEncoder(const ConfigBlock &block)
char *endptr;
quality = ParseDouble(value, &endptr);
if (*endptr != '\0' || quality < -1.0 || quality > 10.0)
if (*endptr != '\0' || quality < -1.0f || quality > 10.0f)
throw FormatRuntimeError("quality \"%s\" is not a number in the "
"range -1 to 10",
value);
@@ -122,13 +122,13 @@ VorbisEncoder::VorbisEncoder(float quality, int bitrate,
_audio_format.format = SampleFormat::FLOAT;
audio_format = _audio_format;
if (quality >= -1.0) {
if (quality >= -1.0f) {
/* a quality was configured (VBR) */
if (0 != vorbis_encode_init_vbr(&vi,
audio_format.channels,
audio_format.sample_rate,
quality * 0.1)) {
quality * 0.1f)) {
vorbis_info_clear(&vi);
throw std::runtime_error("error initializing vorbis vbr");
}
@@ -138,7 +138,7 @@ VorbisEncoder::VorbisEncoder(float quality, int bitrate,
if (0 != vorbis_encode_init(&vi,
audio_format.channels,
audio_format.sample_rate, -1.0,
bitrate * 1000, -1.0)) {
bitrate * 1000, -1.0f)) {
vorbis_info_clear(&vi);
throw std::runtime_error("error initializing vorbis encoder");
}

View File

@@ -59,7 +59,7 @@ input_stream_global_init(const ConfigData &config, EventLoop &event_loop)
plugin->init(event_loop, *block);
input_plugins_enabled[i] = true;
} catch (const PluginUnconfigured &e) {
LogFormat(LogLevel::INFO, e,
LogFormat(LogLevel::DEBUG, e,
"Input plugin '%s' is not configured",
plugin->name);
continue;

View File

@@ -305,18 +305,18 @@ ConfigureCapture(snd_pcm_t *capture_handle,
snd_pcm_hw_params_get_buffer_size_min(hw_params, &buffer_size_min);
snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size_max);
unsigned buffer_time_min, buffer_time_max;
snd_pcm_hw_params_get_buffer_time_min(hw_params, &buffer_time_min, 0);
snd_pcm_hw_params_get_buffer_time_max(hw_params, &buffer_time_max, 0);
snd_pcm_hw_params_get_buffer_time_min(hw_params, &buffer_time_min, nullptr);
snd_pcm_hw_params_get_buffer_time_max(hw_params, &buffer_time_max, nullptr);
FormatDebug(alsa_input_domain, "buffer: size=%u..%u time=%u..%u",
(unsigned)buffer_size_min, (unsigned)buffer_size_max,
buffer_time_min, buffer_time_max);
snd_pcm_uframes_t period_size_min, period_size_max;
snd_pcm_hw_params_get_period_size_min(hw_params, &period_size_min, 0);
snd_pcm_hw_params_get_period_size_max(hw_params, &period_size_max, 0);
snd_pcm_hw_params_get_period_size_min(hw_params, &period_size_min, nullptr);
snd_pcm_hw_params_get_period_size_max(hw_params, &period_size_max, nullptr);
unsigned period_time_min, period_time_max;
snd_pcm_hw_params_get_period_time_min(hw_params, &period_time_min, 0);
snd_pcm_hw_params_get_period_time_max(hw_params, &period_time_max, 0);
snd_pcm_hw_params_get_period_time_min(hw_params, &period_time_min, nullptr);
snd_pcm_hw_params_get_period_time_max(hw_params, &period_time_max, nullptr);
FormatDebug(alsa_input_domain, "period: size=%u..%u time=%u..%u",
(unsigned)period_size_min, (unsigned)period_size_max,
period_time_min, period_time_max);

View File

@@ -139,8 +139,9 @@ FfmpegInputStream::Seek(offset_type new_offset)
static constexpr const char *ffmpeg_prefixes[] = {
"gopher://",
"hls+http://",
"hls+https://",
"rtp://",
"rtsp://",
"rtmp://",
"rtmpt://",
"rtmps://",

View File

@@ -238,18 +238,18 @@ SetupHw(snd_pcm_t *pcm,
snd_pcm_hw_params_get_buffer_size_min(hwparams, &buffer_size_min);
snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size_max);
unsigned buffer_time_min, buffer_time_max;
snd_pcm_hw_params_get_buffer_time_min(hwparams, &buffer_time_min, 0);
snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time_max, 0);
snd_pcm_hw_params_get_buffer_time_min(hwparams, &buffer_time_min, nullptr);
snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time_max, nullptr);
FormatDebug(alsa_output_domain, "buffer: size=%u..%u time=%u..%u",
(unsigned)buffer_size_min, (unsigned)buffer_size_max,
buffer_time_min, buffer_time_max);
snd_pcm_uframes_t period_size_min, period_size_max;
snd_pcm_hw_params_get_period_size_min(hwparams, &period_size_min, 0);
snd_pcm_hw_params_get_period_size_max(hwparams, &period_size_max, 0);
snd_pcm_hw_params_get_period_size_min(hwparams, &period_size_min, nullptr);
snd_pcm_hw_params_get_period_size_max(hwparams, &period_size_max, nullptr);
unsigned period_time_min, period_time_max;
snd_pcm_hw_params_get_period_time_min(hwparams, &period_time_min, 0);
snd_pcm_hw_params_get_period_time_max(hwparams, &period_time_max, 0);
snd_pcm_hw_params_get_period_time_min(hwparams, &period_time_min, nullptr);
snd_pcm_hw_params_get_period_time_max(hwparams, &period_time_max, nullptr);
FormatDebug(alsa_output_domain, "period: size=%u..%u time=%u..%u",
(unsigned)period_size_min, (unsigned)period_size_max,
period_time_min, period_time_max);

View File

@@ -18,7 +18,7 @@ if icu_dep.found()
'Init.cxx',
]
elif not get_option('iconv').disabled()
have_iconv = compiler.has_function('iconv')
have_iconv = compiler.has_function('iconv', prefix : '#include <iconv.h>')
conf.set('HAVE_ICONV', have_iconv)
if not have_iconv and get_option('iconv').enabled()
error('iconv() not available')

View File

@@ -60,7 +60,7 @@ ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
IXML_Document *_response;
auto code = UpnpSendAction(hdl, m_actionURL.c_str(),
m_serviceType.c_str(),
0 /*devUDN*/, request.get(), &_response);
nullptr /*devUDN*/, request.get(), &_response);
if (code != UPNP_E_SUCCESS)
throw FormatRuntimeError("UpnpSendAction() failed: %s",
UpnpGetErrorMessage(code));

View File

@@ -27,7 +27,7 @@
MixerType
mixer_type_parse(const char *input)
{
assert(input != NULL);
assert(input != nullptr);
if (strcmp(input, "none") == 0 || strcmp(input, "disabled") == 0)
return MixerType::NONE;

View File

@@ -51,7 +51,7 @@ public:
double _volume_scale_factor)
:Mixer(pulse_mixer_plugin, _listener),
output(_output),
volume_scale_factor(_volume_scale_factor)
volume_scale_factor(float(_volume_scale_factor))
{
}
@@ -175,7 +175,7 @@ parse_volume_scale_factor(const char *value) {
char *endptr;
float factor = ParseFloat(value, &endptr);
if (endptr == value || *endptr != '\0' || factor < 0.5 || factor > 5.0)
if (endptr == value || *endptr != '\0' || factor < 0.5f || factor > 5.0f)
throw FormatRuntimeError("\"%s\" is not a number in the "
"range 0.5 to 5.0",
value);
@@ -190,7 +190,7 @@ pulse_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao,
{
PulseOutput &po = (PulseOutput &)ao;
float scale = parse_volume_scale_factor(block.GetBlockValue("scale_volume"));
PulseMixer *pm = new PulseMixer(po, listener, scale);
auto *pm = new PulseMixer(po, listener, (double)scale);
pulse_output_set_mixer(po, *pm);
@@ -216,7 +216,7 @@ PulseMixer::GetVolume()
int
PulseMixer::GetVolumeInternal()
{
pa_volume_t max_pa_volume = volume_scale_factor * PA_VOLUME_NORM;
pa_volume_t max_pa_volume = pa_volume_t(volume_scale_factor * PA_VOLUME_NORM);
return online ?
(int)((100 * (pa_cvolume_avg(&volume) + 1)) / max_pa_volume)
: -1;
@@ -230,7 +230,7 @@ PulseMixer::SetVolume(unsigned new_volume)
if (!online)
throw std::runtime_error("disconnected");
pa_volume_t max_pa_volume = volume_scale_factor * PA_VOLUME_NORM;
pa_volume_t max_pa_volume = pa_volume_t(volume_scale_factor * PA_VOLUME_NORM);
struct pa_cvolume cvolume;
pa_cvolume_set(&cvolume, volume.channels,

View File

@@ -22,8 +22,8 @@
#include "filter/plugins/VolumeFilterPlugin.hxx"
#include "pcm/Volume.hxx"
#include <assert.h>
#include <math.h>
#include <cassert>
#include <cmath>
class SoftwareMixer final : public Mixer {
Filter *filter = nullptr;
@@ -70,13 +70,13 @@ PercentVolumeToSoftwareVolume(unsigned volume) noexcept
{
assert(volume <= 100);
if (volume >= 100)
if (volume == 100)
return PCM_VOLUME_1;
else if (volume > 0)
return pcm_float_to_volume((exp(volume / 25.0) - 1) /
return pcm_float_to_volume((std::exp(volume / 25.0f) - 1) /
(54.5981500331F - 1));
else
return 0;
return 0;
}
void

View File

@@ -103,11 +103,6 @@ NeighborExplorer::List
SmbclientNeighborExplorer::GetList() const noexcept
{
const std::lock_guard<Mutex> protect(mutex);
/*
List list;
for (const auto &i : servers)
list.emplace_front(i.Export());
*/
return list;
}

View File

@@ -187,7 +187,7 @@ AudioOutputSource::FilterChunk(const MusicChunk &chunk)
only if the mix ratio is non-negative; a
negative mix ratio is a MixRamp special
case */
mix_ratio = 1.0 - mix_ratio;
mix_ratio = 1.0f - mix_ratio;
void *dest = cross_fade_buffer.Get(other_data.size);
memcpy(dest, other_data.data, other_data.size);

View File

@@ -70,7 +70,7 @@ audio_output_state_read(const char *line, MultipleOutputs &outputs)
name = endptr + 1;
auto *ao = outputs.FindByName(name);
if (ao == NULL) {
if (ao == nullptr) {
FormatDebug(output_domain,
"Ignoring device state for '%s'", name);
return true;

View File

@@ -477,7 +477,7 @@ osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc)
}
static void
osx_output_hog_device(AudioDeviceID dev_id, bool hog)
osx_output_hog_device(AudioDeviceID dev_id, bool hog) noexcept
{
static constexpr AudioObjectPropertyAddress aopa = {
kAudioDevicePropertyHogMode,
@@ -485,8 +485,16 @@ osx_output_hog_device(AudioDeviceID dev_id, bool hog)
kAudioObjectPropertyElementMaster
};
pid_t hog_pid = AudioObjectGetPropertyDataT<pid_t>(dev_id,
aopa);
pid_t hog_pid;
try {
hog_pid = AudioObjectGetPropertyDataT<pid_t>(dev_id, aopa);
} catch (...) {
Log(LogLevel::DEBUG, std::current_exception(),
"Failed to query HogMode");
return;
}
if (hog) {
if (hog_pid != -1) {
FormatDebug(osx_output_domain,

View File

@@ -173,12 +173,12 @@ SlesOutput::Open(AudioFormat &audio_format)
if (audio_format.channels > 2)
audio_format.channels = 1;
SLDataFormat_PCM format_pcm;
SLAndroidDataFormat_PCM_EX format_pcm;
format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = audio_format.channels;
/* from the Android NDK docs: "Note that the field samplesPerSec is
actually in units of milliHz, despite the misleading name." */
format_pcm.samplesPerSec = audio_format.sample_rate * 1000u;
format_pcm.sampleRate = audio_format.sample_rate * 1000u;
format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
format_pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
format_pcm.channelMask = audio_format.channels == 1
@@ -187,6 +187,36 @@ SlesOutput::Open(AudioFormat &audio_format)
format_pcm.endianness = IsLittleEndian()
? SL_BYTEORDER_LITTLEENDIAN
: SL_BYTEORDER_BIGENDIAN;
format_pcm.representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
switch (audio_format.format) {
/* note: Android doesn't support
SL_PCMSAMPLEFORMAT_FIXED_24 and
SL_PCMSAMPLEFORMAT_FIXED_32, so let's not bother
implement it here; SL_PCMSAMPLEFORMAT_FIXED_8
appears to be unsigned, so not usable for us (and
converting S8 to U8 is not worth the trouble) */
case SampleFormat::S16:
/* bitsPerSample and containerSize already set for 16
bit */
break;
case SampleFormat::FLOAT:
/* Android has an OpenSLES extension for floating
point samples:
https://developer.android.com/ndk/guides/audio/opensl/android-extensions */
format_pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
format_pcm.bitsPerSample = format_pcm.containerSize =
SL_PCMSAMPLEFORMAT_FIXED_32;
format_pcm.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
break;
default:
/* fall back to 16 bit */
audio_format.format = SampleFormat::S16;
break;
}
SLDataSource audioSrc = { &loc_bufq, &format_pcm };
@@ -291,9 +321,6 @@ SlesOutput::Open(AudioFormat &audio_format)
n_queued = 0;
next = 0;
filled = 0;
// TODO: support other sample formats
audio_format.format = SampleFormat::S16;
}
void

View File

@@ -53,7 +53,7 @@ struct IntegerToFloatSampleConvert {
typedef typename SrcTraits::value_type SV;
typedef typename DstTraits::value_type DV;
static constexpr DV factor = 1.0 / FloatToIntegerSampleConvert<F, Traits>::factor;
static constexpr DV factor = 1.0f / FloatToIntegerSampleConvert<F, Traits>::factor;
static_assert(factor > 0, "Wrong factor");
static constexpr DV Convert(SV src) noexcept {

View File

@@ -26,6 +26,8 @@
#include "PcmDither.cxx" // including the .cxx file to get inlined templates
#include <cmath>
#include <assert.h>
template<SampleFormat F, class Traits=SampleTraits<F>>
@@ -221,7 +223,7 @@ pcm_mix(PcmDither &dither, void *buffer1, const void *buffer2, size_t size,
if (portion1 < 0)
return pcm_add(buffer1, buffer2, size, format);
s = sin(M_PI_2 * portion1);
s = std::sin((float)M_PI_2 * portion1);
s *= s;
int vol1 = lround(s * PCM_VOLUME_1S);

View File

@@ -122,7 +122,7 @@ SoxrPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate)
ratio = float(new_sample_rate) / float(af.sample_rate);
FormatDebug(soxr_domain,
"samplerate conversion ratio to %.2lf",
ratio);
double(ratio));
/* libsoxr works with floating point samples */
af.format = SampleFormat::FLOAT;

View File

@@ -48,7 +48,7 @@ static constexpr int PCM_VOLUME_1S = PCM_VOLUME_1;
static constexpr inline int
pcm_float_to_volume(float volume) noexcept
{
return volume * PCM_VOLUME_1 + 0.5;
return int(volume * PCM_VOLUME_1 + 0.5f);
}
static constexpr inline float

View File

@@ -440,7 +440,7 @@ Player::ActivateDecoder() noexcept
pc.audio_format.Clear();
{
/* call syncPlaylistWithQueue() in the main thread */
/* call playlist::SyncWithPlayer() in the main thread */
const ScopeUnlock unlock(pc.mutex);
pc.listener.OnPlayerSync();
}
@@ -681,6 +681,12 @@ Player::SeekDecoder() noexcept
/* re-fill the buffer after seeking */
buffering = true;
{
/* call syncPlaylistWithQueue() in the main thread */
const ScopeUnlock unlock(pc.mutex);
pc.listener.OnPlayerSync();
}
return true;
}
@@ -1169,6 +1175,11 @@ try {
{
const ScopeUnlock unlock(mutex);
do_play(*this, dc, buffer);
/* give the main thread a chance to
queue another song, just in case
we've stopped playback
spuriously */
listener.OnPlayerSync();
}

View File

@@ -92,7 +92,8 @@ playlist_state_save(BufferedOutputStream &os, const struct playlist &playlist,
os.Format(PLAYLIST_STATE_FILE_CONSUME "%i\n", playlist.queue.consume);
os.Format(PLAYLIST_STATE_FILE_CROSSFADE "%i\n",
(int)pc.GetCrossFade().count());
os.Format(PLAYLIST_STATE_FILE_MIXRAMPDB "%f\n", pc.GetMixRampDb());
os.Format(PLAYLIST_STATE_FILE_MIXRAMPDB "%f\n",
(double)pc.GetMixRampDb());
os.Format(PLAYLIST_STATE_FILE_MIXRAMPDELAY "%f\n",
pc.GetMixRampDelay().count());
os.Write(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "\n");

View File

@@ -307,9 +307,7 @@ gcc_pure
static bool
SkipNameFS(PathTraitsFS::const_pointer_type name) noexcept
{
return name[0] == '.' &&
(name[1] == 0 ||
(name[1] == '.' && name[2] == 0));
return PathTraitsFS::IsSpecialFilename(name);
}
static void

View File

@@ -144,11 +144,9 @@ SmbclientStorage::OpenDirectory(const char *uri_utf8)
gcc_pure
static bool
SkipNameFS(const char *name) noexcept
SkipNameFS(PathTraitsFS::const_pointer_type name) noexcept
{
return name[0] == '.' &&
(name[1] == 0 ||
(name[1] == '.' && name[2] == 0));
return PathTraitsFS::IsSpecialFilename(name);
}
SmbclientDirectoryReader::~SmbclientDirectoryReader()

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -28,8 +28,10 @@
*/
#include "FileDescriptor.hxx"
#include "system/Error.hxx"
#include <assert.h>
#include <stdint.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -278,6 +280,42 @@ FileDescriptor::GetSize() const noexcept
: -1;
}
void
FileDescriptor::FullRead(void *_buffer, size_t length)
{
uint8_t *buffer = (uint8_t *)_buffer;
while (length > 0) {
ssize_t nbytes = Read(buffer, length);
if (nbytes <= 0) {
if (nbytes < 0)
throw MakeErrno("Failed to read");
throw std::runtime_error("Unexpected end of file");
}
buffer += nbytes;
length -= nbytes;
}
}
void
FileDescriptor::FullWrite(const void *_buffer, size_t length)
{
const uint8_t *buffer = (const uint8_t *)_buffer;
while (length > 0) {
ssize_t nbytes = Write(buffer, length);
if (nbytes <= 0) {
if (nbytes < 0)
throw MakeErrno("Failed to write");
throw std::runtime_error("Failed to write");
}
buffer += nbytes;
length -= nbytes;
}
}
#ifndef _WIN32
int

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 Max Kellermann <max.kellermann@gmail.com>
* Copyright 2012-2019 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -227,10 +227,22 @@ public:
return ::read(fd, buffer, length);
}
/**
* Read until all of the given buffer has been filled. Throws
* on error.
*/
void FullRead(void *buffer, size_t length);
ssize_t Write(const void *buffer, size_t length) noexcept {
return ::write(fd, buffer, length);
}
/**
* Write until all of the given buffer has been written.
* Throws on error.
*/
void FullWrite(const void *buffer, size_t length);
#ifndef _WIN32
int Poll(short events, int timeout) const noexcept;

View File

@@ -42,7 +42,7 @@ HandleShutdownSignal(void *ctx) noexcept
static void
x_sigaction(int signum, const struct sigaction *act)
{
if (sigaction(signum, act, NULL) < 0)
if (sigaction(signum, act, nullptr) < 0)
throw MakeErrno("sigaction() failed");
}

View File

@@ -74,39 +74,39 @@
#endif
constexpr bool
IsLittleEndian()
IsLittleEndian() noexcept
{
return IS_LITTLE_ENDIAN;
}
constexpr bool
IsBigEndian()
IsBigEndian() noexcept
{
return IS_BIG_ENDIAN;
}
constexpr uint16_t
GenericByteSwap16(uint16_t value)
GenericByteSwap16(uint16_t value) noexcept
{
return (value >> 8) | (value << 8);
}
constexpr uint32_t
GenericByteSwap32(uint32_t value)
GenericByteSwap32(uint32_t value) noexcept
{
return (value >> 24) | ((value >> 8) & 0x0000ff00) |
((value << 8) & 0x00ff0000) | (value << 24);
}
constexpr uint64_t
GenericByteSwap64(uint64_t value)
GenericByteSwap64(uint64_t value) noexcept
{
return uint64_t(GenericByteSwap32(uint32_t(value >> 32)))
| (uint64_t(GenericByteSwap32(value)) << 32);
}
constexpr uint16_t
ByteSwap16(uint16_t value)
ByteSwap16(uint16_t value) noexcept
{
#if CLANG_OR_GCC_VERSION(4,8)
return __builtin_bswap16(value);
@@ -116,7 +116,7 @@ ByteSwap16(uint16_t value)
}
constexpr uint32_t
ByteSwap32(uint32_t value)
ByteSwap32(uint32_t value) noexcept
{
#if CLANG_OR_GCC_VERSION(4,3)
return __builtin_bswap32(value);
@@ -126,7 +126,7 @@ ByteSwap32(uint32_t value)
}
constexpr uint64_t
ByteSwap64(uint64_t value)
ByteSwap64(uint64_t value) noexcept
{
#if CLANG_OR_GCC_VERSION(4,3)
return __builtin_bswap64(value);
@@ -139,7 +139,7 @@ ByteSwap64(uint64_t value)
* Converts a 16bit value from big endian to the system's byte order
*/
constexpr uint16_t
FromBE16(uint16_t value)
FromBE16(uint16_t value) noexcept
{
return IsBigEndian() ? value : ByteSwap16(value);
}
@@ -148,7 +148,7 @@ FromBE16(uint16_t value)
* Converts a 32bit value from big endian to the system's byte order
*/
constexpr uint32_t
FromBE32(uint32_t value)
FromBE32(uint32_t value) noexcept
{
return IsBigEndian() ? value : ByteSwap32(value);
}
@@ -157,7 +157,7 @@ FromBE32(uint32_t value)
* Converts a 64bit value from big endian to the system's byte order
*/
constexpr uint64_t
FromBE64(uint64_t value)
FromBE64(uint64_t value) noexcept
{
return IsBigEndian() ? value : ByteSwap64(value);
}
@@ -166,7 +166,7 @@ FromBE64(uint64_t value)
* Converts a 16bit value from little endian to the system's byte order
*/
constexpr uint16_t
FromLE16(uint16_t value)
FromLE16(uint16_t value) noexcept
{
return IsLittleEndian() ? value : ByteSwap16(value);
}
@@ -175,7 +175,7 @@ FromLE16(uint16_t value)
* Converts a 32bit value from little endian to the system's byte order
*/
constexpr uint32_t
FromLE32(uint32_t value)
FromLE32(uint32_t value) noexcept
{
return IsLittleEndian() ? value : ByteSwap32(value);
}
@@ -184,7 +184,7 @@ FromLE32(uint32_t value)
* Converts a 64bit value from little endian to the system's byte order
*/
constexpr uint64_t
FromLE64(uint64_t value)
FromLE64(uint64_t value) noexcept
{
return IsLittleEndian() ? value : ByteSwap64(value);
}
@@ -193,7 +193,7 @@ FromLE64(uint64_t value)
* Converts a 16bit value from the system's byte order to big endian
*/
constexpr uint16_t
ToBE16(uint16_t value)
ToBE16(uint16_t value) noexcept
{
return IsBigEndian() ? value : ByteSwap16(value);
}
@@ -202,7 +202,7 @@ ToBE16(uint16_t value)
* Converts a 32bit value from the system's byte order to big endian
*/
constexpr uint32_t
ToBE32(uint32_t value)
ToBE32(uint32_t value) noexcept
{
return IsBigEndian() ? value : ByteSwap32(value);
}
@@ -211,7 +211,7 @@ ToBE32(uint32_t value)
* Converts a 64bit value from the system's byte order to big endian
*/
constexpr uint64_t
ToBE64(uint64_t value)
ToBE64(uint64_t value) noexcept
{
return IsBigEndian() ? value : ByteSwap64(value);
}
@@ -220,7 +220,7 @@ ToBE64(uint64_t value)
* Converts a 16bit value from the system's byte order to little endian
*/
constexpr uint16_t
ToLE16(uint16_t value)
ToLE16(uint16_t value) noexcept
{
return IsLittleEndian() ? value : ByteSwap16(value);
}
@@ -229,7 +229,7 @@ ToLE16(uint16_t value)
* Converts a 32bit value from the system's byte order to little endian
*/
constexpr uint32_t
ToLE32(uint32_t value)
ToLE32(uint32_t value) noexcept
{
return IsLittleEndian() ? value : ByteSwap32(value);
}
@@ -238,9 +238,20 @@ ToLE32(uint32_t value)
* Converts a 64bit value from the system's byte order to little endian
*/
constexpr uint64_t
ToLE64(uint64_t value)
ToLE64(uint64_t value) noexcept
{
return IsLittleEndian() ? value : ByteSwap64(value);
}
/**
* Converts a 16 bit integer from little endian to the host byte order
* and returns it as a signed integer.
*/
constexpr int16_t
FromLE16S(uint16_t value) noexcept
{
/* assuming two's complement representation */
return static_cast<int16_t>(FromLE16(value));
}
#endif

View File

@@ -28,7 +28,7 @@ FormatStringV(const char *fmt, va_list args) noexcept
{
va_list tmp;
va_copy(tmp, args);
const int length = vsnprintf(NULL, 0, fmt, tmp);
const int length = vsnprintf(nullptr, 0, fmt, tmp);
va_end(tmp);
if (length <= 0)

View File

@@ -112,7 +112,7 @@ try {
const char *const plugin_name = argv[2];
const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
if (plugin == NULL) {
if (plugin == nullptr) {
cerr << "No such database plugin: " << plugin_name << endl;
return EXIT_FAILURE;
}

View File

@@ -41,9 +41,9 @@ static void
tag_save(FILE *file, const Tag &tag)
{
StdioOutputStream sos(file);
BufferedOutputStream bos(sos);
tag_save(bos, tag);
bos.Flush();
WithBufferedOutputStream(sos, [&](auto &bos){
tag_save(bos, tag);
});
}
int main(int argc, char **argv)
@@ -75,7 +75,7 @@ try {
InputStreamPtr is;
auto playlist = playlist_list_open_uri(uri, mutex);
if (playlist == NULL) {
if (playlist == nullptr) {
/* open the stream and wait until it becomes ready */
is = InputStream::OpenReady(uri, mutex);
@@ -83,7 +83,7 @@ try {
/* open the playlist */
playlist = playlist_list_open_stream(std::move(is), uri);
if (playlist == NULL) {
if (playlist == nullptr) {
fprintf(stderr, "Failed to open playlist\n");
return 2;
}
@@ -92,7 +92,7 @@ try {
/* dump the playlist */
std::unique_ptr<DetachedSong> song;
while ((song = playlist->NextSong()) != NULL) {
while ((song = playlist->NextSong()) != nullptr) {
printf("%s\n", song->GetURI());
const unsigned start_ms = song->GetStartTime().ToMS();

View File

@@ -70,7 +70,7 @@ try {
auto is = OpenLocalInputStream(path, mutex);
const auto tag = tag_id3_load(*is);
if (tag == NULL) {
if (tag == nullptr) {
fprintf(stderr, "No ID3 tag found\n");
return EXIT_FAILURE;
}

View File

@@ -624,6 +624,7 @@ executable(
dependencies: [
output_glue_dep,
encoder_glue_dep,
event_dep,
],
)

View File

@@ -36,7 +36,7 @@ const FilterPlugin *
filter_plugin_by_name(gcc_unused const char *name) noexcept
{
assert(false);
return NULL;
return nullptr;
}
int main(int argc, gcc_unused char **argv)

View File

@@ -100,7 +100,7 @@ try {
const ScopeDecoderPluginsInit decoder_plugins_init({});
plugin = decoder_plugin_from_name(decoder_name);
if (plugin == NULL) {
if (plugin == nullptr) {
fprintf(stderr, "No such decoder: %s\n", decoder_name);
return EXIT_FAILURE;
}
@@ -117,7 +117,7 @@ try {
Mutex mutex;
InputStreamPtr is;
if (!success && plugin->scan_stream != NULL) {
if (!success && plugin->scan_stream != nullptr) {
is = InputStream::OpenReady(path, mutex);
success = plugin->ScanStream(*is, h);
}

View File

@@ -55,7 +55,7 @@ try {
/* create the encoder */
const auto plugin = encoder_plugin_get(encoder_name);
if (plugin == NULL) {
if (plugin == nullptr) {
fprintf(stderr, "No such encoder: %s\n", encoder_name);
return EXIT_FAILURE;
}

View File

@@ -27,6 +27,8 @@
#include "filter/Prepared.hxx"
#include "pcm/Volume.hxx"
#include "mixer/MixerControl.hxx"
#include "system/Error.hxx"
#include "system/FileDescriptor.hxx"
#include "util/ConstBuffer.hxx"
#include "util/StringBuffer.hxx"
#include "util/RuntimeError.hxx"
@@ -39,8 +41,6 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
void
mixer_set_volume(gcc_unused Mixer *mixer,
@@ -53,17 +53,44 @@ LoadFilter(const ConfigData &config, const char *name)
{
const auto *param = config.FindBlock(ConfigBlockOption::AUDIO_FILTER,
"name", name);
if (param == NULL)
if (param == nullptr)
throw FormatRuntimeError("No such configured filter: %s",
name);
return filter_configured_new(*param);
}
static size_t
ReadOrThrow(FileDescriptor fd, void *buffer, size_t size)
{
auto nbytes = fd.Read(buffer, size);
if (nbytes < 0)
throw MakeErrno("Read failed");
return nbytes;
}
static size_t
ReadFrames(FileDescriptor fd, void *_buffer, size_t size, size_t frame_size)
{
auto buffer = (uint8_t *)_buffer;
size = (size / frame_size) * frame_size;
size_t nbytes = ReadOrThrow(fd, buffer, size);
const size_t modulo = nbytes % frame_size;
if (modulo > 0) {
size_t rest = frame_size - modulo;
fd.FullRead(buffer + nbytes, rest);
nbytes += rest;
}
return nbytes;
}
int main(int argc, char **argv)
try {
char buffer[4096];
if (argc < 3 || argc > 4) {
fprintf(stderr, "Usage: run_filter CONFIG NAME [FORMAT] <IN\n");
return EXIT_FAILURE;
@@ -82,6 +109,8 @@ try {
if (argc > 3)
audio_format = ParseAudioFormat(argv[3], false);
const size_t in_frame_size = audio_format.GetFrameSize();
/* initialize the filter */
auto prepared_filter = LoadFilter(config, argv[2]);
@@ -97,21 +126,26 @@ try {
/* play */
while (true) {
ssize_t nbytes;
FileDescriptor input_fd(STDIN_FILENO);
FileDescriptor output_fd(STDOUT_FILENO);
nbytes = read(0, buffer, sizeof(buffer));
if (nbytes <= 0)
while (true) {
char buffer[4096];
ssize_t nbytes = ReadFrames(input_fd, buffer, sizeof(buffer),
in_frame_size);
if (nbytes == 0)
break;
auto dest = filter->FilterPCM({(const void *)buffer, (size_t)nbytes});
output_fd.FullWrite(dest.data, dest.size);
}
nbytes = write(1, dest.data, dest.size);
if (nbytes < 0) {
fprintf(stderr, "Failed to write: %s\n",
strerror(errno));
return 1;
}
while (true) {
auto dest = filter->Flush();
if (dest.IsNull())
break;
output_fd.FullWrite(dest.data, dest.size);
}
/* cleanup and exit */

View File

@@ -49,11 +49,15 @@
#include <unistd.h>
#include <stdlib.h>
static constexpr std::size_t MAX_CHUNK_SIZE = 16384;
struct CommandLine {
const char *uri = nullptr;
FromNarrowPath config_path;
std::size_t chunk_size = MAX_CHUNK_SIZE;
bool verbose = false;
bool scan = false;
@@ -63,14 +67,27 @@ enum Option {
OPTION_CONFIG,
OPTION_VERBOSE,
OPTION_SCAN,
OPTION_CHUNK_SIZE,
};
static constexpr OptionDef option_defs[] = {
{"config", 0, true, "Load a MPD configuration file"},
{"verbose", 'v', false, "Verbose logging"},
{"scan", 0, false, "Scan tags instead of reading raw data"},
{"chunk-size", 0, true, "Read this number of bytes at a time"},
};
static std::size_t
ParseSize(const char *s)
{
char *endptr;
std::size_t value = std::strtoul(s, &endptr, 10);
if (endptr == s)
throw std::runtime_error("Failed to parse integer");
return value;
}
static CommandLine
ParseCommandLine(int argc, char **argv)
{
@@ -90,6 +107,12 @@ ParseCommandLine(int argc, char **argv)
case OPTION_SCAN:
c.scan = true;
break;
case OPTION_CHUNK_SIZE:
c.chunk_size = ParseSize(o.value);
if (c.chunk_size <= 0 || c.chunk_size > MAX_CHUNK_SIZE)
throw std::runtime_error("Invalid chunk size");
break;
}
}
@@ -124,43 +147,42 @@ static void
tag_save(FILE *file, const Tag &tag)
{
StdioOutputStream sos(file);
BufferedOutputStream bos(sos);
tag_save(bos, tag);
bos.Flush();
WithBufferedOutputStream(sos, [&](auto &bos){
tag_save(bos, tag);
});
}
static int
dump_input_stream(InputStream *is)
dump_input_stream(InputStream &is, FileDescriptor out, size_t chunk_size)
{
const std::lock_guard<Mutex> protect(is->mutex);
const std::lock_guard<Mutex> protect(is.mutex);
/* print meta data */
if (is->HasMimeType())
fprintf(stderr, "MIME type: %s\n", is->GetMimeType());
if (is.HasMimeType())
fprintf(stderr, "MIME type: %s\n", is.GetMimeType());
/* read data and tags from the stream */
while (!is->IsEOF()) {
while (!is.IsEOF()) {
{
auto tag = is->ReadTag();
auto tag = is.ReadTag();
if (tag) {
fprintf(stderr, "Received a tag:\n");
tag_save(stderr, *tag);
}
}
char buffer[4096];
size_t num_read = is->Read(buffer, sizeof(buffer));
char buffer[MAX_CHUNK_SIZE];
assert(chunk_size <= sizeof(buffer));
size_t num_read = is.Read(buffer, chunk_size);
if (num_read == 0)
break;
ssize_t num_written = write(1, buffer, num_read);
if (num_written <= 0)
break;
out.FullWrite(buffer, num_read);
}
is->Check();
is.Check();
return 0;
}
@@ -234,7 +256,8 @@ try {
Mutex mutex;
auto is = InputStream::OpenReady(c.uri, mutex);
return dump_input_stream(is.get());
return dump_input_stream(*is, FileDescriptor(STDOUT_FILENO),
c.chunk_size);
} catch (...) {
PrintException(std::current_exception());
return EXIT_FAILURE;

View File

@@ -7,4 +7,7 @@ DST="$(pwd)/test/tmp/${SRC_BASE}.iso"
mkdir -p test/tmp
rm -f "$DST"
mkisofs -quiet -l -o "$DST" "$SRC"
./test/run_input "$DST/${SRC_BASE}" |diff "$SRC" -
# Using an odd chunk size to check whether the plugin can cope with
# partial sectors
./test/run_input --chunk-size=1337 "$DST/${SRC_BASE}" |diff "$SRC" -

View File

@@ -41,7 +41,7 @@ try {
/* create the encoder */
const auto plugin = encoder_plugin_get("vorbis");
assert(plugin != NULL);
assert(plugin != nullptr);
ConfigBlock block;
block.AddBlockParam("quality", "5.0", -1);

View File

@@ -66,6 +66,11 @@ class CrossGccToolchain:
' -static-libstdc++ -static-libgcc'
self.libs = ''
# Explicitly disable _FORTIFY_SOURCE because it is broken with
# mingw. This prevents some libraries such as libFLAC to
# enable it.
self.cppflags += ' -D_FORTIFY_SOURCE=0'
self.is_arm = arch.startswith('arm')
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
self.is_aarch64 = arch == 'aarch64'