Compare commits
57 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c2940a8385 | ||
![]() |
bede564618 | ||
![]() |
e0ca4b865a | ||
![]() |
31c206bf80 | ||
![]() |
9187a08106 | ||
![]() |
3859a50466 | ||
![]() |
927071e085 | ||
![]() |
6ba918b203 | ||
![]() |
e8b70dbca4 | ||
![]() |
0f8d223c7f | ||
![]() |
19a2885fd5 | ||
![]() |
b8a094470b | ||
![]() |
2988bb77e8 | ||
![]() |
738317bf34 | ||
![]() |
e46fbd0780 | ||
![]() |
56b74ad990 | ||
![]() |
6de92bb42b | ||
![]() |
c801936e53 | ||
![]() |
817656504d | ||
![]() |
6f00f97b66 | ||
![]() |
5acb978f8f | ||
![]() |
975a4ae871 | ||
![]() |
56aaf3c73e | ||
![]() |
12fd1cad0c | ||
![]() |
e573cbf032 | ||
![]() |
dead461542 | ||
![]() |
3d5da1ac73 | ||
![]() |
ec408ca6a6 | ||
![]() |
ea66cdd6a5 | ||
![]() |
f762e8034f | ||
![]() |
bb1e369f30 | ||
![]() |
8376578921 | ||
![]() |
ed2354cd9d | ||
![]() |
386688b87a | ||
![]() |
38d56dddf1 | ||
![]() |
e8975942ec | ||
![]() |
3ca80a7336 | ||
![]() |
d029dae7ad | ||
![]() |
9e058732ee | ||
![]() |
cad5d11261 | ||
![]() |
fcaedec2ab | ||
![]() |
ead9d59e88 | ||
![]() |
34b8a17ccd | ||
![]() |
a53d081c39 | ||
![]() |
823134e4ba | ||
![]() |
272167b4fc | ||
![]() |
92f09bba94 | ||
![]() |
1f50bdb230 | ||
![]() |
2eef4e6716 | ||
![]() |
d989dbfec4 | ||
![]() |
ca9fcec364 | ||
![]() |
354104f9a9 | ||
![]() |
8649ea3d6f | ||
![]() |
752ff12c37 | ||
![]() |
4bb89b1755 | ||
![]() |
0ef553d30e | ||
![]() |
43a62aef07 |
Makefile.amNEWS
android
configure.acdoc
python/build
src
IOThread.cxxIOThread.hxxMusicPipe.hxxSongFilter.cxxSongFilter.hxxSongSave.cxx
archive
plugins
db
decoder
event
filter
plugins
input
lib
mixer
plugins
neighbor
plugins
output
pcm
player
playlist
plugins
protocol
queue
tag
thread
util
test
win32
10
Makefile.am
10
Makefile.am
@@ -61,8 +61,8 @@ src_mpd_LDADD = \
|
||||
libnet.a \
|
||||
$(FS_LIBS) \
|
||||
libsystem.a \
|
||||
libutil.a \
|
||||
$(ICU_LDADD) \
|
||||
libutil.a \
|
||||
$(SYSTEMD_DAEMON_LIBS)
|
||||
|
||||
src_mpd_SOURCES = \
|
||||
@@ -292,7 +292,7 @@ clean-local:
|
||||
libmpd.so: $(filter %.a,$(src_mpd_LDADD)) libmain.a
|
||||
$(AM_V_CXXLD)$(CXXLD) -shared -Wl,--no-undefined,-shared,-Bsymbolic -llog -lz -o $@ $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) src/libmain_a-Main.o $(src_mpd_LDADD) $(LIBS)
|
||||
|
||||
ANDROID_SDK_BUILD_TOOLS_VERSION = 20.0.0
|
||||
ANDROID_SDK_BUILD_TOOLS_VERSION = 27.0.0
|
||||
ANDROID_SDK_PLATFORM = android-17
|
||||
|
||||
ANDROID_BUILD_TOOLS_DIR = $(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)
|
||||
@@ -307,7 +307,7 @@ ANDROID_XML_RES := $(wildcard $(srcdir)/android/res/*/*.xml)
|
||||
ANDROID_XML_RES_COPIES := $(patsubst $(srcdir)/android/%,android/build/%,$(ANDROID_XML_RES))
|
||||
|
||||
JAVA_SOURCE_NAMES = Bridge.java Loader.java Main.java
|
||||
JAVA_SOURCES = $(addprefix $(srcdir)/android/src/,$(JAVA_SOURCE_NAMES))
|
||||
JAVA_SOURCE_PATHS = $(addprefix $(srcdir)/android/src/,$(JAVA_SOURCE_NAMES))
|
||||
|
||||
JAVA_CLASSFILES_DIR = android/build/classes
|
||||
|
||||
@@ -328,7 +328,7 @@ android/build/resources.apk: $(ANDROID_XML_RES_COPIES) android/build/res/drawabl
|
||||
# R.java is generated by aapt, when resources.apk is generated
|
||||
android/build/gen/org/musicpd/R.java: android/build/resources.apk
|
||||
|
||||
android/build/classes.dex: $(JAVA_SOURCES) android/build/gen/org/musicpd/R.java
|
||||
android/build/classes.dex: $(JAVA_SOURCE_PATHS) android/build/gen/org/musicpd/R.java
|
||||
@$(MKDIR_P) $(JAVA_CLASSFILES_DIR)
|
||||
$(JAVAC) -source 1.5 -target 1.5 -Xlint:-options \
|
||||
-cp $(ANDROID_SDK_PLATFORM_DIR)/android.jar:$(JAVA_CLASSFILES_DIR) \
|
||||
@@ -2159,6 +2159,7 @@ test_run_output_LDADD = $(MPD_LIBS) \
|
||||
libutil.a
|
||||
test_run_output_SOURCES = test/run_output.cxx \
|
||||
test/ScopeIOThread.hxx \
|
||||
test/NullMixerListener.hxx \
|
||||
src/Log.cxx src/LogBackend.cxx \
|
||||
src/IOThread.cxx \
|
||||
src/output/Domain.cxx \
|
||||
@@ -2182,6 +2183,7 @@ test_read_mixer_LDADD = \
|
||||
libsystem.a \
|
||||
libutil.a
|
||||
test_read_mixer_SOURCES = test/read_mixer.cxx \
|
||||
test/NullMixerListener.hxx \
|
||||
src/Log.cxx src/LogBackend.cxx \
|
||||
src/mixer/MixerControl.cxx \
|
||||
src/filter/FilterPlugin.cxx \
|
||||
|
19
NEWS
19
NEWS
@@ -1,3 +1,22 @@
|
||||
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
|
||||
* database
|
||||
- simple: fix search within mount points
|
||||
- upnp: enable IPv6
|
||||
* archive
|
||||
- iso9660: libcdio 2.0 compatibility
|
||||
* fix crash in debug build on Haiku and other operating systems
|
||||
|
||||
ver 0.20.15 (2018/01/05)
|
||||
* queue: fix crash after seek failure
|
||||
* resampler
|
||||
|
@@ -2,10 +2,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="13"
|
||||
android:versionName="0.19.9">
|
||||
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"
|
||||
|
@@ -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'
|
||||
@@ -65,7 +65,9 @@ class AndroidNdkToolchain:
|
||||
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
|
||||
llvm_triple = 'armv7-none-linux-androideabi'
|
||||
|
||||
common_flags = '-march=armv7-a -mfloat-abi=softfp'
|
||||
common_flags = '-Os -g'
|
||||
common_flags += ' -fPIC'
|
||||
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')
|
||||
@@ -80,12 +82,12 @@ class AndroidNdkToolchain:
|
||||
self.nm = os.path.join(toolchain_bin, arch + '-nm')
|
||||
self.strip = os.path.join(toolchain_bin, arch + '-strip')
|
||||
|
||||
self.cflags = '-Os -g ' + common_flags
|
||||
self.cxxflags = '-Os -g ' + common_flags
|
||||
self.cflags = common_flags
|
||||
self.cxxflags = common_flags
|
||||
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') + \
|
||||
@@ -100,15 +102,13 @@ class AndroidNdkToolchain:
|
||||
libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++')
|
||||
libcxx_libs_path = os.path.join(libcxx_path, 'libs', android_abi)
|
||||
|
||||
libstdcxx_cppflags = '-nostdinc++ -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include')
|
||||
libstdcxx_ldadd = os.path.join(libcxx_libs_path, 'libc++_static.a') + ' ' + os.path.join(libcxx_libs_path, 'libc++abi.a')
|
||||
|
||||
if self.is_armv7:
|
||||
libstdcxx_ldadd += ' ' + os.path.join(libcxx_libs_path, 'libunwind.a')
|
||||
libstdcxx_flags = '-stdlib=libc++'
|
||||
libstdcxx_cxxflags = libstdcxx_flags + ' -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include')
|
||||
libstdcxx_ldflags = libstdcxx_flags + ' -static-libstdc++ -L' + libcxx_libs_path
|
||||
|
||||
if use_cxx:
|
||||
self.libs += ' ' + libstdcxx_ldadd
|
||||
self.cppflags += ' ' + libstdcxx_cppflags
|
||||
self.cxxflags += ' ' + libstdcxx_cxxflags
|
||||
self.ldflags += ' ' + libstdcxx_ldflags
|
||||
|
||||
self.env = dict(os.environ)
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
AC_PREREQ(2.60)
|
||||
|
||||
AC_INIT(mpd, 0.20.15, 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=15
|
||||
VERSION_REVISION=17
|
||||
VERSION_EXTRA=0
|
||||
|
||||
AC_CONFIG_SRCDIR([src/Main.cxx])
|
||||
|
21
doc/user.xml
21
doc/user.xml
@@ -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>
|
||||
|
@@ -8,6 +8,7 @@ class AutotoolsProject(MakeProject):
|
||||
cppflags='',
|
||||
ldflags='',
|
||||
libs='',
|
||||
subdirs=None,
|
||||
**kwargs):
|
||||
MakeProject.__init__(self, url, md5, installed, **kwargs)
|
||||
self.configure_args = configure_args
|
||||
@@ -15,6 +16,7 @@ class AutotoolsProject(MakeProject):
|
||||
self.cppflags = cppflags
|
||||
self.ldflags = ldflags
|
||||
self.libs = libs
|
||||
self.subdirs = subdirs
|
||||
|
||||
def configure(self, toolchain):
|
||||
src = self.unpack(toolchain)
|
||||
@@ -51,4 +53,8 @@ class AutotoolsProject(MakeProject):
|
||||
|
||||
def build(self, toolchain):
|
||||
build = self.configure(toolchain)
|
||||
MakeProject.build(self, toolchain, build)
|
||||
if self.subdirs is not None:
|
||||
for subdir in self.subdirs:
|
||||
MakeProject.build(self, toolchain, os.path.join(build, subdir))
|
||||
else:
|
||||
MakeProject.build(self, toolchain, build)
|
||||
|
@@ -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
|
||||
@@ -29,6 +31,8 @@ opus = AutotoolsProject(
|
||||
'lib/libopus.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--disable-doc',
|
||||
'--disable-extra-programs',
|
||||
],
|
||||
|
||||
# suppress "visibility default" from opus_defines.h
|
||||
@@ -42,7 +46,9 @@ flac = AutotoolsProject(
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--disable-xmms-plugin', '--disable-cpplibs',
|
||||
'--disable-doxygen-docs',
|
||||
],
|
||||
subdirs=['include', 'src/libFLAC'],
|
||||
)
|
||||
|
||||
zlib = ZlibProject(
|
||||
@@ -83,8 +89,8 @@ libmad = AutotoolsProject(
|
||||
)
|
||||
|
||||
liblame = AutotoolsProject(
|
||||
'http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz',
|
||||
'24346b4158e4af3bd9f2e194bb23eb473c75fb7377011523353196b19b9a23ff',
|
||||
'http://downloads.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz',
|
||||
'ddfe36cab873794038ae2c1210557ad34857a4b6bdc515785d1da9e175b1da1e',
|
||||
'lib/libmp3lame.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -335,8 +341,8 @@ ffmpeg = FfmpegProject(
|
||||
)
|
||||
|
||||
curl = AutotoolsProject(
|
||||
'http://curl.haxx.se/download/curl-7.57.0.tar.xz',
|
||||
'f5f6fd3c72b7b8389969f4fb671ed8532fa9b5bb7a5cae7ca89bc1cea45c7878',
|
||||
'http://curl.haxx.se/download/curl-7.58.0.tar.xz',
|
||||
'6a813875243609eb75f37fa72044e4ad618b55ec15a4eafdac2df6a7e800e3e3',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -354,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(
|
||||
|
@@ -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
9
python/build/quilt.py
Normal 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')
|
@@ -27,12 +27,17 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static struct {
|
||||
static struct IOThread {
|
||||
Mutex mutex;
|
||||
Cond cond;
|
||||
|
||||
EventLoop *loop;
|
||||
Thread thread;
|
||||
|
||||
IOThread():thread(BIND_THIS_METHOD(Run)) {}
|
||||
|
||||
private:
|
||||
void Run() noexcept;
|
||||
} io;
|
||||
|
||||
void
|
||||
@@ -44,15 +49,15 @@ io_thread_run(void)
|
||||
io.loop->Run();
|
||||
}
|
||||
|
||||
static void
|
||||
io_thread_func(gcc_unused void *arg)
|
||||
inline void
|
||||
IOThread::Run() noexcept
|
||||
{
|
||||
SetThreadName("io");
|
||||
|
||||
/* lock+unlock to synchronize with io_thread_start(), to be
|
||||
sure that io.thread is set */
|
||||
io.mutex.lock();
|
||||
io.mutex.unlock();
|
||||
mutex.lock();
|
||||
mutex.unlock();
|
||||
|
||||
io_thread_run();
|
||||
}
|
||||
@@ -73,7 +78,7 @@ io_thread_start()
|
||||
assert(!io.thread.IsDefined());
|
||||
|
||||
const std::lock_guard<Mutex> protect(io.mutex);
|
||||
io.thread.Start(io_thread_func, nullptr);
|
||||
io.thread.Start();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -103,8 +108,12 @@ io_thread_get() noexcept
|
||||
return *io.loop;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
bool
|
||||
io_thread_inside() noexcept
|
||||
{
|
||||
return io.thread.IsInside();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#ifndef MPD_IO_THREAD_HXX
|
||||
#define MPD_IO_THREAD_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Compiler.h"
|
||||
|
||||
class EventLoop;
|
||||
@@ -53,6 +54,8 @@ gcc_const
|
||||
EventLoop &
|
||||
io_thread_get() noexcept;
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
/**
|
||||
* Is the current thread the I/O thread?
|
||||
*/
|
||||
@@ -61,3 +64,5 @@ bool
|
||||
io_thread_inside() noexcept;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@@ -95,6 +95,7 @@ public:
|
||||
*/
|
||||
gcc_pure
|
||||
const MusicChunk *Peek() const noexcept {
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
return head;
|
||||
}
|
||||
|
||||
@@ -120,6 +121,7 @@ public:
|
||||
*/
|
||||
gcc_pure
|
||||
unsigned GetSize() const noexcept {
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@@ -24,6 +24,8 @@
|
||||
#include "tag/Tag.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
#include "util/StringAPI.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/TimeParser.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
@@ -59,7 +61,7 @@ locate_parse_type(const char *str) noexcept
|
||||
|
||||
SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
|
||||
:tag(_tag),
|
||||
value(AllocatedString<>::Duplicate(_value)),
|
||||
value(_value),
|
||||
fold_case(_fold_case ? IcuCompare(value.c_str()) : IcuCompare())
|
||||
{
|
||||
}
|
||||
@@ -274,3 +276,33 @@ SongFilter::GetBase() const noexcept
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SongFilter
|
||||
SongFilter::WithoutBasePrefix(const char *_prefix) const noexcept
|
||||
{
|
||||
const StringView prefix(_prefix);
|
||||
SongFilter result;
|
||||
|
||||
for (const auto &i : items) {
|
||||
if (i.GetTag() == LOCATE_TAG_BASE_TYPE) {
|
||||
const char *s = StringAfterPrefix(i.GetValue(), prefix);
|
||||
if (s != nullptr) {
|
||||
if (*s == 0)
|
||||
continue;
|
||||
|
||||
if (*s == '/') {
|
||||
++s;
|
||||
|
||||
if (*s != 0)
|
||||
result.items.emplace_back(LOCATE_TAG_BASE_TYPE, s);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.items.emplace_back(i);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@@ -21,9 +21,9 @@
|
||||
#define MPD_SONG_FILTER_HXX
|
||||
|
||||
#include "lib/icu/Compare.hxx"
|
||||
#include "util/AllocatedString.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include <stdint.h>
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
class Item {
|
||||
uint8_t tag;
|
||||
|
||||
AllocatedString<> value;
|
||||
std::string value;
|
||||
|
||||
/**
|
||||
* This value is only set if case folding is enabled.
|
||||
@@ -66,11 +66,6 @@ public:
|
||||
Item(unsigned tag, const char *value, bool fold_case=false);
|
||||
Item(unsigned tag, time_t time);
|
||||
|
||||
Item(const Item &other) = delete;
|
||||
Item(Item &&) = default;
|
||||
|
||||
Item &operator=(const Item &other) = delete;
|
||||
|
||||
unsigned GetTag() const {
|
||||
return tag;
|
||||
}
|
||||
@@ -157,6 +152,13 @@ public:
|
||||
*/
|
||||
gcc_pure
|
||||
const char *GetBase() const noexcept;
|
||||
|
||||
/**
|
||||
* Create a copy of the filter with the given prefix stripped
|
||||
* from all #LOCATE_TAG_BASE_TYPE items. This is used to
|
||||
* filter songs in mounted databases.
|
||||
*/
|
||||
SongFilter WithoutBasePrefix(const char *prefix) const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -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) {
|
||||
|
@@ -115,7 +115,12 @@ Iso9660ArchiveFile::Visit(char *path, size_t length, size_t capacity,
|
||||
visitor.VisitArchiveEntry(path + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#if LIBCDIO_VERSION_NUM >= 20000
|
||||
iso9660_filelist_free(entlist);
|
||||
#else
|
||||
_cdio_list_free (entlist, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
static ArchiveFile *
|
||||
|
@@ -20,18 +20,12 @@
|
||||
#include "config.h"
|
||||
#include "Mount.hxx"
|
||||
#include "PrefixedLightSong.hxx"
|
||||
#include "SongFilter.hxx"
|
||||
#include "db/Selection.hxx"
|
||||
#include "db/LightDirectory.hxx"
|
||||
#include "db/Interface.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
|
||||
#ifdef _LIBCPP_VERSION
|
||||
/* workaround for "error: incomplete type 'PlaylistInfo' used in type
|
||||
trait expression" with libc++ version 3900 (from Android NDK
|
||||
r13b) */
|
||||
#include "db/PlaylistInfo.hxx"
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
struct PrefixedLightDirectory : LightDirectory {
|
||||
@@ -93,5 +87,16 @@ WalkMount(const char *base, const Database &db,
|
||||
vp = std::bind(PrefixVisitPlaylist,
|
||||
base, std::ref(visit_playlist), _1, _2);
|
||||
|
||||
SongFilter prefix_filter;
|
||||
|
||||
if (base != nullptr && filter != nullptr) {
|
||||
/* if the SongFilter contains a LOCATE_TAG_BASE_TYPE
|
||||
item, copy the SongFilter and drop the mount point
|
||||
from the filter, because the mounted database
|
||||
doesn't know its own location within MPD's VFS */
|
||||
prefix_filter = filter->WithoutBasePrefix(base);
|
||||
filter = &prefix_filter;
|
||||
}
|
||||
|
||||
db.Visit(DatabaseSelection(uri, recursive, filter), vd, vs, vp);
|
||||
}
|
||||
|
@@ -49,6 +49,10 @@ struct UpdateQueueItem {
|
||||
bool IsDefined() const {
|
||||
return id != 0;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
id = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class UpdateQueue {
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include "Idle.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "thread/Thread.hxx"
|
||||
#include "thread/Name.hxx"
|
||||
#include "thread/Util.hxx"
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -43,6 +44,7 @@ UpdateService::UpdateService(EventLoop &_loop, SimpleDatabase &_db,
|
||||
:DeferredMonitor(_loop),
|
||||
db(_db), storage(_storage),
|
||||
listener(_listener),
|
||||
update_thread(BIND_THIS_METHOD(Task)),
|
||||
update_task_id(0),
|
||||
walk(nullptr)
|
||||
{
|
||||
@@ -112,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());
|
||||
@@ -140,13 +144,6 @@ UpdateService::Task()
|
||||
DeferredMonitor::Schedule();
|
||||
}
|
||||
|
||||
void
|
||||
UpdateService::Task(void *ctx)
|
||||
{
|
||||
UpdateService &service = *(UpdateService *)ctx;
|
||||
return service.Task();
|
||||
}
|
||||
|
||||
void
|
||||
UpdateService::StartThread(UpdateQueueItem &&i)
|
||||
{
|
||||
@@ -158,7 +155,7 @@ UpdateService::StartThread(UpdateQueueItem &&i)
|
||||
next = std::move(i);
|
||||
walk = new UpdateWalk(GetEventLoop(), listener, *next.storage);
|
||||
|
||||
update_thread.Start(Task, this);
|
||||
update_thread.Start();
|
||||
|
||||
FormatDebug(update_domain,
|
||||
"spawned thread for update job id %i", next.id);
|
||||
@@ -258,7 +255,7 @@ UpdateService::RunDeferred()
|
||||
delete walk;
|
||||
walk = nullptr;
|
||||
|
||||
next = UpdateQueueItem();
|
||||
next.Clear();
|
||||
|
||||
idle_add(IDLE_UPDATE);
|
||||
|
||||
|
@@ -98,7 +98,6 @@ private:
|
||||
|
||||
/* the update thread */
|
||||
void Task();
|
||||
static void Task(void *ctx);
|
||||
|
||||
void StartThread(UpdateQueueItem &&i);
|
||||
|
||||
|
@@ -30,7 +30,8 @@
|
||||
DecoderControl::DecoderControl(Mutex &_mutex, Cond &_client_cond,
|
||||
const AudioFormat _configured_audio_format,
|
||||
const ReplayGainConfig &_replay_gain_config)
|
||||
:mutex(_mutex), client_cond(_client_cond),
|
||||
:thread(BIND_THIS_METHOD(RunThread)),
|
||||
mutex(_mutex), client_cond(_client_cond),
|
||||
configured_audio_format(_configured_audio_format),
|
||||
replay_gain_config(_replay_gain_config) {}
|
||||
|
||||
|
@@ -415,6 +415,9 @@ public:
|
||||
* mixramp_start/mixramp_end.
|
||||
*/
|
||||
void CycleMixRamp();
|
||||
|
||||
private:
|
||||
void RunThread();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -513,30 +513,28 @@ try {
|
||||
dc.client_cond.signal();
|
||||
}
|
||||
|
||||
static void
|
||||
decoder_task(void *arg)
|
||||
void
|
||||
DecoderControl::RunThread()
|
||||
{
|
||||
DecoderControl &dc = *(DecoderControl *)arg;
|
||||
|
||||
SetThreadName("decoder");
|
||||
|
||||
const std::lock_guard<Mutex> protect(dc.mutex);
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
|
||||
do {
|
||||
assert(dc.state == DecoderState::STOP ||
|
||||
dc.state == DecoderState::ERROR);
|
||||
assert(state == DecoderState::STOP ||
|
||||
state == DecoderState::ERROR);
|
||||
|
||||
switch (dc.command) {
|
||||
switch (command) {
|
||||
case DecoderCommand::START:
|
||||
dc.CycleMixRamp();
|
||||
dc.replay_gain_prev_db = dc.replay_gain_db;
|
||||
dc.replay_gain_db = 0;
|
||||
CycleMixRamp();
|
||||
replay_gain_prev_db = replay_gain_db;
|
||||
replay_gain_db = 0;
|
||||
|
||||
decoder_run(dc);
|
||||
decoder_run(*this);
|
||||
|
||||
if (dc.state == DecoderState::ERROR) {
|
||||
if (state == DecoderState::ERROR) {
|
||||
try {
|
||||
std::rethrow_exception(dc.error);
|
||||
std::rethrow_exception(error);
|
||||
} catch (const std::exception &e) {
|
||||
LogError(e);
|
||||
} catch (...) {
|
||||
@@ -552,20 +550,20 @@ decoder_task(void *arg)
|
||||
/* we need to clear the pipe here; usually the
|
||||
PlayerThread is responsible, but it is not
|
||||
aware that the decoder has finished */
|
||||
dc.pipe->Clear(*dc.buffer);
|
||||
pipe->Clear(*buffer);
|
||||
|
||||
decoder_run(dc);
|
||||
decoder_run(*this);
|
||||
break;
|
||||
|
||||
case DecoderCommand::STOP:
|
||||
dc.CommandFinishedLocked();
|
||||
CommandFinishedLocked();
|
||||
break;
|
||||
|
||||
case DecoderCommand::NONE:
|
||||
dc.Wait();
|
||||
Wait();
|
||||
break;
|
||||
}
|
||||
} while (dc.command != DecoderCommand::NONE || !dc.quit);
|
||||
} while (command != DecoderCommand::NONE || !quit);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -574,5 +572,5 @@ decoder_thread_start(DecoderControl &dc)
|
||||
assert(!dc.thread.IsDefined());
|
||||
|
||||
dc.quit = false;
|
||||
dc.thread.Start(decoder_task, &dc);
|
||||
dc.thread.Start();
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
EventLoop::EventLoop()
|
||||
:SocketMonitor(*this)
|
||||
:SocketMonitor(*this), quit(false)
|
||||
{
|
||||
SocketMonitor::Open(wake_fd.Get());
|
||||
SocketMonitor::Schedule(SocketMonitor::READ);
|
||||
@@ -46,7 +46,9 @@ EventLoop::~EventLoop()
|
||||
void
|
||||
EventLoop::Break()
|
||||
{
|
||||
quit = true;
|
||||
if (quit.exchange(true))
|
||||
return;
|
||||
|
||||
wake_fd.Write();
|
||||
}
|
||||
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include "SocketMonitor.hxx"
|
||||
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
@@ -82,7 +83,7 @@ class EventLoop final : SocketMonitor
|
||||
|
||||
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
|
||||
|
||||
bool quit = false;
|
||||
std::atomic_bool quit;
|
||||
|
||||
/**
|
||||
* True when the object has been modified and another check is
|
||||
|
@@ -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 = {
|
||||
|
@@ -54,10 +54,10 @@ ThreadInputStream::Start()
|
||||
assert(p != nullptr);
|
||||
|
||||
buffer = new CircularBuffer<uint8_t>((uint8_t *)p, buffer_size);
|
||||
thread.Start(ThreadFunc, this);
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
inline void
|
||||
void
|
||||
ThreadInputStream::ThreadFunc()
|
||||
{
|
||||
FormatThreadName("input:%s", plugin);
|
||||
@@ -107,13 +107,6 @@ ThreadInputStream::ThreadFunc()
|
||||
Close();
|
||||
}
|
||||
|
||||
void
|
||||
ThreadInputStream::ThreadFunc(void *ctx)
|
||||
{
|
||||
ThreadInputStream &tis = *(ThreadInputStream *)ctx;
|
||||
tis.ThreadFunc();
|
||||
}
|
||||
|
||||
void
|
||||
ThreadInputStream::Check()
|
||||
{
|
||||
|
@@ -73,6 +73,7 @@ public:
|
||||
size_t _buffer_size)
|
||||
:InputStream(_uri, _mutex, _cond),
|
||||
plugin(_plugin),
|
||||
thread(BIND_THIS_METHOD(ThreadFunc)),
|
||||
buffer_size(_buffer_size) {}
|
||||
|
||||
virtual ~ThreadInputStream();
|
||||
@@ -138,7 +139,6 @@ protected:
|
||||
|
||||
private:
|
||||
void ThreadFunc();
|
||||
static void ThreadFunc(void *ctx);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -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(),
|
||||
|
20
src/lib/curl/patches/no_netrc.patch
Normal file
20
src/lib/curl/patches/no_netrc.patch
Normal 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;
|
||||
}
|
15
src/lib/curl/patches/only_lib.patch
Normal file
15
src/lib/curl/patches/only_lib.patch
Normal 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 \
|
2
src/lib/curl/patches/series
Normal file
2
src/lib/curl/patches/series
Normal file
@@ -0,0 +1,2 @@
|
||||
only_lib.patch
|
||||
no_netrc.patch
|
@@ -37,6 +37,18 @@ public:
|
||||
|
||||
explicit IcuCompare(const char *needle) noexcept;
|
||||
|
||||
IcuCompare(const IcuCompare &src) noexcept
|
||||
:needle(src
|
||||
? AllocatedString<>::Duplicate(src.needle.c_str())
|
||||
: nullptr) {}
|
||||
|
||||
IcuCompare &operator=(const IcuCompare &src) noexcept {
|
||||
needle = src
|
||||
? AllocatedString<>::Duplicate(src.needle.c_str())
|
||||
: nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
IcuCompare(IcuCompare &&) = default;
|
||||
IcuCompare &operator=(IcuCompare &&) = default;
|
||||
|
||||
|
@@ -34,7 +34,11 @@ static unsigned upnp_ref;
|
||||
static void
|
||||
DoInit()
|
||||
{
|
||||
auto code = UpnpInit(0, 0);
|
||||
#ifdef UPNP_ENABLE_IPV6
|
||||
auto code = UpnpInit2(nullptr, 0);
|
||||
#else
|
||||
auto code = UpnpInit(nullptr, 0);
|
||||
#endif
|
||||
if (code != UPNP_E_SUCCESS)
|
||||
throw FormatRuntimeError("UpnpInit() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
|
@@ -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);
|
||||
|
@@ -69,7 +69,8 @@ class SmbclientNeighborExplorer final : public NeighborExplorer {
|
||||
|
||||
public:
|
||||
SmbclientNeighborExplorer(NeighborListener &_listener)
|
||||
:NeighborExplorer(_listener) {}
|
||||
:NeighborExplorer(_listener),
|
||||
thread(BIND_THIS_METHOD(ThreadFunc)) {}
|
||||
|
||||
/* virtual methods from class NeighborExplorer */
|
||||
void Open() override;
|
||||
@@ -79,14 +80,13 @@ public:
|
||||
private:
|
||||
void Run();
|
||||
void ThreadFunc();
|
||||
static void ThreadFunc(void *ctx);
|
||||
};
|
||||
|
||||
void
|
||||
SmbclientNeighborExplorer::Open()
|
||||
{
|
||||
quit = false;
|
||||
thread.Start(ThreadFunc, this);
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -239,6 +239,8 @@ SmbclientNeighborExplorer::Run()
|
||||
inline void
|
||||
SmbclientNeighborExplorer::ThreadFunc()
|
||||
{
|
||||
SetThreadName("smbclient");
|
||||
|
||||
mutex.lock();
|
||||
|
||||
while (!quit) {
|
||||
@@ -257,15 +259,6 @@ SmbclientNeighborExplorer::ThreadFunc()
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
SmbclientNeighborExplorer::ThreadFunc(void *ctx)
|
||||
{
|
||||
SetThreadName("smbclient");
|
||||
|
||||
SmbclientNeighborExplorer &e = *(SmbclientNeighborExplorer *)ctx;
|
||||
e.ThreadFunc();
|
||||
}
|
||||
|
||||
static NeighborExplorer *
|
||||
smbclient_neighbor_create(gcc_unused EventLoop &loop,
|
||||
NeighborListener &listener,
|
||||
|
@@ -51,7 +51,8 @@
|
||||
|
||||
AudioOutput::AudioOutput(const AudioOutputPlugin &_plugin,
|
||||
const ConfigBlock &block)
|
||||
:plugin(_plugin)
|
||||
:plugin(_plugin),
|
||||
thread(BIND_THIS_METHOD(Task))
|
||||
{
|
||||
assert(plugin.finish != nullptr);
|
||||
assert(plugin.open != nullptr);
|
||||
|
@@ -515,7 +515,6 @@ private:
|
||||
* The OutputThread.
|
||||
*/
|
||||
void Task();
|
||||
static void Task(void *arg);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -396,7 +396,7 @@ AudioOutput::Pause()
|
||||
pause = false;
|
||||
}
|
||||
|
||||
inline void
|
||||
void
|
||||
AudioOutput::Task()
|
||||
{
|
||||
FormatThreadName("output:%s", name);
|
||||
@@ -512,17 +512,10 @@ AudioOutput::Task()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioOutput::Task(void *arg)
|
||||
{
|
||||
AudioOutput *ao = (AudioOutput *)arg;
|
||||
ao->Task();
|
||||
}
|
||||
|
||||
void
|
||||
AudioOutput::StartThread()
|
||||
{
|
||||
assert(command == Command::NONE);
|
||||
|
||||
thread.Start(Task, this);
|
||||
thread.Start();
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include "../Wrapper.hxx"
|
||||
#include "mixer/MixerList.hxx"
|
||||
#include "mixer/plugins/PulseMixerPlugin.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <pulse/thread-mainloop.h>
|
||||
@@ -854,7 +855,10 @@ PulseOutput::TestDefaultDevice()
|
||||
try {
|
||||
const ConfigBlock empty;
|
||||
PulseOutput po(empty);
|
||||
po.Enable();
|
||||
AtScopeExit(&po) { po.Disable(); };
|
||||
po.WaitConnection();
|
||||
|
||||
return true;
|
||||
} catch (const std::runtime_error &e) {
|
||||
return false;
|
||||
|
@@ -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 };
|
||||
}
|
||||
|
||||
|
@@ -37,6 +37,7 @@ PlayerControl::PlayerControl(PlayerListener &_listener,
|
||||
buffer_chunks(_buffer_chunks),
|
||||
buffered_before_play(_buffered_before_play),
|
||||
configured_audio_format(_configured_audio_format),
|
||||
thread(BIND_THIS_METHOD(RunThread)),
|
||||
replay_gain_config(_replay_gain_config)
|
||||
{
|
||||
}
|
||||
|
@@ -537,6 +537,9 @@ public:
|
||||
void ApplyEnabled() override {
|
||||
LockUpdateAudio();
|
||||
}
|
||||
|
||||
private:
|
||||
void RunThread();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -1147,91 +1147,89 @@ do_play(PlayerControl &pc, DecoderControl &dc,
|
||||
player.Run();
|
||||
}
|
||||
|
||||
static void
|
||||
player_task(void *arg)
|
||||
void
|
||||
PlayerControl::RunThread()
|
||||
{
|
||||
PlayerControl &pc = *(PlayerControl *)arg;
|
||||
|
||||
SetThreadName("player");
|
||||
|
||||
DecoderControl dc(pc.mutex, pc.cond,
|
||||
pc.configured_audio_format,
|
||||
pc.replay_gain_config);
|
||||
DecoderControl dc(mutex, cond,
|
||||
configured_audio_format,
|
||||
replay_gain_config);
|
||||
decoder_thread_start(dc);
|
||||
|
||||
MusicBuffer buffer(pc.buffer_chunks);
|
||||
MusicBuffer buffer(buffer_chunks);
|
||||
|
||||
pc.Lock();
|
||||
Lock();
|
||||
|
||||
while (1) {
|
||||
switch (pc.command) {
|
||||
switch (command) {
|
||||
case PlayerCommand::SEEK:
|
||||
case PlayerCommand::QUEUE:
|
||||
assert(pc.next_song != nullptr);
|
||||
assert(next_song != nullptr);
|
||||
|
||||
pc.Unlock();
|
||||
do_play(pc, dc, buffer);
|
||||
pc.listener.OnPlayerSync();
|
||||
pc.Lock();
|
||||
Unlock();
|
||||
do_play(*this, dc, buffer);
|
||||
listener.OnPlayerSync();
|
||||
Lock();
|
||||
break;
|
||||
|
||||
case PlayerCommand::STOP:
|
||||
pc.Unlock();
|
||||
pc.outputs.Cancel();
|
||||
pc.Lock();
|
||||
Unlock();
|
||||
outputs.Cancel();
|
||||
Lock();
|
||||
|
||||
/* fall through */
|
||||
|
||||
case PlayerCommand::PAUSE:
|
||||
delete pc.next_song;
|
||||
pc.next_song = nullptr;
|
||||
delete next_song;
|
||||
next_song = nullptr;
|
||||
|
||||
pc.CommandFinished();
|
||||
CommandFinished();
|
||||
break;
|
||||
|
||||
case PlayerCommand::CLOSE_AUDIO:
|
||||
pc.Unlock();
|
||||
Unlock();
|
||||
|
||||
pc.outputs.Release();
|
||||
outputs.Release();
|
||||
|
||||
pc.Lock();
|
||||
pc.CommandFinished();
|
||||
Lock();
|
||||
CommandFinished();
|
||||
|
||||
assert(buffer.IsEmptyUnsafe());
|
||||
|
||||
break;
|
||||
|
||||
case PlayerCommand::UPDATE_AUDIO:
|
||||
pc.Unlock();
|
||||
pc.outputs.EnableDisable();
|
||||
pc.Lock();
|
||||
pc.CommandFinished();
|
||||
Unlock();
|
||||
outputs.EnableDisable();
|
||||
Lock();
|
||||
CommandFinished();
|
||||
break;
|
||||
|
||||
case PlayerCommand::EXIT:
|
||||
pc.Unlock();
|
||||
Unlock();
|
||||
|
||||
dc.Quit();
|
||||
|
||||
pc.outputs.Close();
|
||||
outputs.Close();
|
||||
|
||||
pc.LockCommandFinished();
|
||||
LockCommandFinished();
|
||||
return;
|
||||
|
||||
case PlayerCommand::CANCEL:
|
||||
delete pc.next_song;
|
||||
pc.next_song = nullptr;
|
||||
delete next_song;
|
||||
next_song = nullptr;
|
||||
|
||||
pc.CommandFinished();
|
||||
CommandFinished();
|
||||
break;
|
||||
|
||||
case PlayerCommand::REFRESH:
|
||||
/* no-op when not playing */
|
||||
pc.CommandFinished();
|
||||
CommandFinished();
|
||||
break;
|
||||
|
||||
case PlayerCommand::NONE:
|
||||
pc.Wait();
|
||||
Wait();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1242,5 +1240,5 @@ StartPlayerThread(PlayerControl &pc)
|
||||
{
|
||||
assert(!pc.thread.IsDefined());
|
||||
|
||||
pc.thread.Start(player_task, &pc);
|
||||
pc.thread.Start();
|
||||
}
|
||||
|
@@ -313,7 +313,7 @@ soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond)
|
||||
|
||||
SoundCloudJsonData data;
|
||||
yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, &data);
|
||||
AtScopeExit(hand, &data) { yajl_free(hand); };
|
||||
AtScopeExit(hand) { yajl_free(hand); };
|
||||
|
||||
int ret = soundcloud_parse_json(u, hand, mutex, cond);
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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))) {
|
||||
|
@@ -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;
|
||||
|
@@ -52,13 +52,11 @@ public:
|
||||
constexpr ThreadId(pthread_t _id):id(_id) {}
|
||||
#endif
|
||||
|
||||
gcc_const
|
||||
static ThreadId Null() noexcept {
|
||||
static constexpr ThreadId Null() noexcept {
|
||||
#ifdef _WIN32
|
||||
return 0;
|
||||
#else
|
||||
static ThreadId null;
|
||||
return null;
|
||||
return pthread_t();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -81,11 +79,13 @@ public:
|
||||
|
||||
gcc_pure
|
||||
bool operator==(const ThreadId &other) const noexcept {
|
||||
#ifdef _WIN32
|
||||
/* note: not using pthread_equal() because that
|
||||
function "is undefined if either thread ID is not
|
||||
valid so we can't safely use it on
|
||||
default-constructed values" (comment from
|
||||
libstdc++) - and if both libstdc++ and libc++ get
|
||||
away with this, we can do it as well */
|
||||
return id == other.id;
|
||||
#else
|
||||
return pthread_equal(id, other.id);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -25,39 +25,21 @@
|
||||
#include "java/Global.hxx"
|
||||
#endif
|
||||
|
||||
bool
|
||||
Thread::Start(void (*_f)(void *ctx), void *_ctx)
|
||||
void
|
||||
Thread::Start()
|
||||
{
|
||||
assert(!IsDefined());
|
||||
|
||||
f = _f;
|
||||
ctx = _ctx;
|
||||
|
||||
#ifdef _WIN32
|
||||
handle = ::CreateThread(nullptr, 0, ThreadProc, this, 0, &id);
|
||||
if (handle == nullptr)
|
||||
throw MakeLastError("Failed to create thread");
|
||||
#else
|
||||
#ifndef NDEBUG
|
||||
creating = true;
|
||||
#endif
|
||||
|
||||
int e = pthread_create(&handle, nullptr, ThreadProc, this);
|
||||
|
||||
if (e != 0) {
|
||||
#ifndef NDEBUG
|
||||
creating = false;
|
||||
#endif
|
||||
if (e != 0)
|
||||
throw MakeErrno(e, "Failed to create thread");
|
||||
}
|
||||
|
||||
defined = true;
|
||||
#ifndef NDEBUG
|
||||
creating = false;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -72,7 +54,17 @@ Thread::Join()
|
||||
handle = nullptr;
|
||||
#else
|
||||
pthread_join(handle, nullptr);
|
||||
defined = false;
|
||||
handle = pthread_t();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void
|
||||
Thread::Run()
|
||||
{
|
||||
f();
|
||||
|
||||
#ifdef ANDROID
|
||||
Java::DetachCurrentThread();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -83,7 +75,7 @@ Thread::ThreadProc(LPVOID ctx)
|
||||
{
|
||||
Thread &thread = *(Thread *)ctx;
|
||||
|
||||
thread.f(thread.ctx);
|
||||
thread.Run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -95,18 +87,10 @@ Thread::ThreadProc(void *ctx)
|
||||
Thread &thread = *(Thread *)ctx;
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* this works around a race condition that causes an assertion
|
||||
failure due to IsInside() spuriously returning false right
|
||||
after the thread has been created, and the calling thread
|
||||
hasn't initialised "defined" yet */
|
||||
thread.defined = true;
|
||||
thread.inside_handle = pthread_self();
|
||||
#endif
|
||||
|
||||
thread.f(thread.ctx);
|
||||
|
||||
#ifdef ANDROID
|
||||
Java::DetachCurrentThread();
|
||||
#endif
|
||||
thread.Run();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#define MPD_THREAD_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "util/BindMethod.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -32,28 +33,28 @@
|
||||
#include <assert.h>
|
||||
|
||||
class Thread {
|
||||
typedef BoundMethod<void()> Function;
|
||||
const Function f;
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE handle = nullptr;
|
||||
DWORD id;
|
||||
#else
|
||||
pthread_t handle;
|
||||
bool defined = false;
|
||||
pthread_t handle = pthread_t();
|
||||
|
||||
#ifndef NDEBUG
|
||||
/**
|
||||
* The thread is currently being created. This is a workaround for
|
||||
* IsInside(), which may return false until pthread_create() has
|
||||
* initialised the #handle.
|
||||
* This handle is only used by IsInside(), and is set by the
|
||||
* thread function. Since #handle is set by pthread_create()
|
||||
* which is racy, we need this attribute for early checks
|
||||
* inside the thread function.
|
||||
*/
|
||||
bool creating = false;
|
||||
pthread_t inside_handle = pthread_t();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void (*f)(void *ctx);
|
||||
void *ctx;
|
||||
|
||||
public:
|
||||
Thread() = default;
|
||||
explicit Thread(Function _f):f(_f) {}
|
||||
|
||||
Thread(const Thread &) = delete;
|
||||
|
||||
@@ -69,10 +70,11 @@ public:
|
||||
#ifdef _WIN32
|
||||
return handle != nullptr;
|
||||
#else
|
||||
return defined;
|
||||
return handle != pthread_t();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
/**
|
||||
* Check if this thread is the current thread.
|
||||
*/
|
||||
@@ -81,18 +83,23 @@ public:
|
||||
#ifdef _WIN32
|
||||
return GetCurrentThreadId() == id;
|
||||
#else
|
||||
#ifdef NDEBUG
|
||||
constexpr bool creating = false;
|
||||
#endif
|
||||
return IsDefined() && (creating ||
|
||||
pthread_equal(pthread_self(), handle));
|
||||
/* note: not using pthread_equal() because that
|
||||
function "is undefined if either thread ID is not
|
||||
valid so we can't safely use it on
|
||||
default-constructed values" (comment from
|
||||
libstdc++) - and if both libstdc++ and libc++ get
|
||||
away with this, we can do it as well */
|
||||
return pthread_self() == inside_handle;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Start(void (*f)(void *ctx), void *ctx);
|
||||
void Start();
|
||||
void Join();
|
||||
|
||||
private:
|
||||
void Run();
|
||||
|
||||
#ifdef _WIN32
|
||||
static DWORD WINAPI ThreadProc(LPVOID ctx);
|
||||
#else
|
||||
|
@@ -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__
|
||||
};
|
||||
|
@@ -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
|
||||
|
@@ -42,11 +42,6 @@ class RefCount {
|
||||
std::atomic_uint n;
|
||||
|
||||
public:
|
||||
#ifndef _LIBCPP_VERSION
|
||||
/* the "constexpr" is missing in libc++'s "atomic"
|
||||
implementation */
|
||||
constexpr
|
||||
#endif
|
||||
RefCount():n(1) {}
|
||||
|
||||
void Increment() {
|
||||
|
30
test/NullMixerListener.hxx
Normal file
30
test/NullMixerListener.hxx
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef NULL_MIXER_LISTENER_HXX
|
||||
#define NULL_MIXER_LISTENER_HXX
|
||||
|
||||
#include "mixer/Listener.hxx"
|
||||
|
||||
class NullMixerListener : public MixerListener {
|
||||
public:
|
||||
void OnMixerVolumeChanged(Mixer &, int) override {}
|
||||
};
|
||||
|
||||
#endif
|
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "NullMixerListener.hxx"
|
||||
#include "mixer/MixerControl.hxx"
|
||||
#include "mixer/MixerList.hxx"
|
||||
#include "filter/FilterRegistry.hxx"
|
||||
@@ -50,9 +51,14 @@ try {
|
||||
|
||||
EventLoop event_loop;
|
||||
|
||||
NullMixerListener mixer_listener;
|
||||
Mixer *mixer = mixer_new(event_loop, alsa_mixer_plugin,
|
||||
*(AudioOutput *)nullptr,
|
||||
*(MixerListener *)nullptr,
|
||||
/* ugly dangerous dummy pointer to
|
||||
make the compiler happy; this
|
||||
works with most mixers, because
|
||||
they don't need the AudioOutput */
|
||||
*(AudioOutput *)0x1,
|
||||
mixer_listener,
|
||||
ConfigBlock());
|
||||
|
||||
mixer_open(mixer);
|
||||
|
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "NullMixerListener.hxx"
|
||||
#include "output/Internal.hxx"
|
||||
#include "output/OutputPlugin.hxx"
|
||||
#include "output/Client.hxx"
|
||||
@@ -44,6 +45,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void AudioOutput::Task() {}
|
||||
|
||||
class DummyAudioOutputClient final : public AudioOutputClient {
|
||||
public:
|
||||
/* virtual methods from AudioOutputClient */
|
||||
@@ -62,7 +65,9 @@ filter_plugin_by_name(gcc_unused const char *name) noexcept
|
||||
}
|
||||
|
||||
static AudioOutput *
|
||||
load_audio_output(EventLoop &event_loop, AudioOutputClient &client,
|
||||
load_audio_output(EventLoop &event_loop,
|
||||
NullMixerListener &mixer_listener,
|
||||
AudioOutputClient &client,
|
||||
const char *name)
|
||||
{
|
||||
const auto *param = config_find_block(ConfigBlockOption::AUDIO_OUTPUT,
|
||||
@@ -72,7 +77,7 @@ load_audio_output(EventLoop &event_loop, AudioOutputClient &client,
|
||||
name);
|
||||
|
||||
return audio_output_new(event_loop, ReplayGainConfig(), *param,
|
||||
*(MixerListener *)nullptr,
|
||||
mixer_listener,
|
||||
client);
|
||||
}
|
||||
|
||||
@@ -142,8 +147,10 @@ try {
|
||||
|
||||
/* initialize the audio output */
|
||||
|
||||
NullMixerListener mixer_listener;
|
||||
DummyAudioOutputClient client;
|
||||
AudioOutput *ao = load_audio_output(event_loop, client, argv[2]);
|
||||
AudioOutput *ao = load_audio_output(event_loop, mixer_listener,
|
||||
client, argv[2]);
|
||||
|
||||
/* parse the audio format */
|
||||
|
||||
|
@@ -50,14 +50,14 @@ class CrossGccToolchain:
|
||||
self.nm = os.path.join(toolchain_bin, arch + '-nm')
|
||||
self.strip = os.path.join(toolchain_bin, arch + '-strip')
|
||||
|
||||
common_flags = ''
|
||||
common_flags = '-O2 -g'
|
||||
|
||||
if not x64:
|
||||
# enable SSE support which is required for LAME
|
||||
common_flags += ' -march=pentium3'
|
||||
|
||||
self.cflags = '-O2 -g ' + common_flags
|
||||
self.cxxflags = '-O2 -g ' + common_flags
|
||||
self.cflags = common_flags
|
||||
self.cxxflags = common_flags
|
||||
self.cppflags = '-isystem ' + os.path.join(install_prefix, 'include')
|
||||
self.ldflags = '-L' + os.path.join(install_prefix, 'lib')
|
||||
self.libs = ''
|
||||
|
Reference in New Issue
Block a user