Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
79f2f8cddc | ||
![]() |
39fa949345 | ||
![]() |
e1d7a5cbf5 | ||
![]() |
f3cefaf043 | ||
![]() |
b3460f3f54 | ||
![]() |
1e0ad1f6bf | ||
![]() |
4abcb08cc9 | ||
![]() |
81e7833711 | ||
![]() |
82e261ad33 | ||
![]() |
cae2811762 | ||
![]() |
09112c6869 | ||
![]() |
77aaf1baee | ||
![]() |
6626c2d00d | ||
![]() |
315f9d98f6 | ||
![]() |
f087518e7a | ||
![]() |
db9997a106 | ||
![]() |
0cbfb610f2 | ||
![]() |
f901cd042b | ||
![]() |
5719207dfa | ||
![]() |
a84fbbe327 | ||
![]() |
93c97972b9 | ||
![]() |
ac61d43720 | ||
![]() |
1958f78cc1 | ||
![]() |
a7ee64a25b | ||
![]() |
2a58f22649 | ||
![]() |
f066bb7716 | ||
![]() |
4e3d182189 | ||
![]() |
205fba74cf | ||
![]() |
a9bcf8d50d |
@@ -1219,6 +1219,7 @@ liboutput_plugins_a_SOURCES = \
|
|||||||
|
|
||||||
MIXER_LIBS = \
|
MIXER_LIBS = \
|
||||||
libmixer_plugins.a \
|
libmixer_plugins.a \
|
||||||
|
$(ALSA_LIBS) \
|
||||||
$(PULSE_LIBS)
|
$(PULSE_LIBS)
|
||||||
|
|
||||||
MIXER_API_SRC = \
|
MIXER_API_SRC = \
|
||||||
|
24
NEWS
24
NEWS
@@ -1,3 +1,27 @@
|
|||||||
|
ver 0.19.13 (2016/02/23)
|
||||||
|
* tags
|
||||||
|
- aiff, riff: fix ID3 chunk padding
|
||||||
|
* decoder
|
||||||
|
- ffmpeg: support the TAK codec
|
||||||
|
* fix disappearing duration of remote songs during playback
|
||||||
|
* initialize supplementary groups with glibc 2.19+
|
||||||
|
|
||||||
|
ver 0.19.12 (2015/12/15)
|
||||||
|
* fix assertion failure on malformed UTF-8 tag
|
||||||
|
* fix build failure on non-Linux systems
|
||||||
|
* fix LimitRTTIME in systemd unit file
|
||||||
|
|
||||||
|
ver 0.19.11 (2015/10/27)
|
||||||
|
* tags
|
||||||
|
- ape: fix buffer overflow
|
||||||
|
* decoder
|
||||||
|
- ffmpeg: fix crash due to wrong avio_alloc_context() call
|
||||||
|
- gme: don't loop forever, fall back to GME's default play length
|
||||||
|
* encoder
|
||||||
|
- flac: fix crash with 32 bit playback
|
||||||
|
* mixer
|
||||||
|
- fix mixer lag after enabling/disabling output
|
||||||
|
|
||||||
ver 0.19.10 (2015/06/21)
|
ver 0.19.10 (2015/06/21)
|
||||||
* input
|
* input
|
||||||
- curl: fix deadlock on small responses
|
- curl: fix deadlock on small responses
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
AC_PREREQ(2.60)
|
AC_PREREQ(2.60)
|
||||||
|
|
||||||
AC_INIT(mpd, 0.19.10, musicpd-dev-team@lists.sourceforge.net)
|
AC_INIT(mpd, 0.19.13, musicpd-dev-team@lists.sourceforge.net)
|
||||||
|
|
||||||
VERSION_MAJOR=0
|
VERSION_MAJOR=0
|
||||||
VERSION_MINOR=19
|
VERSION_MINOR=19
|
||||||
VERSION_REVISION=10
|
VERSION_REVISION=13
|
||||||
VERSION_EXTRA=0
|
VERSION_EXTRA=0
|
||||||
|
|
||||||
AC_CONFIG_SRCDIR([src/Main.cxx])
|
AC_CONFIG_SRCDIR([src/Main.cxx])
|
||||||
@@ -206,6 +206,7 @@ if test x$host_is_linux = xyes; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
AC_CHECK_FUNCS(getpwnam_r getpwuid_r)
|
AC_CHECK_FUNCS(getpwnam_r getpwuid_r)
|
||||||
|
AC_CHECK_FUNCS(initgroups)
|
||||||
AC_CHECK_FUNCS(strndup)
|
AC_CHECK_FUNCS(strndup)
|
||||||
|
|
||||||
if test x$host_is_linux = xyes; then
|
if test x$host_is_linux = xyes; then
|
||||||
|
55
doc/user.xml
55
doc/user.xml
@@ -1190,6 +1190,58 @@ database {
|
|||||||
plugin).
|
plugin).
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="realtime">
|
||||||
|
<title>Real-Time Scheduling</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
On Linux, <application>MPD</application> attempts to configure
|
||||||
|
<ulink
|
||||||
|
url="https://en.wikipedia.org/wiki/Real-time_computing">real-time
|
||||||
|
scheduling</ulink> for some threads that benefit from it.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This is only possible you allow <application>MPD</application>
|
||||||
|
to do it. This privilege is controlled by
|
||||||
|
<varname>RLIMIT_RTPRIO</varname>
|
||||||
|
<varname>RLIMIT_RTTIME</varname>. You can configure this
|
||||||
|
privilege with <command>ulimit</command> before launching
|
||||||
|
<application>MPD</application>:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting>ulimit -HS -r 50; mpd</programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Or you can use the <command>prlimit</command> program from the
|
||||||
|
<application>util-linux</application> package:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting>prlimit --rtprio=50 --rttime=unlimited mpd</programlisting>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <application>systemd</application> service file shipped
|
||||||
|
with <application>MPD</application> comes with this setting.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This works only if the Linux kernel was compiled with
|
||||||
|
<varname>CONFIG_RT_GROUP_SCHED</varname> disabled. Use the
|
||||||
|
following command to check this option for your current
|
||||||
|
kernel:
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<programlisting>zgrep ^CONFIG_RT_GROUP_SCHED /proc/config.gz</programlisting>
|
||||||
|
|
||||||
|
<note>
|
||||||
|
<para>
|
||||||
|
There is a rumor that real-time scheduling improves audio
|
||||||
|
quality. That is not true. All it does is reduce the
|
||||||
|
probability of skipping (audio buffer xruns) when the
|
||||||
|
computer is under heavy load.
|
||||||
|
</para>
|
||||||
|
</note>
|
||||||
|
</section>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
||||||
<chapter id="use">
|
<chapter id="use">
|
||||||
@@ -2595,7 +2647,8 @@ buffer_size: 16384</programlisting>
|
|||||||
/ <ulink
|
/ <ulink
|
||||||
url="http://icecast.org/"><application>IceCast</application></ulink>.
|
url="http://icecast.org/"><application>IceCast</application></ulink>.
|
||||||
HTTP streaming clients like
|
HTTP streaming clients like
|
||||||
<application>mplayer</application> can connect to it.
|
<application>mplayer</application>, <application>VLC</application>,
|
||||||
|
and <application>mpv</application> can connect to it.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@@ -188,6 +188,14 @@ public:
|
|||||||
tag = std::move(other.tag);
|
tag = std::move(other.tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to the MoveTagFrom(), but move only the #TagItem
|
||||||
|
* array.
|
||||||
|
*/
|
||||||
|
void MoveTagItemsFrom(DetachedSong &&other) {
|
||||||
|
tag.MoveItemsFrom(std::move(other.tag));
|
||||||
|
}
|
||||||
|
|
||||||
time_t GetLastModified() const {
|
time_t GetLastModified() const {
|
||||||
return mtime;
|
return mtime;
|
||||||
}
|
}
|
||||||
|
@@ -54,7 +54,6 @@
|
|||||||
#include "system/FatalError.hxx"
|
#include "system/FatalError.hxx"
|
||||||
#include "util/UriUtil.hxx"
|
#include "util/UriUtil.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "util/Domain.hxx"
|
|
||||||
#include "thread/Id.hxx"
|
#include "thread/Id.hxx"
|
||||||
#include "thread/Slack.hxx"
|
#include "thread/Slack.hxx"
|
||||||
#include "lib/icu/Init.hxx"
|
#include "lib/icu/Init.hxx"
|
||||||
@@ -123,8 +122,6 @@
|
|||||||
static constexpr unsigned DEFAULT_BUFFER_SIZE = 4096;
|
static constexpr unsigned DEFAULT_BUFFER_SIZE = 4096;
|
||||||
static constexpr unsigned DEFAULT_BUFFER_BEFORE_PLAY = 10;
|
static constexpr unsigned DEFAULT_BUFFER_BEFORE_PLAY = 10;
|
||||||
|
|
||||||
static constexpr Domain main_domain("main");
|
|
||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
Context *context;
|
Context *context;
|
||||||
#endif
|
#endif
|
||||||
@@ -633,7 +630,7 @@ static int mpd_main_after_fork(struct options options)
|
|||||||
config_get_unsigned(CONF_AUTO_UPDATE_DEPTH,
|
config_get_unsigned(CONF_AUTO_UPDATE_DEPTH,
|
||||||
INT_MAX));
|
INT_MAX));
|
||||||
#else
|
#else
|
||||||
FormatWarning(main_domain,
|
FormatWarning(config_domain,
|
||||||
"inotify: auto_update was disabled. enable during compilation phase");
|
"inotify: auto_update was disabled. enable during compilation phase");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@@ -92,14 +92,14 @@ struct AvioStream {
|
|||||||
|
|
||||||
AVIOContext *io;
|
AVIOContext *io;
|
||||||
|
|
||||||
unsigned char buffer[8192];
|
|
||||||
|
|
||||||
AvioStream(Decoder *_decoder, InputStream &_input)
|
AvioStream(Decoder *_decoder, InputStream &_input)
|
||||||
:decoder(_decoder), input(_input), io(nullptr) {}
|
:decoder(_decoder), input(_input), io(nullptr) {}
|
||||||
|
|
||||||
~AvioStream() {
|
~AvioStream() {
|
||||||
if (io != nullptr)
|
if (io != nullptr) {
|
||||||
|
av_free(io->buffer);
|
||||||
av_free(io);
|
av_free(io);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Open();
|
bool Open();
|
||||||
@@ -153,11 +153,20 @@ mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
|
|||||||
bool
|
bool
|
||||||
AvioStream::Open()
|
AvioStream::Open()
|
||||||
{
|
{
|
||||||
io = avio_alloc_context(buffer, sizeof(buffer),
|
constexpr size_t BUFFER_SIZE = 8192;
|
||||||
|
auto buffer = (unsigned char *)av_malloc(BUFFER_SIZE);
|
||||||
|
if (buffer == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
io = avio_alloc_context(buffer, BUFFER_SIZE,
|
||||||
false, this,
|
false, this,
|
||||||
mpd_ffmpeg_stream_read, nullptr,
|
mpd_ffmpeg_stream_read, nullptr,
|
||||||
input.IsSeekable()
|
input.IsSeekable()
|
||||||
? mpd_ffmpeg_stream_seek : nullptr);
|
? mpd_ffmpeg_stream_seek : nullptr);
|
||||||
|
/* If avio_alloc_context() fails, who frees the buffer? The
|
||||||
|
libavformat API documentation does not specify this, it
|
||||||
|
only says that AVIOContext.buffer must be freed in the end,
|
||||||
|
however no AVIOContext exists in that failure code path. */
|
||||||
return io != nullptr;
|
return io != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -729,7 +738,7 @@ static const char *const ffmpeg_suffixes[] = {
|
|||||||
"mve", "mvi", "mxf", "nc", "nsv", "nut", "nuv", "oga", "ogm", "ogv",
|
"mve", "mvi", "mxf", "nc", "nsv", "nut", "nuv", "oga", "ogm", "ogv",
|
||||||
"ogx", "oma", "ogg", "omg", "opus", "psp", "pva", "qcp", "qt", "r3d", "ra",
|
"ogx", "oma", "ogg", "omg", "opus", "psp", "pva", "qcp", "qt", "r3d", "ra",
|
||||||
"ram", "rl2", "rm", "rmvb", "roq", "rpl", "rvc", "shn", "smk", "snd",
|
"ram", "rl2", "rm", "rmvb", "roq", "rpl", "rvc", "shn", "smk", "snd",
|
||||||
"sol", "son", "spx", "str", "swf", "tgi", "tgq", "tgv", "thp", "ts",
|
"sol", "son", "spx", "str", "swf", "tak", "tgi", "tgq", "tgv", "thp", "ts",
|
||||||
"tsp", "tta", "xa", "xvid", "uv", "uv2", "vb", "vid", "vob", "voc",
|
"tsp", "tta", "xa", "xvid", "uv", "uv2", "vb", "vid", "vob", "voc",
|
||||||
"vp6", "vmd", "wav", "webm", "wma", "wmv", "wsaud", "wsvga", "wv",
|
"vp6", "vmd", "wav", "webm", "wma", "wmv", "wsaud", "wsvga", "wv",
|
||||||
"wve",
|
"wve",
|
||||||
|
@@ -156,8 +156,11 @@ gme_file_decode(Decoder &decoder, Path path_fs)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SignedSongTime song_len = ti->length > 0
|
const int length = ti->play_length;
|
||||||
? SignedSongTime::FromMS(ti->length)
|
gme_free_info(ti);
|
||||||
|
|
||||||
|
const SignedSongTime song_len = length > 0
|
||||||
|
? SignedSongTime::FromMS(length)
|
||||||
: SignedSongTime::Negative();
|
: SignedSongTime::Negative();
|
||||||
|
|
||||||
/* initialize the MPD decoder */
|
/* initialize the MPD decoder */
|
||||||
@@ -168,7 +171,6 @@ gme_file_decode(Decoder &decoder, Path path_fs)
|
|||||||
SampleFormat::S16, GME_CHANNELS,
|
SampleFormat::S16, GME_CHANNELS,
|
||||||
error)) {
|
error)) {
|
||||||
LogError(error);
|
LogError(error);
|
||||||
gme_free_info(ti);
|
|
||||||
gme_delete(emu);
|
gme_delete(emu);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -179,8 +181,8 @@ gme_file_decode(Decoder &decoder, Path path_fs)
|
|||||||
if (gme_err != nullptr)
|
if (gme_err != nullptr)
|
||||||
LogWarning(gme_domain, gme_err);
|
LogWarning(gme_domain, gme_err);
|
||||||
|
|
||||||
if (ti->length > 0)
|
if (length > 0)
|
||||||
gme_set_fade(emu, ti->length);
|
gme_set_fade(emu, length);
|
||||||
|
|
||||||
/* play */
|
/* play */
|
||||||
DecoderCommand cmd;
|
DecoderCommand cmd;
|
||||||
@@ -196,16 +198,17 @@ gme_file_decode(Decoder &decoder, Path path_fs)
|
|||||||
if (cmd == DecoderCommand::SEEK) {
|
if (cmd == DecoderCommand::SEEK) {
|
||||||
unsigned where = decoder_seek_time(decoder).ToMS();
|
unsigned where = decoder_seek_time(decoder).ToMS();
|
||||||
gme_err = gme_seek(emu, where);
|
gme_err = gme_seek(emu, where);
|
||||||
if (gme_err != nullptr)
|
if (gme_err != nullptr) {
|
||||||
LogWarning(gme_domain, gme_err);
|
LogWarning(gme_domain, gme_err);
|
||||||
decoder_command_finished(decoder);
|
decoder_seek_error(decoder);
|
||||||
|
} else
|
||||||
|
decoder_command_finished(decoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gme_track_ended(emu))
|
if (gme_track_ended(emu))
|
||||||
break;
|
break;
|
||||||
} while (cmd != DecoderCommand::STOP);
|
} while (cmd != DecoderCommand::STOP);
|
||||||
|
|
||||||
gme_free_info(ti);
|
|
||||||
gme_delete(emu);
|
gme_delete(emu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,9 +239,9 @@ gme_scan_file(Path path_fs,
|
|||||||
|
|
||||||
assert(ti != nullptr);
|
assert(ti != nullptr);
|
||||||
|
|
||||||
if (ti->length > 0)
|
if (ti->play_length > 0)
|
||||||
tag_handler_invoke_duration(handler, handler_ctx,
|
tag_handler_invoke_duration(handler, handler_ctx,
|
||||||
SongTime::FromMS(ti->length));
|
SongTime::FromMS(ti->play_length));
|
||||||
|
|
||||||
if (ti->song != nullptr) {
|
if (ti->song != nullptr) {
|
||||||
if (gme_track_count(emu) > 1) {
|
if (gme_track_count(emu) > 1) {
|
||||||
|
@@ -22,10 +22,12 @@
|
|||||||
#include "../DecoderAPI.hxx"
|
#include "../DecoderAPI.hxx"
|
||||||
#include "input/InputStream.hxx"
|
#include "input/InputStream.hxx"
|
||||||
#include "CheckAudioFormat.hxx"
|
#include "CheckAudioFormat.hxx"
|
||||||
|
#include "pcm/Traits.hxx"
|
||||||
#include "tag/TagHandler.hxx"
|
#include "tag/TagHandler.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
#include "util/Macros.hxx"
|
#include "util/Macros.hxx"
|
||||||
|
#include "util/Clamp.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <mpc/mpcdec.h>
|
#include <mpc/mpcdec.h>
|
||||||
@@ -42,6 +44,9 @@ struct mpc_decoder_data {
|
|||||||
|
|
||||||
static constexpr Domain mpcdec_domain("mpcdec");
|
static constexpr Domain mpcdec_domain("mpcdec");
|
||||||
|
|
||||||
|
static constexpr SampleFormat mpcdec_sample_format = SampleFormat::S24_P32;
|
||||||
|
typedef SampleTraits<mpcdec_sample_format> MpcdecSampleTraits;
|
||||||
|
|
||||||
static mpc_int32_t
|
static mpc_int32_t
|
||||||
mpc_read_cb(mpc_reader *reader, void *ptr, mpc_int32_t size)
|
mpc_read_cb(mpc_reader *reader, void *ptr, mpc_int32_t size)
|
||||||
{
|
{
|
||||||
@@ -91,18 +96,15 @@ mpc_getsize_cb(mpc_reader *reader)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* this _looks_ performance-critical, don't de-inline -- eric */
|
/* this _looks_ performance-critical, don't de-inline -- eric */
|
||||||
static inline int32_t
|
static inline MpcdecSampleTraits::value_type
|
||||||
mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
|
mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
|
||||||
{
|
{
|
||||||
/* only doing 16-bit audio for now */
|
/* only doing 16-bit audio for now */
|
||||||
int32_t val;
|
MpcdecSampleTraits::value_type val;
|
||||||
|
|
||||||
enum {
|
constexpr int bits = MpcdecSampleTraits::BITS;
|
||||||
bits = 24,
|
constexpr auto clip_min = MpcdecSampleTraits::MIN;
|
||||||
};
|
constexpr auto clip_max = MpcdecSampleTraits::MAX;
|
||||||
|
|
||||||
const int clip_min = -1 << (bits - 1);
|
|
||||||
const int clip_max = (1 << (bits - 1)) - 1;
|
|
||||||
|
|
||||||
#ifdef MPC_FIXED_POINT
|
#ifdef MPC_FIXED_POINT
|
||||||
const int shift = bits - MPC_FIXED_POINT_SCALE_SHIFT;
|
const int shift = bits - MPC_FIXED_POINT_SCALE_SHIFT;
|
||||||
@@ -117,16 +119,12 @@ mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
|
|||||||
val = sample * float_scale;
|
val = sample * float_scale;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (val < clip_min)
|
return Clamp(val, clip_min, clip_max);
|
||||||
val = clip_min;
|
|
||||||
else if (val > clip_max)
|
|
||||||
val = clip_max;
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mpc_to_mpd_buffer(int32_t *dest, const MPC_SAMPLE_FORMAT *src,
|
mpc_to_mpd_buffer(MpcdecSampleTraits::pointer_type dest,
|
||||||
|
const MPC_SAMPLE_FORMAT *src,
|
||||||
unsigned num_samples)
|
unsigned num_samples)
|
||||||
{
|
{
|
||||||
while (num_samples-- > 0)
|
while (num_samples-- > 0)
|
||||||
@@ -162,7 +160,7 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is)
|
|||||||
Error error;
|
Error error;
|
||||||
AudioFormat audio_format;
|
AudioFormat audio_format;
|
||||||
if (!audio_format_init_checked(audio_format, info.sample_freq,
|
if (!audio_format_init_checked(audio_format, info.sample_freq,
|
||||||
SampleFormat::S24_P32,
|
mpcdec_sample_format,
|
||||||
info.channels, error)) {
|
info.channels, error)) {
|
||||||
LogError(error);
|
LogError(error);
|
||||||
mpc_demux_exit(demux);
|
mpc_demux_exit(demux);
|
||||||
@@ -214,7 +212,7 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is)
|
|||||||
mpc_uint32_t ret = frame.samples;
|
mpc_uint32_t ret = frame.samples;
|
||||||
ret *= info.channels;
|
ret *= info.channels;
|
||||||
|
|
||||||
int32_t chunk[ARRAY_SIZE(sample_buffer)];
|
MpcdecSampleTraits::value_type chunk[ARRAY_SIZE(sample_buffer)];
|
||||||
mpc_to_mpd_buffer(chunk, sample_buffer, ret);
|
mpc_to_mpd_buffer(chunk, sample_buffer, ret);
|
||||||
|
|
||||||
long bit_rate = vbr_update_bits * audio_format.sample_rate
|
long bit_rate = vbr_update_bits * audio_format.sample_rate
|
||||||
|
@@ -157,8 +157,6 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
|
|||||||
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
|
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
|
||||||
unsigned bits_per_sample;
|
unsigned bits_per_sample;
|
||||||
|
|
||||||
encoder->audio_format = audio_format;
|
|
||||||
|
|
||||||
/* FIXME: flac should support 32bit as well */
|
/* FIXME: flac should support 32bit as well */
|
||||||
switch (audio_format.format) {
|
switch (audio_format.format) {
|
||||||
case SampleFormat::S8:
|
case SampleFormat::S8:
|
||||||
@@ -178,6 +176,8 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
|
|||||||
audio_format.format = SampleFormat::S24_P32;
|
audio_format.format = SampleFormat::S24_P32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encoder->audio_format = audio_format;
|
||||||
|
|
||||||
/* allocate the encoder */
|
/* allocate the encoder */
|
||||||
encoder->fse = FLAC__stream_encoder_new();
|
encoder->fse = FLAC__stream_encoder_new();
|
||||||
if (encoder->fse == nullptr) {
|
if (encoder->fse == nullptr) {
|
||||||
|
@@ -26,7 +26,6 @@
|
|||||||
#include "util/NumberParser.hxx"
|
#include "util/NumberParser.hxx"
|
||||||
#include "util/DynamicFifoBuffer.hxx"
|
#include "util/DynamicFifoBuffer.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "util/Domain.hxx"
|
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
@@ -60,8 +59,6 @@ struct ShineEncoder {
|
|||||||
bool WriteChunk(bool flush);
|
bool WriteChunk(bool flush);
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr Domain shine_encoder_domain("shine_encoder");
|
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
ShineEncoder::Configure(const config_param ¶m,
|
ShineEncoder::Configure(const config_param ¶m,
|
||||||
gcc_unused Error &error)
|
gcc_unused Error &error)
|
||||||
|
@@ -26,7 +26,6 @@
|
|||||||
#include "AudioFormat.hxx"
|
#include "AudioFormat.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "util/Domain.hxx"
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -50,8 +49,6 @@ public:
|
|||||||
Error &error) override;
|
Error &error) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr Domain volume_domain("pcm_volume");
|
|
||||||
|
|
||||||
static Filter *
|
static Filter *
|
||||||
volume_filter_init(gcc_unused const config_param ¶m,
|
volume_filter_init(gcc_unused const config_param ¶m,
|
||||||
gcc_unused Error &error)
|
gcc_unused Error &error)
|
||||||
|
@@ -25,13 +25,10 @@
|
|||||||
#include "output/Internal.hxx"
|
#include "output/Internal.hxx"
|
||||||
#include "pcm/Volume.hxx"
|
#include "pcm/Volume.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "util/Domain.hxx"
|
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
static constexpr Domain mixer_domain("mixer");
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
output_mixer_get_volume(const AudioOutput &ao)
|
output_mixer_get_volume(const AudioOutput &ao)
|
||||||
{
|
{
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
#include "Internal.hxx"
|
#include "Internal.hxx"
|
||||||
#include "PlayerControl.hxx"
|
#include "PlayerControl.hxx"
|
||||||
#include "mixer/MixerControl.hxx"
|
#include "mixer/MixerControl.hxx"
|
||||||
|
#include "mixer/Volume.hxx"
|
||||||
#include "Idle.hxx"
|
#include "Idle.hxx"
|
||||||
|
|
||||||
extern unsigned audio_output_state_version;
|
extern unsigned audio_output_state_version;
|
||||||
@@ -47,6 +48,11 @@ audio_output_enable_index(MultipleOutputs &outputs, unsigned idx)
|
|||||||
ao.enabled = true;
|
ao.enabled = true;
|
||||||
idle_add(IDLE_OUTPUT);
|
idle_add(IDLE_OUTPUT);
|
||||||
|
|
||||||
|
if (ao.mixer != nullptr) {
|
||||||
|
InvalidateHardwareVolume();
|
||||||
|
idle_add(IDLE_MIXER);
|
||||||
|
}
|
||||||
|
|
||||||
ao.player_control->UpdateAudio();
|
ao.player_control->UpdateAudio();
|
||||||
|
|
||||||
++audio_output_state_version;
|
++audio_output_state_version;
|
||||||
@@ -70,6 +76,7 @@ audio_output_disable_index(MultipleOutputs &outputs, unsigned idx)
|
|||||||
Mixer *mixer = ao.mixer;
|
Mixer *mixer = ao.mixer;
|
||||||
if (mixer != nullptr) {
|
if (mixer != nullptr) {
|
||||||
mixer_close(mixer);
|
mixer_close(mixer);
|
||||||
|
InvalidateHardwareVolume();
|
||||||
idle_add(IDLE_MIXER);
|
idle_add(IDLE_MIXER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +101,7 @@ audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx)
|
|||||||
Mixer *mixer = ao.mixer;
|
Mixer *mixer = ao.mixer;
|
||||||
if (mixer != nullptr) {
|
if (mixer != nullptr) {
|
||||||
mixer_close(mixer);
|
mixer_close(mixer);
|
||||||
|
InvalidateHardwareVolume();
|
||||||
idle_add(IDLE_MIXER);
|
idle_add(IDLE_MIXER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,6 @@
|
|||||||
#include "../OutputAPI.hxx"
|
#include "../OutputAPI.hxx"
|
||||||
#include "config/ConfigError.hxx"
|
#include "config/ConfigError.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "util/Domain.hxx"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -44,8 +43,6 @@ struct PipeOutput {
|
|||||||
bool Configure(const config_param ¶m, Error &error);
|
bool Configure(const config_param ¶m, Error &error);
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr Domain pipe_output_domain("pipe_output");
|
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
PipeOutput::Configure(const config_param ¶m, Error &error)
|
PipeOutput::Configure(const config_param ¶m, Error &error)
|
||||||
{
|
{
|
||||||
|
@@ -25,14 +25,11 @@
|
|||||||
#include "input/InputStream.hxx"
|
#include "input/InputStream.hxx"
|
||||||
#include "tag/TagBuilder.hxx"
|
#include "tag/TagBuilder.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "util/Domain.hxx"
|
|
||||||
#include "lib/expat/ExpatParser.hxx"
|
#include "lib/expat/ExpatParser.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static constexpr Domain xspf_domain("xspf");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the state object for the GLib XML parser.
|
* This is the state object for the GLib XML parser.
|
||||||
*/
|
*/
|
||||||
|
@@ -37,7 +37,7 @@ playlist::TagModified(DetachedSong &&song)
|
|||||||
|
|
||||||
DetachedSong ¤t_song = queue.GetOrder(current);
|
DetachedSong ¤t_song = queue.GetOrder(current);
|
||||||
if (song.IsSame(current_song))
|
if (song.IsSame(current_song))
|
||||||
current_song.MoveTagFrom(std::move(song));
|
current_song.MoveTagItemsFrom(std::move(song));
|
||||||
|
|
||||||
queue.ModifyAtOrder(current);
|
queue.ModifyAtOrder(current);
|
||||||
queue.IncrementVersion();
|
queue.IncrementVersion();
|
||||||
|
@@ -84,14 +84,14 @@ aiff_seek_id3(FILE *file)
|
|||||||
underflow when casting to off_t */
|
underflow when casting to off_t */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (size % 2 != 0)
|
|
||||||
/* pad byte */
|
|
||||||
++size;
|
|
||||||
|
|
||||||
if (memcmp(chunk.id, "ID3 ", 4) == 0)
|
if (memcmp(chunk.id, "ID3 ", 4) == 0)
|
||||||
/* found it! */
|
/* found it! */
|
||||||
return size;
|
return size;
|
||||||
|
|
||||||
|
if (size % 2 != 0)
|
||||||
|
/* pad byte */
|
||||||
|
++size;
|
||||||
|
|
||||||
if (fseek(file, size, SEEK_CUR) != 0)
|
if (fseek(file, size, SEEK_CUR) != 0)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -78,12 +78,12 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback)
|
|||||||
|
|
||||||
/* get the key */
|
/* get the key */
|
||||||
const char *key = p;
|
const char *key = p;
|
||||||
while (remaining > size && *p != '\0') {
|
const char *key_end = (const char *)memchr(p, '\0', remaining);
|
||||||
p++;
|
if (key_end == nullptr)
|
||||||
remaining--;
|
break;
|
||||||
}
|
|
||||||
p++;
|
p = key_end + 1;
|
||||||
remaining--;
|
remaining -= p - key;
|
||||||
|
|
||||||
/* get the value */
|
/* get the value */
|
||||||
if (remaining < size)
|
if (remaining < size)
|
||||||
|
@@ -82,15 +82,15 @@ riff_seek_id3(FILE *file)
|
|||||||
underflow when casting to off_t */
|
underflow when casting to off_t */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (size % 2 != 0)
|
|
||||||
/* pad byte */
|
|
||||||
++size;
|
|
||||||
|
|
||||||
if (memcmp(chunk.id, "id3 ", 4) == 0 ||
|
if (memcmp(chunk.id, "id3 ", 4) == 0 ||
|
||||||
memcmp(chunk.id, "ID3 ", 4) == 0)
|
memcmp(chunk.id, "ID3 ", 4) == 0)
|
||||||
/* found it! */
|
/* found it! */
|
||||||
return size;
|
return size;
|
||||||
|
|
||||||
|
if (size % 2 != 0)
|
||||||
|
/* pad byte */
|
||||||
|
++size;
|
||||||
|
|
||||||
if (fseek(file, size, SEEK_CUR) != 0)
|
if (fseek(file, size, SEEK_CUR) != 0)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -80,9 +80,17 @@ struct Tag {
|
|||||||
Tag &operator=(Tag &&other) {
|
Tag &operator=(Tag &&other) {
|
||||||
duration = other.duration;
|
duration = other.duration;
|
||||||
has_playlist = other.has_playlist;
|
has_playlist = other.has_playlist;
|
||||||
|
MoveItemsFrom(std::move(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to the move operator, but move only the #TagItem
|
||||||
|
* array.
|
||||||
|
*/
|
||||||
|
void MoveItemsFrom(Tag &&other) {
|
||||||
std::swap(items, other.items);
|
std::swap(items, other.items);
|
||||||
std::swap(num_items, other.num_items);
|
std::swap(num_items, other.num_items);
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -40,9 +40,9 @@ FindInvalidUTF8(const char *p, const char *const end)
|
|||||||
/* now call the other SequenceLengthUTF8() overload
|
/* now call the other SequenceLengthUTF8() overload
|
||||||
which also validates the continuations */
|
which also validates the continuations */
|
||||||
const size_t t = SequenceLengthUTF8(p);
|
const size_t t = SequenceLengthUTF8(p);
|
||||||
assert(s == t);
|
|
||||||
if (t == 0)
|
if (t == 0)
|
||||||
return p;
|
return p;
|
||||||
|
assert(s == t);
|
||||||
|
|
||||||
p += s;
|
p += s;
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,6 @@
|
|||||||
#include "system/FatalError.hxx"
|
#include "system/FatalError.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileSystem.hxx"
|
||||||
#include "util/Domain.hxx"
|
|
||||||
#include "PidFile.hxx"
|
#include "PidFile.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
@@ -37,8 +36,6 @@
|
|||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static constexpr Domain daemon_domain("daemon");
|
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
|
|
||||||
/** the Unix user name which MPD runs as */
|
/** the Unix user name which MPD runs as */
|
||||||
@@ -113,7 +110,7 @@ daemonize_set_user(void)
|
|||||||
(int)user_gid);
|
(int)user_gid);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _BSD_SOURCE
|
#ifdef HAVE_INITGROUPS
|
||||||
/* init supplementary groups
|
/* init supplementary groups
|
||||||
* (must be done before we change our uid)
|
* (must be done before we change our uid)
|
||||||
*/
|
*/
|
||||||
|
@@ -7,16 +7,7 @@ ExecStart=@prefix@/bin/mpd --no-daemon
|
|||||||
|
|
||||||
# allow MPD to use real-time priority 50
|
# allow MPD to use real-time priority 50
|
||||||
LimitRTPRIO=50
|
LimitRTPRIO=50
|
||||||
LimitRTTIME=-1
|
LimitRTTIME=infinity
|
||||||
|
|
||||||
# move MPD to a top-level cgroup, as real-time budget assignment fails
|
|
||||||
# in cgroup /system/mpd.service, because /system has a zero real-time
|
|
||||||
# budget; see
|
|
||||||
# http://www.freedesktop.org/wiki/Software/systemd/MyServiceCantGetRealtime/
|
|
||||||
ControlGroup=cpu:/mpd
|
|
||||||
|
|
||||||
# assign a real-time budget
|
|
||||||
ControlGroupAttribute=cpu.rt_runtime_us 500000
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
Reference in New Issue
Block a user