Compare commits

..

21 Commits

Author SHA1 Message Date
Max Kellermann
c2940a8385 release v0.20.17 2018-02-11 13:02:53 +01:00
Max Kellermann
bede564618 mixer/alsa: work around rounding error at volume 0
Due to rounding errors, a slightly negative value can be passed to
set_normalized_volume(), which will make the log10() call fail.
Actually, volume 0 is already failing because log10(0) is illegal.  So
let's fix this by implementing two corner cases: <=0 and >=100.

Closes #212
2018-02-10 09:07:51 +01:00
Max Kellermann
e0ca4b865a android: require SDK version 14
Closes #213.
2018-02-10 00:03:23 +01:00
Max Kellermann
31c206bf80 android/build.py: add -mfpu=vfp, explicitly disabling NEON
Apparently, clang defaults to NEON when ARMv7 is used.  Not all ARMv7
CPUs we target have NEON, so we need to disable that.
2018-02-10 00:00:57 +01:00
Max Kellermann
9187a08106 lib/curl: remove .netrc support on Android
Not needed on Android, and the implementation uses getpwuid_r() which
is unavailable on old Android versions.
2018-02-09 23:14:29 +01:00
Max Kellermann
3859a50466 python/build/libs.py: convert CURL edit to quilt patch 2018-02-09 23:14:27 +01:00
Max Kellermann
927071e085 python/build/project.py: add quilt support 2018-02-09 22:59:12 +01:00
Max Kellermann
6ba918b203 input/file: don't use posix_fadvise() on Android
Requires Android API 21, but we want to support older versions as
well.
2018-02-09 22:54:22 +01:00
Max Kellermann
e8b70dbca4 SongSave, queue/PlaylistState, tag/ReplayGain: use portable atof() wrappers
For Android pre-5.0 compatibility (#213).
2018-02-09 22:54:22 +01:00
Max Kellermann
0f8d223c7f protocol/ArgParser: move strtof()/strtod() switch to util/NumberParser.hxx 2018-02-09 22:54:22 +01:00
Max Kellermann
19a2885fd5 protocol/ArgParser: use strtod() instead of strtof() on Android
For Android pre-5.0 compatibility (#213).
2018-02-09 22:54:22 +01:00
Max Kellermann
b8a094470b python/build/libs.py: build only the library 2018-02-09 22:54:22 +01:00
Max Kellermann
2988bb77e8 python/build/project: allow trailing digit after letter in version number
For version numbers such as OpenSSH's, e.g.: "7.2p2"
2018-02-09 22:54:22 +01:00
Max Kellermann
738317bf34 doc/user: document MPD on Android
Closes #217
2018-02-09 19:11:39 +01:00
Max Kellermann
e46fbd0780 filter/convert: set the PcmConvert instance only if it was initialized
Fixes valgrind warning.
2018-02-09 19:05:45 +01:00
Max Kellermann
56b74ad990 filter/convert: add method IsActive() 2018-02-09 19:04:45 +01:00
Max Kellermann
6de92bb42b pcm/Order: fix size calculation with 8 channels
This was a buffer overflow bug which could cause MPD crahes when
playing back 8 channels with the ALSA output plugin.

Closes #216
2018-02-09 19:01:12 +01:00
Max Kellermann
c801936e53 db/update/Service: set the update thread name 2018-02-09 18:48:14 +01:00
Max Kellermann
817656504d thread/Util: implement system call wrapper for sched_setscheduler()
There is a POSIX definition for sched_setscheduler(), but Linux does
not implement that; instead of changing the process's scheduler, it
only affects one thread.  This has caused some confusion among
application developers and C library developers.

While glibc implements Linux semantics, Musl has made their
sched_setscheduler() function an always-failing no-op, causing the
error message "sched_setscheduler failed: Function not implemented".

 http://git.musl-libc.org/cgit/musl/commit/src/sched/sched_setscheduler.c?id=1e21e78bf7a5c24c217446d8760be7b7188711c2

Instead of relying on the C library which may be unreliable here, we
now roll our own system call wrapper.

Closes #218
2018-02-09 18:43:45 +01:00
Max Kellermann
6f00f97b66 thread/Util: rename ioprio_set() to linux_ioprio_set()
Juse in cas glibc gets a wrapper for the system call which would then
conflict with ours.
2018-02-09 18:43:45 +01:00
Max Kellermann
5acb978f8f increment version number to 0.20.17 2018-02-09 18:43:45 +01:00
22 changed files with 161 additions and 32 deletions

9
NEWS
View File

@@ -1,3 +1,12 @@
ver 0.20.17 (2018/02/11)
* output
- alsa: fix crash bug with 8 channels
* mixer
- alsa: fix rounding error at volume 0
* fix real-time and idle scheduling with Musl
* Android
- fix compatibility with Android 4.0
ver 0.20.16 (2018/02/03)
* output
- pulse: fix crash during auto-detection

View File

@@ -2,10 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="15"
android:versionName="0.20.16">
android:versionCode="16"
android:versionName="0.20.17">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17"/>
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17"/>
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Main"

View File

@@ -46,7 +46,7 @@ class AndroidNdkToolchain:
self.ndk_arch = 'arm'
android_abi = 'armeabi-v7a'
ndk_platform = 'android-21'
ndk_platform = 'android-14'
# select the NDK compiler
gcc_version = '4.9'
@@ -67,7 +67,7 @@ class AndroidNdkToolchain:
common_flags = '-Os -g'
common_flags += ' -fPIC'
common_flags += ' -march=armv7-a -mfloat-abi=softfp'
common_flags += ' -march=armv7-a -mfpu=vfp -mfloat-abi=softfp'
toolchain_bin = os.path.join(toolchain_path, 'bin')
llvm_bin = os.path.join(llvm_path, 'bin')
@@ -87,7 +87,7 @@ class AndroidNdkToolchain:
self.cppflags = '--sysroot=' + sysroot + \
' -isystem ' + os.path.join(install_prefix, 'include') + \
' -isystem ' + os.path.join(sysroot, 'usr', 'include', arch) + \
' -D__ANDROID_API__=21'
' -D__ANDROID_API__=14'
self.ldflags = '--sysroot=' + sysroot + \
' -L' + os.path.join(install_prefix, 'lib') + \
' -L' + os.path.join(target_root, 'usr', 'lib') + \

View File

@@ -1,10 +1,10 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.20.16, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.20.17, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
VERSION_MINOR=20
VERSION_REVISION=16
VERSION_REVISION=17
VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx])

View File

@@ -66,6 +66,23 @@
</para>
</section>
<section id="install_android">
<title>Installing on Android</title>
<para>
An experimental Android build is available on <ulink
url="https://play.google.com/store/apps/details?id=org.musicpd">Google
Play</ulink>. After installing and launching it, MPD will
scan the music in your <filename>Music</filename> directory
and you can control it as usual with a MPD client.
</para>
<para>
If you need to tweak the configuration, you can create a file
called <filename>mpd.conf</filename> on the data partition.
</para>
</section>
<section id="install_source">
<title>Compiling from source</title>
@@ -323,7 +340,9 @@ systemctl start mpd.socket</programlisting>
<application>MPD</application> as a user daemon (and not as a
system daemon), the configuration is read from
<filename>$XDG_CONFIG_HOME/mpd/mpd.conf</filename> (usually
<filename>~/.config/mpd/mpd.conf</filename>).
<filename>~/.config/mpd/mpd.conf</filename>). On Android,
<filename>mpd.conf</filename> will be loaded from the
top-level directory of the data partition.
</para>
<para>

View File

@@ -1,4 +1,6 @@
import re
from os.path import abspath
from build.project import Project
from build.zlib import ZlibProject
from build.autotools import AutotoolsProject
@@ -358,6 +360,8 @@ curl = AutotoolsProject(
'--disable-crypto-auth', '--disable-ntlm-wb', '--disable-tls-srp', '--disable-cookies',
'--without-ssl', '--without-gnutls', '--without-nss', '--without-libssh2',
],
patches='src/lib/curl/patches',
)
boost = BoostProject(

View File

@@ -3,10 +3,12 @@ import re
from build.download import download_and_verify
from build.tar import untar
from build.quilt import push_all
class Project:
def __init__(self, url, md5, installed, name=None, version=None,
base=None,
patches=None,
edits=None,
use_cxx=False):
if base is None:
@@ -18,7 +20,7 @@ class Project:
self.base = base
if name is None or version is None:
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?)$', self.base)
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*)$', self.base)
if name is None: name = m.group(1)
if version is None: version = m.group(2)
@@ -29,6 +31,10 @@ class Project:
self.md5 = md5
self.installed = installed
if patches is not None:
srcdir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
patches = os.path.join(srcdir, patches)
self.patches = patches
self.edits = edits
self.use_cxx = use_cxx
@@ -51,6 +57,9 @@ class Project:
parent_path = toolchain.build_path
path = untar(self.download(toolchain), parent_path, self.base)
if self.patches is not None:
push_all(toolchain, path, self.patches)
if self.edits is not None:
for filename, function in self.edits.items():
with open(os.path.join(path, filename), 'r+t') as f:

9
python/build/quilt.py Normal file
View File

@@ -0,0 +1,9 @@
import subprocess
def run_quilt(toolchain, cwd, patches_path, *args):
env = dict(toolchain.env)
env['QUILT_PATCHES'] = patches_path
subprocess.check_call(['quilt'] + list(args), cwd=cwd, env=env)
def push_all(toolchain, src_path, patches_path):
run_quilt(toolchain, src_path, patches_path, 'push', '-a')

View File

@@ -28,6 +28,7 @@
#include "tag/TagBuilder.hxx"
#include "util/StringUtil.hxx"
#include "util/RuntimeError.hxx"
#include "util/NumberParser.hxx"
#include <string.h>
#include <stdlib.h>
@@ -94,7 +95,7 @@ song_load(TextFile &file, const char *uri)
if ((type = tag_name_parse(line)) != TAG_NUM_OF_ITEM_TYPES) {
tag.AddItem(type, value);
} else if (strcmp(line, "Time") == 0) {
tag.SetDuration(SignedSongTime::FromS(atof(value)));
tag.SetDuration(SignedSongTime::FromS(ParseDouble(value)));
} else if (strcmp(line, "Playlist") == 0) {
tag.SetHasPlaylist(strcmp(value, "yes") == 0);
} else if (strcmp(line, SONG_MTIME) == 0) {

View File

@@ -29,6 +29,7 @@
#include "Idle.hxx"
#include "Log.hxx"
#include "thread/Thread.hxx"
#include "thread/Name.hxx"
#include "thread/Util.hxx"
#ifndef NDEBUG
@@ -113,6 +114,8 @@ UpdateService::Task()
{
assert(walk != nullptr);
SetThreadName("update");
if (!next.path_utf8.empty())
FormatDebug(update_domain, "starting: %s",
next.path_utf8.c_str());

View File

@@ -53,10 +53,16 @@ public:
void Set(const AudioFormat &_out_audio_format);
void Reset() override {
state.Reset();
if (IsActive())
state.Reset();
}
ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override;
private:
bool IsActive() const noexcept {
return out_audio_format != in_audio_format;
}
};
class PreparedConvertFilter final : public PreparedFilter {
@@ -80,7 +86,7 @@ ConvertFilter::Set(const AudioFormat &_out_audio_format)
/* no change */
return;
if (out_audio_format != in_audio_format) {
if (IsActive()) {
out_audio_format = in_audio_format;
state.Close();
}
@@ -111,7 +117,7 @@ ConvertFilter::~ConvertFilter()
{
assert(in_audio_format.IsValid());
if (out_audio_format != in_audio_format)
if (IsActive())
state.Close();
}
@@ -120,11 +126,10 @@ ConvertFilter::FilterPCM(ConstBuffer<void> src)
{
assert(in_audio_format.IsValid());
if (out_audio_format == in_audio_format)
return IsActive()
? state.Convert(src)
/* optimized special case: no-op */
return src;
return state.Convert(src);
: src;
}
const FilterPlugin convert_filter_plugin = {

View File

@@ -65,9 +65,12 @@ OpenFileInputStream(Path path,
throw FormatRuntimeError("Not a regular file: %s",
path.c_str());
#if !defined(__BIONIC__) || __ANDROID_API__ >= 21
/* posix_fadvise() requires Android API 21 */
#ifdef POSIX_FADV_SEQUENTIAL
posix_fadvise(reader.GetFD().Get(), (off_t)0, info.GetSize(),
POSIX_FADV_SEQUENTIAL);
#endif
#endif
return InputStreamPtr(new FileInputStream(path.ToUTF8().c_str(),

View File

@@ -0,0 +1,20 @@
Index: curl-7.58.0/lib/url.c
===================================================================
--- curl-7.58.0.orig/lib/url.c
+++ curl-7.58.0/lib/url.c
@@ -3503,6 +3503,7 @@ static CURLcode override_login(struct Cu
}
conn->bits.netrc = FALSE;
+#ifndef __BIONIC__
if(data->set.use_netrc != CURL_NETRC_IGNORED) {
int ret = Curl_parsenetrc(conn->host.name,
userp, passwdp,
@@ -3524,6 +3525,7 @@ static CURLcode override_login(struct Cu
conn->bits.user_passwd = TRUE; /* enable user+password */
}
}
+#endif
return CURLE_OK;
}

View File

@@ -0,0 +1,15 @@
Index: curl-7.58.0/Makefile.in
===================================================================
--- curl-7.58.0.orig/Makefile.in
+++ curl-7.58.0/Makefile.in
@@ -641,8 +641,8 @@ CLEANFILES = $(VC6_LIBDSP) $(VC6_SRCDSP)
$(VC14_LIBVCXPROJ) $(VC14_SRCVCXPROJ) $(VC15_LIBVCXPROJ) $(VC15_SRCVCXPROJ)
bin_SCRIPTS = curl-config
-SUBDIRS = lib src
-DIST_SUBDIRS = $(SUBDIRS) tests packages scripts include docs
+SUBDIRS = lib
+DIST_SUBDIRS = $(SUBDIRS) include
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libcurl.pc
LIB_VAUTH_CFILES = vauth/vauth.c vauth/cleartext.c vauth/cram.c \

View File

@@ -0,0 +1,2 @@
only_lib.patch
no_netrc.patch

View File

@@ -139,6 +139,13 @@ static int set_normalized_volume(snd_mixer_elem_t *elem,
return set_raw[ctl_dir](elem, value);
}
/* two special cases to avoid rounding errors at 0% and
100% */
if (volume <= 0)
return set_dB[ctl_dir](elem, min, dir);
else if (volume >= 100)
return set_dB[ctl_dir](elem, max, dir);
if (use_linear_dB_scale(min, max)) {
value = lrint_dir(volume * (max - min), dir) + min;
return set_dB[ctl_dir](elem, value, dir);

View File

@@ -88,7 +88,7 @@ static inline ConstBuffer<V>
ToAlsaChannelOrder71(PcmBuffer &buffer, ConstBuffer<V> src)
{
auto dest = buffer.GetT<V>(src.size);
ToAlsaChannelOrder71(dest, src.data, src.size / 6);
ToAlsaChannelOrder71(dest, src.data, src.size / 8);
return { dest, src.size };
}

View File

@@ -21,6 +21,7 @@
#include "ArgParser.hxx"
#include "Ack.hxx"
#include "Chrono.hxx"
#include "util/NumberParser.hxx"
#include <stdlib.h>
@@ -151,7 +152,7 @@ float
ParseCommandArgFloat(const char *s)
{
char *endptr;
auto value = strtof(s, &endptr);
auto value = ParseFloat(s, &endptr);
if (endptr == s || *endptr != 0)
throw FormatProtocolError(ACK_ERROR_ARG,
"Float expected: %s", s);

View File

@@ -35,6 +35,7 @@
#include "util/CharUtil.hxx"
#include "util/StringAPI.hxx"
#include "util/StringCompare.hxx"
#include "util/NumberParser.hxx"
#include "Log.hxx"
#include <string.h>
@@ -148,7 +149,7 @@ playlist_state_restore(const char *line, TextFile &file,
while ((line = file.ReadLine()) != nullptr) {
const char *p;
if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_TIME))) {
seek_time = SongTime::FromS(atof(p));
seek_time = SongTime::FromS(ParseDouble(p));
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_REPEAT))) {
playlist.SetRepeat(pc, StringIsEqual(p, "1"));
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_SINGLE))) {
@@ -158,12 +159,12 @@ playlist_state_restore(const char *line, TextFile &file,
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_CROSSFADE))) {
pc.SetCrossFade(atoi(p));
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_MIXRAMPDB))) {
pc.SetMixRampDb(atof(p));
pc.SetMixRampDb(ParseFloat(p));
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_MIXRAMPDELAY))) {
/* this check discards "nan" which was used
prior to MPD 0.18 */
if (IsDigitASCII(*p))
pc.SetMixRampDelay(atof(p));
pc.SetMixRampDelay(ParseFloat(p));
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_RANDOM))) {
random_mode = StringIsEqual(p, "1");
} else if ((p = StringAfterPrefix(line, PLAYLIST_STATE_FILE_CURRENT))) {

View File

@@ -22,6 +22,7 @@
#include "VorbisComment.hxx"
#include "ReplayGainInfo.hxx"
#include "util/ASCII.hxx"
#include "util/NumberParser.hxx"
#include <assert.h>
#include <stdlib.h>
@@ -33,16 +34,16 @@ ParseReplayGainTagTemplate(ReplayGainInfo &info, const T t)
const char *value;
if ((value = t["replaygain_track_gain"]) != nullptr) {
info.track.gain = atof(value);
info.track.gain = ParseFloat(value);
return true;
} else if ((value = t["replaygain_album_gain"]) != nullptr) {
info.album.gain = atof(value);
info.album.gain = ParseFloat(value);
return true;
} else if ((value = t["replaygain_track_peak"]) != nullptr) {
info.track.peak = atof(value);
info.track.peak = ParseFloat(value);
return true;
} else if ((value = t["replaygain_album_peak"]) != nullptr) {
info.album.peak = atof(value);
info.album.peak = ParseFloat(value);
return true;
} else
return false;

View File

@@ -38,10 +38,12 @@
#include <windows.h>
#endif
#if defined(__linux__) && !defined(ANDROID)
#ifdef __linux__
#ifndef ANDROID
static int
ioprio_set(int which, int who, int ioprio)
linux_ioprio_set(int which, int who, int ioprio)
{
return syscall(__NR_ioprio_set, which, who, ioprio);
}
@@ -55,7 +57,20 @@ ioprio_set_idle()
static constexpr int _IOPRIO_IDLE =
(_IOPRIO_CLASS_IDLE << _IOPRIO_CLASS_SHIFT) | 7;
ioprio_set(_IOPRIO_WHO_PROCESS, 0, _IOPRIO_IDLE);
linux_ioprio_set(_IOPRIO_WHO_PROCESS, 0, _IOPRIO_IDLE);
}
#endif /* !ANDROID */
/**
* Wrapper for the "sched_setscheduler" system call. We don't use the
* one from the C library because Musl has an intentionally broken
* implementation.
*/
static int
linux_sched_setscheduler(pid_t pid, int sched, const struct sched_param *param)
{
return syscall(__NR_sched_setscheduler, pid, sched, param);
}
#endif
@@ -66,7 +81,7 @@ SetThreadIdlePriority()
#ifdef __linux__
#ifdef SCHED_IDLE
static struct sched_param sched_param;
sched_setscheduler(0, SCHED_IDLE, &sched_param);
linux_sched_setscheduler(0, SCHED_IDLE, &sched_param);
#endif
#ifndef ANDROID
@@ -92,7 +107,7 @@ SetThreadRealtime()
policy |= SCHED_RESET_ON_FORK;
#endif
if (sched_setscheduler(0, policy, &sched_param) < 0)
if (linux_sched_setscheduler(0, policy, &sched_param) < 0)
throw MakeErrno("sched_setscheduler failed");
#endif // __linux__
};

View File

@@ -78,7 +78,12 @@ ParseDouble(const char *p, char **endptr=nullptr)
static inline float
ParseFloat(const char *p, char **endptr=nullptr)
{
#if defined(__BIONIC__) && __ANDROID_API__ < 21
/* strtof() requires API level 21 */
return (float)ParseDouble(p, endptr);
#else
return strtof(p, endptr);
#endif
}
#endif