Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
566787f041 | ||
|
|
79b2366387 | ||
|
|
5acea014b0 | ||
|
|
b72801abf3 | ||
|
|
23d5a2b862 | ||
|
|
7715311117 | ||
|
|
4c1cfca95b | ||
|
|
e113ce9621 | ||
|
|
e8213220e2 | ||
|
|
83f9d2a963 | ||
|
|
bf97ebf89f | ||
|
|
5b22d27cbb | ||
|
|
e907ff43ae | ||
|
|
b18fc3a8d0 | ||
|
|
a8e23c4140 | ||
|
|
fc3861b421 | ||
|
|
e81bb5d8f1 | ||
|
|
32f4f15831 | ||
|
|
e29c06b718 | ||
|
|
d9d511f33e | ||
|
|
c61a3b8d13 | ||
|
|
e10b867fe6 | ||
|
|
43e230f543 | ||
|
|
b2ae5298a7 | ||
|
|
17dd21ac7f | ||
|
|
1a5e0ef7c9 | ||
|
|
979a7a1dcc | ||
|
|
962cf32ba7 | ||
|
|
ae23682372 | ||
|
|
540919f256 | ||
|
|
398281cd76 | ||
|
|
88446ccde9 | ||
|
|
6238cc0734 | ||
|
|
fd4823c507 | ||
|
|
68bcfd8bf0 | ||
|
|
1d332746af | ||
|
|
f3e133c617 | ||
|
|
1678a6eb59 | ||
|
|
b4dc2c07d5 | ||
|
|
d7838950d8 | ||
|
|
2e93a83dd5 | ||
|
|
db8b419b8c | ||
|
|
990f631cbc | ||
|
|
db46d84458 | ||
|
|
9e6c4f8d80 | ||
|
|
41b47f95c5 | ||
|
|
15939fd87c | ||
|
|
f63c343f68 | ||
|
|
1a516e7744 | ||
|
|
5c9d97775f | ||
|
|
64aadcd13f | ||
|
|
1f6a7d6462 | ||
|
|
e44b953d9a | ||
|
|
6c85020630 | ||
|
|
9d910320f3 | ||
|
|
c53074efc9 | ||
|
|
3b51c53eca | ||
|
|
0aa0ffb67b | ||
|
|
33f70931dd | ||
|
|
8830ea319f | ||
|
|
cbcdc73f9a | ||
|
|
4f6c54ecb3 | ||
|
|
2bdf1b2284 | ||
|
|
c876d6a51c | ||
|
|
3c745b4bc6 | ||
|
|
3a08a6ad72 | ||
|
|
448b397cb8 | ||
|
|
64a1386eb6 | ||
|
|
77c2efe171 | ||
|
|
587c0f6232 | ||
|
|
64e8abf203 | ||
|
|
6c40d2a656 | ||
|
|
cf674e9273 | ||
|
|
9bda0379af |
20
NEWS
20
NEWS
@@ -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
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
#ifndef MPD_INOTIFY_UPDATE_HXX
|
||||
#define MPD_INOTIFY_UPDATE_HXX
|
||||
|
||||
#include "util/Compiler.h"
|
||||
|
||||
class EventLoop;
|
||||
class Storage;
|
||||
class UpdateService;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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://",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -624,6 +624,7 @@ executable(
|
||||
dependencies: [
|
||||
output_glue_dep,
|
||||
encoder_glue_dep,
|
||||
event_dep,
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" -
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user