Commit d1e51243 authored by Konstantin A. Lepikhov's avatar Konstantin A. Lepikhov

Merge tag 'v0.23.8' of https://github.com/MusicPlayerDaemon/MPD into sisyphus

release v0.23.8
parents 1c467988 1f287904

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

BasedOnStyle: LLVM
AccessModifierOffset: -8
AllowAllConstructorInitializersOnNextLine: false
AlwaysBreakTemplateDeclarations: Yes
BraceWrapping:
AfterFunction: true
SplitEmptyFunction: true
BreakConstructorInitializers: BeforeColon
ColumnLimit: 90
ConstructorInitializerIndentWidth: 0
ContinuationIndentWidth: 8
IndentCaseLabels: false
IndentPPDirectives: AfterHash
IndentWidth: 8
Standard: c++17
TabWidth: 8
UseTab: Always
---
name: Bug report
about: Create a bug report
---
<!-- See https://www.musicpd.org/help/ -->
## Bug report
### Describe the bug
## Expected Behavior
## Actual Behavior
## Version
<!-- Paste the output of "mpd --version" here -->
## Configuration
<!-- Paste your MPD configuration here -->
## Log
<!-- Paste relevant portions of the log file here (--verbose) -->
---
name: Feature request
about: Create a feature request
---
## Feature request
---
on:
workflow_dispatch:
push:
paths-ignore:
- 'android/**'
- 'build/**'
- 'doc/**'
- 'python/**'
- 'subprojects/**'
- 'systemd/**'
- 'win32/**'
branches:
- master
- v0.23.x
pull_request:
paths-ignore:
- 'android/**'
- 'build/**'
- 'doc/**'
- 'python/**'
- 'subprojects/**'
- 'systemd/**'
- 'win32/**'
branches:
- master
- v0.23.x
jobs:
build-linux:
runs-on: ubuntu-latest
env:
CC: 'ccache gcc-10'
CXX: 'ccache g++-10'
steps:
- id: checkout
uses: actions/checkout@v2
- id: cache-ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: linux
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
g++-10 libfmt-dev libboost-dev \
libgtest-dev \
libpcre2-dev \
libsystemd-dev libdbus-1-dev \
libicu-dev \
libcurl4-gnutls-dev \
libpcre2-dev \
libavahi-client-dev \
libmad0-dev libmpg123-dev libid3tag0-dev \
libflac-dev libvorbis-dev libopus-dev libogg-dev \
libadplug-dev libaudiofile-dev libsndfile1-dev libfaad-dev \
libfluidsynth-dev libgme-dev libmikmod-dev libmodplug-dev \
libmpcdec-dev libwavpack-dev libwildmidi-dev \
libsidplay2-dev libsidutils-dev libresid-builder-dev \
libavcodec-dev libavformat-dev \
libmp3lame-dev libtwolame-dev libshine-dev \
libsamplerate0-dev libsoxr-dev \
libbz2-dev libcdio-paranoia-dev libiso9660-dev libmms-dev \
libzzip-dev \
libyajl-dev libexpat-dev \
libasound2-dev libao-dev libjack-jackd2-dev libopenal-dev \
libpulse-dev libshout3-dev \
libsndio-dev \
libmpdclient-dev \
libnfs-dev \
libupnp-dev \
libsqlite3-dev \
libchromaprint-dev \
libgcrypt20-dev
- name: Full Build
uses: BSFishy/meson-build@v1.0.3
with:
action: build
directory: output/full
setup-options: -Ddocumentation=disabled -Dtest=true -Dsystemd=enabled -Dpcre=enabled
options: --verbose
meson-version: 0.56.0
- name: Unit Tests
uses: BSFishy/meson-build@v1.0.3
with:
action: test
directory: output/full
setup-options: -Ddocumentation=disabled -Dtest=true -Dsystemd=enabled -Dpcre=enabled
options: --verbose
meson-version: 0.56.0
- name: Mini Build
uses: BSFishy/meson-build@v1.0.3
with:
action: build
directory: output/mini
setup-options: -Dbuildtype=minsize -Dauto_features=disabled -Dtest=true -Ddaemon=false -Dinotify=false -Depoll=false -Deventfd=false -Dsignalfd=false -Dtcp=false -Ddsd=false -Ddatabase=false -Dneighbor=false -Dcue=false -Dfifo=false -Dhttpd=false -Dpipe=false -Drecorder=false -Dsnapcast=false
options: --verbose
meson-version: 0.56.0
build-macos:
runs-on: macos-latest
steps:
- id: checkout
uses: actions/checkout@v2
- id: cache-ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: macos
- uses: actions/setup-python@v1
- name: Install dependencies
run: |
brew install \
meson ninja \
fmt \
boost \
googletest \
icu4c \
ffmpeg \
libnfs \
yajl \
libupnp \
libid3tag \
chromaprint \
libsamplerate \
libsoxr \
flac \
opus \
libvorbis \
faad2 \
wavpack \
libmpdclient
- name: Build
uses: BSFishy/meson-build@v1.0.3
with:
action: build
directory: output
setup-options: -Ddocumentation=disabled -Dtest=true
options: --verbose
meson-version: 0.56.0
- name: Unit Tests
uses: BSFishy/meson-build@v1.0.3
with:
action: test
directory: output
setup-options: -Ddocumentation=disabled -Dtest=true
options: --verbose
meson-version: 0.56.0
......@@ -2,96 +2,40 @@ language: cpp
jobs:
include:
# Ubuntu Bionic (18.04) with GCC 7
# Ubuntu Focal (20.04) with GCC 9.3
- os: linux
dist: bionic
dist: focal
addons:
apt:
sources:
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
packages:
- meson
- libgtest-dev
- libboost-dev
- python3.6
- python3-urllib3
- ninja-build
before_install:
- wget https://bootstrap.pypa.io/get-pip.py
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
install:
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
env:
- MATRIX_EVAL="export PATH=\$HOME/.local/bin:\$PATH"
- libfmt-dev
# Ubuntu Bionic (18.04) with GCC 7 on big-endian
# Ubuntu Focal (20.04) with GCC 9.3 on big-endian
- os: linux
arch: s390x
dist: bionic
dist: focal
addons:
apt:
sources:
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
packages:
- meson
- libgtest-dev
- libboost-dev
- python3.6
- python3-urllib3
- ninja-build
before_install:
- wget https://bootstrap.pypa.io/get-pip.py
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
install:
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
env:
- MATRIX_EVAL="export PATH=\$HOME/.local/bin:\$PATH"
- libfmt-dev
# Ubuntu Bionic (18.04) with GCC 7 on ARM64
# Ubuntu Focal (20.04) with GCC 9.3 on ARM64
- os: linux
arch: arm64
dist: bionic
dist: focal
addons:
apt:
sources:
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
packages:
- meson
- libgtest-dev
- libboost-dev
- python3.6
- python3-urllib3
- ninja-build
before_install:
- wget https://bootstrap.pypa.io/get-pip.py
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
install:
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
env:
- MATRIX_EVAL="export PATH=\$HOME/.local/bin:\$PATH"
# Ubuntu Trusty (16.04) with GCC 6
- os: linux
dist: trusty
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
- sourceline: 'ppa:mstipicevic/ninja-build-1-7-2'
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
packages:
- g++-6
- libgtest-dev
- boost1.67
- python3.6
- python3-urllib3
- ninja-build
before_install:
- wget https://bootstrap.pypa.io/get-pip.py
- /usr/bin/python3.6 get-pip.py --user --no-cache-dir
install:
- /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson --no-cache-dir
env:
# use gold as workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17068
- MATRIX_EVAL="export CC='ccache gcc-6' CXX='ccache g++-6' LDFLAGS=-fuse-ld=gold PATH=\$HOME/.local/bin:\$PATH"
- libfmt-dev
# Ubuntu Trusty (16.04) with GCC 8
- os: linux
......@@ -101,7 +45,7 @@ jobs:
sources:
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mhier/libboost-latest'
- sourceline: 'ppa:mstipicevic/ninja-build-1-7-2'
- sourceline: 'ppa:ricotz/toolchain'
- sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson)
packages:
- g++-8
......@@ -120,12 +64,14 @@ jobs:
- MATRIX_EVAL="export CC='ccache gcc-8' CXX='ccache g++-8' LDFLAGS=-fuse-ld=gold PATH=\$HOME/.local/bin:\$PATH"
- os: osx
osx_image: xcode9.4
osx_image: xcode11.6
addons:
homebrew:
packages:
- ccache
- meson
- fmt
- googletest
- icu4c
- ffmpeg
- libnfs
......@@ -143,7 +89,6 @@ jobs:
- faad2
- wavpack
- libmpdclient
update: true
env:
- MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1"
......@@ -160,13 +105,6 @@ before_install:
- eval "${MATRIX_EVAL}"
install:
# C++14
# Work around "Target /usr/local/lib/libgtest.a is a symlink
# belonging to nss. You can unlink it" during gtest install
- test "$TRAVIS_OS_NAME" != "osx" || brew unlink nss
- test "$TRAVIS_OS_NAME" != "osx" || brew install https://gist.githubusercontent.com/Kronuz/96ac10fbd8472eb1e7566d740c4034f8/raw/gtest.rb
before_script:
- ccache -s
......
Music Player Daemon - http://www.musicpd.org
Copyright 2003-2018 The Music Player Daemon Project
Copyright 2003-2021 The Music Player Daemon Project
The following people have contributed code to MPD:
......
......@@ -14,7 +14,7 @@ For basic installation instructions
- [Manual](http://www.musicpd.org/doc/user/)
- [Forum](http://forum.musicpd.org/)
- [IRC](irc://chat.freenode.net/#mpd)
- [IRC](ircs://irc.libera.chat:6697/#mpd)
- [Bug tracker](https://github.com/MusicPlayerDaemon/MPD/issues/)
# Developers
......
......@@ -2,10 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="47"
android:versionName="0.21.24">
android:versionCode="66"
android:versionName="0.23.7">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
<uses-feature android:name="android.software.leanback"
android:required="false" />
......@@ -19,6 +19,8 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application android:allowBackup="true"
android:debuggable="true"
android:requestLegacyExternalStorage="true"
android:icon="@drawable/icon"
android:banner="@drawable/icon"
android:label="@string/app_name">
......
......@@ -13,7 +13,7 @@ android_abi = sys.argv[3]
configure_args = sys.argv[4:]
if not os.path.isfile(os.path.join(sdk_path, 'tools', 'android')):
print("SDK not found in", ndk_path, file=sys.stderr)
print("SDK not found in", sdk_path, file=sys.stderr)
sys.exit(1)
if not os.path.isdir(ndk_path):
......@@ -24,15 +24,13 @@ android_abis = {
'armeabi-v7a': {
'arch': 'arm-linux-androideabi',
'ndk_arch': 'arm',
'toolchain_arch': 'arm-linux-androideabi',
'llvm_triple': 'armv7-linux-androideabi',
'cflags': '-fpic -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp',
'cflags': '-fpic -mfpu=neon -mfloat-abi=softfp',
},
'arm64-v8a': {
'arch': 'aarch64-linux-android',
'ndk_arch': 'arm64',
'toolchain_arch': 'aarch64-linux-android',
'llvm_triple': 'aarch64-linux-android',
'cflags': '-fpic',
},
......@@ -40,7 +38,6 @@ android_abis = {
'x86': {
'arch': 'i686-linux-android',
'ndk_arch': 'x86',
'toolchain_arch': 'x86',
'llvm_triple': 'i686-linux-android',
'cflags': '-fPIC -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
},
......@@ -48,7 +45,6 @@ android_abis = {
'x86_64': {
'arch': 'x86_64-linux-android',
'ndk_arch': 'x86_64',
'toolchain_arch': 'x86_64',
'llvm_triple': 'x86_64-linux-android',
'cflags': '-fPIC -m64',
},
......@@ -84,36 +80,29 @@ class AndroidNdkToolchain:
ndk_arch = abi_info['ndk_arch']
android_api_level = '21'
# select the NDK compiler
gcc_version = '4.9'
install_prefix = os.path.join(arch_path, 'root')
self.arch = arch
self.actual_arch = arch
self.install_prefix = install_prefix
toolchain_path = os.path.join(ndk_path, 'toolchains', abi_info['toolchain_arch'] + '-' + gcc_version, 'prebuilt', build_arch)
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
llvm_triple = abi_info['llvm_triple'] + android_api_level
common_flags = '-Os -g'
common_flags += ' ' + abi_info['cflags']
toolchain_bin = os.path.join(toolchain_path, 'bin')
llvm_bin = os.path.join(llvm_path, 'bin')
self.cc = os.path.join(llvm_bin, 'clang')
self.cxx = os.path.join(llvm_bin, 'clang++')
common_flags += ' -target ' + llvm_triple + ' -integrated-as -gcc-toolchain ' + toolchain_path
common_flags += ' -target ' + llvm_triple
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
# required flags from https://android.googlesource.com/platform/ndk/+/ndk-release-r20/docs/BuildSystemMaintainers.md#additional-required-arguments
common_flags += ' -fno-addrsig'
self.ar = os.path.join(toolchain_bin, arch + '-ar')
self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib')
self.nm = os.path.join(toolchain_bin, arch + '-nm')
self.strip = os.path.join(toolchain_bin, arch + '-strip')
self.ar = os.path.join(llvm_bin, 'llvm-ar')
self.ranlib = os.path.join(llvm_bin, 'llvm-ranlib')
self.nm = os.path.join(llvm_bin, 'llvm-nm')
self.strip = os.path.join(llvm_bin, 'llvm-strip')
self.cflags = common_flags
self.cxxflags = common_flags
......@@ -151,10 +140,7 @@ class AndroidNdkToolchain:
# default one on the build host
import shutil
bin_dir = os.path.join(install_prefix, 'bin')
try:
os.makedirs(bin_dir)
except:
pass
os.makedirs(bin_dir, exist_ok=True)
self.pkg_config = shutil.copy(os.path.join(mpd_path, 'build', 'pkg-config.sh'),
os.path.join(bin_dir, 'pkg-config'))
self.env['PKG_CONFIG'] = self.pkg_config
......@@ -164,7 +150,6 @@ from build.libs import *
thirdparty_libs = [
libmpdclient,
libogg,
libvorbis,
opus,
flac,
libid3tag,
......@@ -172,8 +157,8 @@ thirdparty_libs = [
wildmidi,
gme,
ffmpeg,
openssl,
curl,
libexpat,
libnfs,
boost,
]
......
#!/bin/sh
# This script need the following modification in ANDROID_NDK in order to attach
# to the good :main pid
#--- a/prebuilt/linux-x86_64/bin/ndk-gdb.py
#+++ b/prebuilt/linux-x86_64/bin/ndk-gdb.py
#@@ -669,7 +669,7 @@
# log("Sleeping for {} seconds.".format(args.delay))
# time.sleep(args.delay)
#
#- pids = gdbrunner.get_pids(device, pkg_name)
#+ pids = gdbrunner.get_pids(device, pkg_name + ":main")
# if len(pids) == 0:
# error("Failed to find running process '{}'".format(pkg_name))
# if len(pids) > 1:
SCRIPT_PATH=$(dirname $0)
BUILD_PATH="`pwd`"
TMP_PATH="$BUILD_PATH/gdb"
NDK_GDB_ARGS="--force"
ANDROID_NDK=$1
if [ ! -f $ANDROID_NDK/source.properties ];then
echo "usage: $0 ANDROID_NDK"
exit 1
fi
if [ ! -f $BUILD_PATH/libmpd.so ];then
echo "This script need to be executed from the android build directory"
exit 1
fi
rm -rf "$TMP_PATH"
mkdir -p "$TMP_PATH"
ANDROID_MANIFEST="$SCRIPT_PATH/AndroidManifest.xml"
ABI=`ls "$BUILD_PATH/android/apk/apk/lib" --sort=time | head -n 1`
if [ ! -f "$ANDROID_MANIFEST" -o "$ABI" = "" ]; then
echo "Invalid manifest/ABI, did you try building first ?"
exit 1
fi
mkdir -p "$TMP_PATH"/jni
touch "$TMP_PATH"/jni/Android.mk
echo "APP_ABI := $ABI" > "$TMP_PATH"/jni/Application.mk
DEST=obj/local/$ABI
mkdir -p "$TMP_PATH/$DEST"
cp "$BUILD_PATH/libmpd.so" "$TMP_PATH/$DEST"
cp "$ANDROID_MANIFEST" "$TMP_PATH"
(cd "$TMP_PATH" && bash $ANDROID_NDK/ndk-gdb $NDK_GDB_ARGS)
......@@ -5,8 +5,8 @@ android_ndk = get_option('android_ndk')
android_sdk = get_option('android_sdk')
android_abi = get_option('android_abi')
android_sdk_build_tools_version = '27.0.0'
android_sdk_platform = 'android-23'
android_sdk_build_tools_version = '29.0.3'
android_sdk_platform = 'android-29'
android_build_tools_dir = join_paths(android_sdk, 'build-tools', android_sdk_build_tools_version)
android_sdk_platform_dir = join_paths(android_sdk, 'platforms', android_sdk_platform)
......
......@@ -23,6 +23,12 @@
android:layout_height="wrap_content"
android:text="@string/checkbox_wakelock" />
<CheckBox
android:id="@+id/pause_on_headphones_disconnect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/checkbox_pause_on_headphones_disconnect" />
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
......
......@@ -8,4 +8,5 @@
<string name="toggle_button_run_off">MPD is not running</string>
<string name="checkbox_run_on_boot">Run MPD automatically on boot</string>
<string name="checkbox_wakelock">Prevent suspend when MPD is running (Wakelock)</string>
<string name="checkbox_pause_on_headphones_disconnect">Pause MPD when headphones disconnect</string>
</resources>
......@@ -13,7 +13,7 @@ GENCLASS="$D/classes"
GENINCLUDE="$D/include"
mkdir -p "$GENSRC/$JAVA_PKG_PATH"
"$JAVAC" -source 1.6 -target 1.6 -Xlint:-options \
"$JAVAC" -source 1.7 -target 1.7 -Xlint:-options \
-cp "$CLASSPATH" \
-h "$GENINCLUDE" \
-d "$GENCLASS" \
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -33,4 +33,5 @@ public class Bridge {
public static native void run(Context context, LogListener logListener);
public static native void shutdown();
public static native void pause();
}
......@@ -5,6 +5,7 @@ interface IMain
{
void start();
void stop();
void setPauseOnHeadphonesDisconnect(boolean enabled);
void setWakelockEnabled(boolean enabled);
boolean isRunning();
void registerCallback(IMainCallback cb);
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -24,10 +24,13 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.media.AudioManager;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
......@@ -55,6 +58,7 @@ public class Main extends Service implements Runnable {
private String mError = null;
private final RemoteCallbackList<IMainCallback> mCallbacks = new RemoteCallbackList<IMainCallback>();
private final IBinder mBinder = new MainStub(this);
private boolean mPauseOnHeadphonesDisconnect = false;
private PowerManager.WakeLock mWakelock = null;
static class MainStub extends IMain.Stub {
......@@ -68,6 +72,9 @@ public class Main extends Service implements Runnable {
public void stop() {
mService.stop();
}
public void setPauseOnHeadphonesDisconnect(boolean enabled) {
mService.setPauseOnHeadphonesDisconnect(enabled);
}
public void setWakelockEnabled(boolean enabled) {
mService.setWakelockEnabled(enabled);
}
......@@ -191,6 +198,18 @@ public class Main extends Service implements Runnable {
if (mThread != null)
return;
IntentFilter filter = new IntentFilter();
filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!mPauseOnHeadphonesDisconnect)
return;
if (intent.getAction() == AudioManager.ACTION_AUDIO_BECOMING_NOISY)
pause();
}
}, filter);
final Intent mainIntent = new Intent(this, Settings.class);
mainIntent.setAction("android.intent.action.MAIN");
mainIntent.addCategory("android.intent.category.LAUNCHER");
......@@ -241,6 +260,21 @@ public class Main extends Service implements Runnable {
stopSelf();
}
private void pause() {
if (mThread != null) {
if (mThread.isAlive()) {
synchronized (this) {
if (mStatus == MAIN_STATUS_STARTED)
Bridge.pause();
}
}
}
}
private void setPauseOnHeadphonesDisconnect(boolean enabled) {
mPauseOnHeadphonesDisconnect = enabled;
}
private void setWakelockEnabled(boolean enabled) {
if (enabled && mWakelock == null) {
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
......@@ -368,6 +402,19 @@ public class Main extends Service implements Runnable {
}
}
public boolean setPauseOnHeadphonesDisconnect(boolean enabled) {
synchronized (this) {
if (mIMain != null) {
try {
mIMain.setPauseOnHeadphonesDisconnect(enabled);
return true;
} catch (RemoteException e) {
}
}
return false;
}
}
public boolean setWakelockEnabled(boolean enabled) {
synchronized (this) {
if (mIMain != null) {
......@@ -414,6 +461,15 @@ public class Main extends Service implements Runnable {
* start Main service without any callback
*/
public static void start(Context context, boolean wakelock) {
context.startService(new Intent(context, Main.class).putExtra("wakelock", wakelock));
Intent intent = new Intent(context, Main.class)
.putExtra("wakelock", wakelock);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
/* in Android 8+, we need to use this method
or else we'll get "IllegalStateException:
app is in background" */
context.startForegroundService(intent);
else
context.startService(intent);
}
}
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* Copyright (C) 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -26,16 +25,18 @@ import android.content.Intent;
import android.util.Log;
public class Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("Receiver", "onReceive: " + intent);
if (intent.getAction() == "android.intent.action.BOOT_COMPLETED") {
if (Settings.Preferences.getBoolean(context,
Settings.Preferences.KEY_RUN_ON_BOOT, false)) {
final boolean wakelock = Settings.Preferences.getBoolean(context,
Settings.Preferences.KEY_WAKELOCK, false);
Main.start(context, wakelock);
}
}
}
@Override
public void onReceive(Context context, Intent intent) {
Log.d("Receiver", "onReceive: " + intent);
if (intent.getAction() == "android.intent.action.BOOT_COMPLETED") {
if (Settings.Preferences.getBoolean(context,
Settings.Preferences.KEY_RUN_ON_BOOT,
false)) {
final boolean wakelock =
Settings.Preferences.getBoolean(context,
Settings.Preferences.KEY_WAKELOCK, false);
Main.start(context, wakelock);
}
}
}
}
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -59,6 +59,7 @@ public class Settings extends Activity {
public static class Preferences {
public static final String KEY_RUN_ON_BOOT ="run_on_boot";
public static final String KEY_WAKELOCK ="wakelock";
public static final String KEY_PAUSE_ON_HEADPHONES_DISCONNECT ="pause_on_headphones_disconnect";
public static SharedPreferences get(Context context) {
return context.getSharedPreferences(TAG, MODE_PRIVATE);
......@@ -154,6 +155,9 @@ public class Settings extends Activity {
if (Preferences.getBoolean(Settings.this,
Preferences.KEY_WAKELOCK, false))
mClient.setWakelockEnabled(true);
if (Preferences.getBoolean(Settings.this,
Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, false))
mClient.setPauseOnHeadphonesDisconnect(true);
} else {
mClient.stop();
}
......@@ -179,6 +183,15 @@ public class Settings extends Activity {
}
};
private final OnCheckedChangeListener mOnPauseOnHeadphonesDisconnectChangeListener = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Preferences.putBoolean(Settings.this, Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, isChecked);
if (mClient != null && mClient.isRunning())
mClient.setPauseOnHeadphonesDisconnect(isChecked);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
/* TODO: this sure is the wrong place to request
......@@ -211,6 +224,11 @@ public class Settings extends Activity {
if (Preferences.getBoolean(this, Preferences.KEY_WAKELOCK, false))
checkbox.setChecked(true);
checkbox = (CheckBox) findViewById(R.id.pause_on_headphones_disconnect);
checkbox.setOnCheckedChangeListener(mOnPauseOnHeadphonesDisconnectChangeListener);
if (Preferences.getBoolean(this, Preferences.KEY_PAUSE_ON_HEADPHONES_DISCONNECT, false))
checkbox.setChecked(true);
super.onCreate(savedInstanceState);
}
......
Client Developer's Manual
#########################
Introduction
************
MPD is a music player without a user interface. The user interface
will be provided by independent clients, which connect to MPD over
socket connections (TCP or local sockets).
This chapter describes how to develop a client.
Before you develop a new client, consider joining an existing client
project. There are many clients, but few are mature; we need fewer,
but better clients.
Client Libraries
****************
There are many libraries which help with connecting to MPD. If you
develop a MPD client, use a library instead of reinventing the wheel.
The MPD website has a list of libraries: https://www.musicpd.org/libs/
Connecting to MPD
*****************
Do not hard-code your client to connect to ``localhost:6600``.
Instead, use the defaults of the client library. For example, with
:program:`libmpdclient`, don't do::
c = mpd_connection_new("localhost", 6600, 30000);
Instead, do::
c = mpd_connection_new(NULL, 0, 0);
This way, the library can choose the best defaults, maybe derived from
environment variables, so all MPD clients use the same settings.
If you need to reimplement those defaults (or if you are developing a
client library), this is a good set of addresses to attempt to connect
to:
- if the environment variable :envvar:`MPD_HOST` is set:
``$MPD_HOST:$MPD_PORT`` (:envvar:`MPD_PORT` defaulting to 6600)
- if the environment variable :envvar:`XDG_RUNTIME_DIR` is set:
``$XDG_RUNTIME_DIR/mpd/socket``
- :file:`/run/mpd/socket`
- ``localhost:$MPD_PORT`` (:envvar:`MPD_PORT` defaulting to 6600)
Environment Variables
*********************
The following environment variables should be obeyed by all clients
(preferably by the client library):
- :envvar:`MPD_HOST`: the host (or local socket path) to connect to;
on Linux, this may start with a ``@`` to connect to an abstract
socket. To use a password with MPD, set :envvar:`MPD_HOST` to
``password@host`` (then abstract socket requires double ``@``:
``password@@socket``).
- :envvar:`MPD_PORT`: the port number; defaults to 6600.
- :envvar:`MPD_TIMEOUT`: timeout for connecting to MPD and for waiting
for MPD's response in seconds. A good default is 30 seconds.
......@@ -30,7 +30,7 @@ master_doc = 'index'
# General information about the project.
project = 'Music Player Daemon'
copyright = '2003-2018 The Music Player Daemon Project'
copyright = '2003-2021 The Music Player Daemon Project'
author = 'Max Kellermann'
# The version info for the project you're documenting, acts as replacement for
......@@ -38,9 +38,9 @@ author = 'Max Kellermann'
# built documents.
#
# The short X.Y version.
version = '0.21.24'
version = '0.23.8'
# The full version, including alpha/beta/rc tags.
release = version
#release = version + '~git'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......@@ -107,6 +107,7 @@ html_theme = 'classic'
# documentation.
#
# html_theme_options = {}
html_theme_options = {"sidebarwidth": "300px"}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []
......@@ -212,3 +213,7 @@ html_static_path = ['_static']
# implements a search results scorer. If empty, the default will be used.
#
# html_search_scorer = 'scorer.js'
man_pages = [
('mpd.1', 'mpd', 'MPD documentation', [author], 1),
('mpd.conf.5', 'mpd.conf', 'mpd.conf documentation', [author], 5)
]
......@@ -12,8 +12,7 @@ Code Style
* indent with tabs (width 8)
* don't write CPP when you can write C++: use inline functions and constexpr instead of macros
* comment your code, document your APIs
* the code should be C++14 compliant, and must compile with :program:`GCC` 6.0 and :program:`clang` 3.4
* report error conditions with C++ exceptions, preferable derived from :envvar:`std::runtime_error`
* the code should be C++17 compliant, and must compile with :program:`GCC` 8 and :program:`clang` 5
* all code must be exception-safe
* classes and functions names use CamelCase; variables are lower-case with words separated by underscore
......@@ -31,6 +30,56 @@ Some example code:
return xyz;
}
Error handling
==============
If an error occurs, throw a C++ exception, preferably derived from
:code:`std::runtime_error`. The function's API documentation should
mention that. If a function cannot throw exceptions, add
:code:`noexcept` to its prototype.
Some parts of MPD use callbacks to report completion; the handler
classes usually have an "error" callback which receives a
:code:`std::exception_ptr`
(e.g. :code:`BufferedSocket::OnSocketError()`). Wrapping errors in
:code:`std::exception_ptr` allows propagating details about the error
across thread boundaries to the entity which is interested in handling
it (e.g. giving the MPD client details about an I/O error caught by
the decoder thread).
Out-of-memory errors (i.e. :code:`std::bad_alloc`) do not need to be
handled. Some operating systems such as Linux do not report
out-of-memory to userspace, and instead kill a process to recover.
Even if we know we are out of memory, there is little we can do except
for aborting the process quickly. Any other attempts to give back
memory may cause page faults on the way which make the situation
worse.
Error conditions which are caused by a bug do not need to be handled
at runtime; instead, use :code:`assert()` to detect them in debug
builds.
git Branches
************
There are two active branches in the git repository:
- the "unstable" branch called ``master`` where new features are
merged. This will become the next major release eventually.
- the "stable" branch (currently called ``v0.22.x``) where only bug
fixes are merged.
Once :program:`MPD` 0.23 is released, a new branch called ``v0.23.x``
will be created for 0.23 bug-fix releases; after that, ``v0.22.x``
will eventually cease to be maintained.
After bug fixes have been added to the "stable" branch, it will be
merged into ``master``. This ensures that all known bugs are fixed in
all active branches.
Hacking The Source
******************
......
......@@ -8,6 +8,7 @@ Music Player Daemon
user
plugins
developer
client
protocol
......
install_man(['mpd.1', 'mpd.conf.5'])
if not get_option('html_manual') and not get_option('manpages')
subdir_done()
endif
sphinx = find_program('sphinx-build')
sphinx_output = custom_target(
'HTML documentation',
output: 'html',
input: [
'index.rst', 'user.rst', 'developer.rst',
'plugins.rst',
'protocol.rst',
'conf.py',
],
command: [sphinx, '-q', '-b', 'html', '-d', '@OUTDIR@/doctrees', meson.current_source_dir(), '@OUTPUT@'],
build_by_default: true,
install: true,
install_dir: join_paths(get_option('datadir'), 'doc', meson.project_name()),
)
sphinx = find_program('sphinx-build', required: get_option('documentation'))
if not sphinx.found()
subdir_done()
endif
custom_target(
'upload',
input: sphinx_output,
output: 'upload',
build_always_stale: true,
command: [
'rsync', '-vpruz', '--delete', meson.current_build_dir() + '/',
'www.musicpd.org:/var/www/mpd/doc/',
'--chmod=Dug+rwx,Do+rx,Fug+rw,Fo+r',
'--include=html', '--include=html/**',
'--exclude=*',
],
)
if get_option('html_manual')
sphinx_output = custom_target(
'HTML documentation',
output: 'html',
input: [
'index.rst', 'user.rst', 'developer.rst',
'plugins.rst',
'client.rst',
'protocol.rst',
'conf.py',
],
command: [sphinx, '-q', '-b', 'html', '-d', '@OUTDIR@/doctrees', meson.current_source_dir(), '@OUTPUT@'],
build_by_default: true,
install: true,
install_dir: join_paths(get_option('datadir'), 'doc', meson.project_name()),
)
endif
if get_option('manpages')
custom_target(
'Manpage documentation',
output: ['mpd.1', 'mpd.conf.5'],
input: ['mpd.1.rst', 'conf.py'],
command: [sphinx, '-q', '-b', 'man', '-d', '@OUTDIR@/man_doctrees', meson.current_source_dir(), '@OUTDIR@'],
build_by_default: true,
install: true,
install_dir: [join_paths(get_option('mandir'), 'man1'), join_paths(get_option('mandir'), 'man5')],
)
endif
.TH "Music Player Daemon" 1
.SH NAME
MPD \- A daemon for playing music
.SH SYNOPSIS
.B mpd
.RI [ options ]
.RI [ CONF_FILE ]
.SH DESCRIPTION
MPD is a daemon for playing music. Music is played through the configured
audio output(s) (which are generally local, but can be remote). The daemon
stores info about all available music, and this info can be easily searched and
retrieved. Player control, info retrieval, and playlist management can all be
managed remotely.
MPD searches for a config file in \fB$XDG_CONFIG_HOME/mpd/mpd.conf\fP then
\fB~/.mpdconf\fP then \fB/etc/mpd.conf\fP or uses CONF_FILE.
Read more about MPD at <\fBhttp://www.musicpd.org/\fP>.
.SH OPTIONS
.TP
.BI \-\-help
Output a brief help message.
.TP
.BI \-\-kill
Kill the currently running mpd session. The pid_file parameter must be
specified in the config file for this to work.
.TP
.BI \-\-no\-daemon
Don't detach from console.
.TP
.BI \-\-stderr
Print messages stderr.
.TP
.BI \-\-verbose
Verbose logging.
.TP
.BI \-\-version
Print version information.
.SH FILES
.TP
.BI ~/.mpdconf
User configuration file.
.TP
.BI /etc/mpd.conf
Global configuration file.
.SH SEE ALSO
mpd.conf(5), mpc(1)
.SH BUGS
If you find a bug, please report it at
.br
<\fBhttps://github.com/MusicPlayerDaemon/MPD/issues/\fP>.
.SH AUTHORS
Max Kellermann <max.kellermann@gmail.com>
Special thanks to all the people that provided feedback and patches.
===
mpd
===
SYNOPSIS
--------
``mpd`` [options] [CONF_FILE]
DESCRIPTION
------------
MPD is a daemon for playing music. Music is played through the configured audio output(s) (which are generally local, but can be remote). The daemon stores info about all available music, and this info can be easily searched and retrieved. Player control, info retrieval, and playlist management can all be managed remotely.
MPD searches for a config file in ``$XDG_CONFIG_HOME/mpd/mpd.conf``
then ``~/.mpdconf`` then ``~/.mpd/mpd.conf`` then ``/etc/mpd.conf`` or uses ``CONF_FILE``.
Read more about MPD at http://www.musicpd.org/
OPTIONS
-------
.. program:: mpd
.. option:: --help
Output a brief help message.
.. option:: --kill
Kill the currently running mpd session. The pid_file parameter must be specified in the config file for this to work.
.. option:: --no-config
Don't read from the configuration file.
.. option:: --no-daemon
Don't detach from console.
.. option:: --stderr
Print messages to stderr.
.. option:: --verbose
Verbose logging.
.. option:: --version
Print version information.
FILES
-----
:file:`$XDG_CONFIG_HOME/mpd/mpd.conf`
User configuration file (usually :file:`~/.config/mpd/mpd.conf`).
:file:`/etc/mpd.conf`
Global configuration file.
SEE ALSO
--------
:manpage:`mpd.conf(5)`, :manpage:`mpc(1)`
BUGS
----
If you find a bug, please report it at https://github.com/MusicPlayerDaemon/MPD/issues/
.TH mpd.conf 5
.SH NAME
mpd.conf \- Music Player Daemon configuration file
.SH DESCRIPTION
\fBmpd.conf\fP is the configuration file for mpd(1). If not specified on the
command line, MPD first searches for it at \fB$XDG_CONFIG_HOME/mpd/mpd.conf\fP
then at \fB~/.mpdconf\fP then at \fB~/.mpd/mpd.conf\fP and then in
\fB/etc/mpd.conf\fP.
Lines beginning with a "#" character are comments. All other non-empty lines
specify parameters and their values. These lines contain the parameter name
and parameter value (surrounded by double quotes) separated by whitespace
(either tabs or spaces). For example:
parameter "value"
The exception to this rule is the audio_output parameter, which is of the form:
audio_output {
.br
parameter1 "value"
parameter2 "value"
.br
}
Parameters that take a file or directory as an argument should use absolute
paths.
See \fBdocs/mpdconf.example\fP in the source tarball for an example
configuration file.
This manual is not complete, it lists only the most important options.
Please read the MPD user manual for a complete configuration guide:
<\fBhttp://www.musicpd.org/doc/user/\fP>
.SH REQUIRED PARAMETERS
.TP
.B db_file <file>
This specifies where the db file will be stored.
.TP
.B log_file <file>
This specifies where the log file should be located.
The special value "syslog" makes MPD use the local syslog daemon.
.SH OPTIONAL PARAMETERS
.TP
.B sticker_file <file>
The location of the sticker database. This is a database which
manages dynamic information attached to songs.
.TP
.B pid_file <file>
This specifies the file to save mpd's process ID in.
.TP
.B music_directory <directory>
This specifies the directory where music is located.
If you do not configure this, you can only play streams.
.TP
.B playlist_directory <directory>
This specifies the directory where saved playlists are stored.
If you do not configure this, you cannot save playlists.
.TP
.B state_file <file>
This specifies if a state file is used and where it is located. The state of
mpd will be saved to this file when mpd is terminated by a TERM signal or by
the "kill" command. When mpd is restarted, it will read the state file and
restore the state of mpd (including the playlist).
.TP
.B restore_paused <yes or no>
Put MPD into pause mode instead of starting playback after startup.
.TP
.B user <username>
This specifies the user that MPD will run as, if set. MPD should
never run as root, and you may use this option to make MPD change its
user id after initialization. Do not use this option if you start MPD
as an unprivileged user.
.TP
.B port <port>
This specifies the port that mpd listens on. The default is 6600.
.TP
.B log_level <default, secure, or verbose>
This specifies how verbose logs are. "default" is minimal logging, "secure"
reports from what address a connection is opened, and when it is closed, and
"verbose" records excessive amounts of information for debugging purposes. The
default is "default".
.TP
.B follow_outside_symlinks <yes or no>
Control if MPD will follow symbolic links pointing outside the music dir.
You must recreate the database after changing this option.
The default is "yes".
.TP
.B follow_inside_symlinks <yes or no>
Control if MPD will follow symbolic links pointing inside the music dir,
potentially adding duplicates to the database.
You must recreate the database after changing this option.
The default is "yes".
.TP
.B zeroconf_enabled <yes or no>
If yes, and MPD has been compiled with support for Avahi or Bonjour, service
information will be published with Zeroconf. The default is yes.
.TP
.B zeroconf_name <name>
If Zeroconf is enabled, this is the service name to publish. This name should
be unique to your local network, but name collisions will be properly dealt
with. The default is "Music Player @ %h", where %h will be replaced with the
hostname of the machine running MPD.
.TP
.B audio_output
See \fBDESCRIPTION\fP and the various \fBAUDIO OUTPUT PARAMETERS\fP sections
for the format of this parameter. Multiple audio_output sections may be
specified. If no audio_output section is specified, then MPD will scan for a
usable audio output.
.TP
.B replaygain <off or album or track or auto>
If specified, mpd will adjust the volume of songs played using ReplayGain tags
(see <\fBhttp://www.replaygain.org/\fP>). Setting this to "album" will adjust
volume using the album's ReplayGain tags, while setting it to "track" will
adjust it using the track ReplayGain tags. "auto" uses the track ReplayGain
tags if random play is activated otherwise the album ReplayGain tags. Currently
only FLAC, Ogg Vorbis, Musepack, and MP3 (through ID3v2 ReplayGain tags, not
APEv2) are supported.
.TP
.B replaygain_preamp <\-15 to 15>
This is the gain (in dB) applied to songs with ReplayGain tags.
.TP
.B volume_normalization <yes or no>
If yes, mpd will normalize the volume of songs as they play. The default is no.
.TP
.B filesystem_charset <charset>
This specifies the character set used for the filesystem. A list of supported
character sets can be obtained by running "iconv \-l". The default is
determined from the locale when the db was originally created.
.TP
.B save_absolute_paths_in_playlists <yes or no>
This specifies whether relative or absolute paths for song filenames are used
when saving playlists. The default is "no".
.TP
.B auto_update <yes or no>
This specifies the whether to support automatic update of music database when
files are changed in music_directory. The default is to disable autoupdate
of database.
.TP
.B auto_update_depth <N>
Limit the depth of the directories being watched, 0 means only watch
the music directory itself. There is no limit by default.
.SH REQUIRED AUDIO OUTPUT PARAMETERS
.TP
.B type <type>
This specifies the audio output type. See the list of supported outputs in mpd
\-\-version for possible values.
.TP
.B name <name>
This specifies a unique name for the audio output.
.SH OPTIONAL AUDIO OUTPUT PARAMETERS
.TP
.B format <sample_rate:bits:channels>
This specifies the sample rate, bits per sample, and number of channels of
audio that is sent to the audio output device. See documentation for the
\fBaudio_output_format\fP parameter for more details. The default is to use
whatever audio format is passed to the audio output.
Any of the three attributes may be an asterisk to specify that this
attribute should not be enforced
.TP
.B replay_gain_handler <software, mixer or none>
Specifies how replay gain is applied. The default is "software",
which uses an internal software volume control. "mixer" uses the
configured (hardware) mixer control. "none" disables replay gain on
this audio output.
.TP
.B mixer_type <hardware, software or none>
Specifies which mixer should be used for this audio output: the
hardware mixer (available for ALSA, OSS and PulseAudio), the software
mixer or no mixer ("none"). By default, the hardware mixer is used
for devices which support it, and none for the others.
.SH FILES
.TP
.BI ~/.mpdconf
User configuration file.
.TP
.BI /etc/mpd.conf
Global configuration file.
.SH SEE ALSO
mpd(1), mpc(1)
========
mpd.conf
========
DESCRIPTION
------------
:file:`mpd.conf` is the configuration file for :manpage:`mpd(1)`. If
not specified on the command line, MPD first searches for it at
:file:`$XDG_CONFIG_HOME/mpd/mpd.conf` then at :file:`~/.mpdconf` then
at :file:`~/.mpd/mpd.conf` and then in :file:`/etc/mpd.conf`.
Each line in the configuration file contains a setting name and its value, e.g.:
:code:`connection_timeout "5"`
For settings which specify a filesystem path, the tilde is expanded:
:code:`music_directory "~/Music"`
Some of the settings are grouped in blocks with curly braces, e.g. per-plugin settings:
.. code-block:: none
audio_output {
type "alsa"
name "My ALSA output"
device "iec958:CARD=Intel,DEV=0"
mixer_control "PCM"
}
The :code:`include` directive can be used to include settings from
another file; the given file name is relative to the current file:
:code:`include "other.conf"`
You can use include_optional instead if you want the included file to be
optional; the directive will be ignored if the file does not exist:
:code:`include_optional "may_not_exist.conf"`
See :file:`docs/mpdconf.example` in the source tarball for an example
configuration file.
This manual is not complete, it lists only the most important options.
Please read the MPD user manual for a complete configuration guide:
http://www.musicpd.org/doc/user/
OPTIONAL PARAMETERS
-------------------
db_file <file>
This specifies where the db file will be stored.
log_file <file>
This specifies where the log file should be located. The special value "syslog" makes MPD use the local syslog daemon.
sticker_file <file>
The location of the sticker database. This is a database which manages
dynamic information attached to songs.
pid_file <file>
This specifies the file to save mpd's process ID in.
music_directory <directory>
This specifies the directory where music is located. If you do not configure
this, you can only play streams.
playlist_directory <directory>
This specifies the directory where saved playlists are stored. If
you do not configure this, you cannot save playlists.
state_file <file>
This specifies if a state file is used and where it is located. The state of
mpd will be saved to this file when mpd is terminated by a TERM signal or by
the :program:`kill` command. When mpd is restarted, it will read the state file and
restore the state of mpd (including the playlist).
restore_paused <yes or no>
Put MPD into pause mode instead of starting playback after startup.
user <username>
This specifies the user that MPD will run as, if set. MPD should never run
as root, and you may use this option to make MPD change its user id after
initialization. Do not use this option if you start MPD as an unprivileged
user.
port <port>
This specifies the port that mpd listens on. The default is 6600.
log_level <level>
Suppress all messages below the given threshold. The following
log levels are available:
- :samp:`error`: errors
- :samp:`warning`: warnings
- :samp:`notice`: interesting informational messages
- :samp:`info`: unimportant informational messages
- :samp:`verbose`: debug messages (for developers and for
troubleshooting)
The default is :samp:`notice`.
follow_outside_symlinks <yes or no>
Control if MPD will follow symbolic links pointing outside the music dir. You
must recreate the database after changing this option. The default is "yes".
follow_inside_symlinks <yes or no>
Control if MPD will follow symbolic links pointing inside the music dir,
potentially adding duplicates to the database. You must recreate the
database after changing this option. The default is "yes".
zeroconf_enabled <yes or no>
If yes, and MPD has been compiled with support for Avahi or Bonjour, service
information will be published with Zeroconf. The default is yes.
zeroconf_name <name>
If Zeroconf is enabled, this is the service name to publish. This name should
be unique to your local network, but name collisions will be properly dealt
with. The default is "Music Player @ %h", where %h will be replaced with the
hostname of the machine running MPD.
audio_output
See DESCRIPTION and the various ``AUDIO OUTPUT PARAMETERS`` sections for the
format of this parameter. Multiple audio_output sections may be specified. If
no audio_output section is specified, then MPD will scan for a usable audio
output.
filesystem_charset <charset>
This specifies the character set used for the filesystem. A list of supported
character sets can be obtained by running "iconv -l". The default is
determined from the locale when the db was originally created.
save_absolute_paths_in_playlists <yes or no>
This specifies whether relative or absolute paths for song filenames are used
when saving playlists. The default is "no".
auto_update <yes or no>
This specifies the whether to support automatic update of music database
when files are changed in music_directory. The default is to disable
autoupdate of database.
auto_update_depth <N>
Limit the depth of the directories being watched, 0 means only watch the
music directory itself. There is no limit by default.
REQUIRED AUDIO OUTPUT PARAMETERS
--------------------------------
type <type>
This specifies the audio output type. See the list of supported outputs in
``mpd --version`` for possible values.
name <name>
This specifies a unique name for the audio output.
OPTIONAL AUDIO OUTPUT PARAMETERS
--------------------------------
format <sample_rate:bits:channels>
This specifies the sample rate, bits per sample, and number of channels of
audio that is sent to the audio output device. See documentation for the
``audio_output_format`` parameter for more details. The default is to use
whatever audio format is passed to the audio output. Any of the three
attributes may be an asterisk to specify that this attribute should not be
enforced
replay_gain_handler <software, mixer or none>
Specifies how replay gain is applied. The default is "software", which uses
an internal software volume control. "mixer" uses the configured (hardware)
mixer control. "none" disables replay gain on this audio output.
mixer_type <hardware, software or none>
Specifies which mixer should be used for this audio output: the hardware
mixer (available for ALSA, OSS and PulseAudio), the software mixer or no
mixer ("none"). By default, the hardware mixer is used for devices which
support it, and none for the others.
FILES
-----
:file:`$XDG_CONFIG_HOME/mpd/mpd.conf`
User configuration file (usually :file:`~/.config/mpd/mpd.conf`).
:file:`/etc/mpd.conf`
Global configuration file.
SEE ALSO
--------
:manpage:`mpd(1)`, :manpage:`mpc(1)`
......@@ -5,7 +5,7 @@
# Files and directories #######################################################
#
# This setting controls the top directory which MPD will search to discover the
# available audio files and add them to the daemon's online database. This
# available audio files and add them to the daemon's online database. This
# setting defaults to the XDG directory, otherwise the music directory will be
# be disabled and audio files will only be accepted over ipc socket (using
# file:// protocol) or streaming files over an accepted protocol.
......@@ -13,38 +13,41 @@
#music_directory "~/music"
#
# This setting sets the MPD internal playlist directory. The purpose of this
# directory is storage for playlists created by MPD. The server will use
# directory is storage for playlists created by MPD. The server will use
# playlist files not created by the server but only if they are in the MPD
# format. This setting defaults to playlist saving being disabled.
#
#playlist_directory "~/.mpd/playlists"
#
# This setting sets the location of the MPD database. This file is used to
# load the database at server start up and store the database while the
# load the database at server start up and store the database while the
# server is not up. This setting defaults to disabled which will allow
# MPD to accept files over ipc socket (using file:// protocol) or streaming
# files over an accepted protocol.
#
#db_file "~/.mpd/database"
#
# These settings are the locations for the daemon log files for the daemon.
# These logs are great for troubleshooting, depending on your log_level
# settings.
#
# The special value "syslog" makes MPD use the local syslog daemon. This
# setting defaults to logging to syslog.
#
#log_file "~/.mpd/log"
# If you use systemd, do not configure a log_file. With systemd, MPD
# defaults to the systemd journal, which is fine.
#
#log_file "~/.mpd/log"
# This setting sets the location of the file which stores the process ID
# for use of mpd --kill and some init scripts. This setting is disabled by
# default and the pid file will not be stored.
#
#pid_file "~/.mpd/pid"
# If you use systemd, do not configure a pid_file.
#
#pid_file "~/.mpd/pid"
# This setting sets the location of the file which contains information about
# most variables to get MPD back into the same general shape it was in before
# it was brought down. This setting is disabled by default and the server
# it was brought down. This setting is disabled by default and the server
# state will be reset on server start up.
#
#state_file "~/.mpd/state"
......@@ -74,9 +77,9 @@
#group "nogroup"
#
# This setting sets the address for the daemon to listen on. Careful attention
# should be paid if this is assigned to anything other then the default, any.
# should be paid if this is assigned to anything other than the default, any.
# This setting can deny access to control of the daemon. Not effective if
# systemd socket activiation is in use.
# systemd socket activation is in use.
#
# For network
#bind_to_address "any"
......@@ -89,12 +92,11 @@
#
#port "6600"
#
# This setting controls the type of information which is logged. Available
# setting arguments are "default", "secure" or "verbose". The "verbose" setting
# argument is recommended for troubleshooting, though can quickly stretch
# available resources on limited hardware storage.
# Suppress all messages below the given threshold. Use "verbose" for
# troubleshooting. Available setting arguments are "notice", "info", "verbose",
# "warning" and "error".
#
#log_level "default"
#log_level "notice"
#
# Setting "restore_paused" to "yes" puts MPD into pause mode instead
# of starting playback after startup.
......@@ -115,7 +117,7 @@
# the other supported tags:
#metadata_to_use "+comment"
#
# This setting enables automatic update of MPD's database when files in
# This setting enables automatic update of MPD's database when files in
# music_directory are changed.
#
#auto_update "yes"
......@@ -130,7 +132,7 @@
# Symbolic link behavior ######################################################
#
# If this setting is set to "yes", MPD will discover audio files by following
# If this setting is set to "yes", MPD will discover audio files by following
# symbolic links outside of the configured music_directory.
#
#follow_outside_symlinks "yes"
......@@ -165,7 +167,7 @@
#
#password "password@read,add,control,admin"
#
# This setting specifies the permissions a user has who has not yet logged in.
# This setting specifies the permissions a user has who has not yet logged in.
#
#default_permissions "read,add,control,admin"
#
......@@ -174,7 +176,18 @@
# Database #######################################################################
#
# An example of a database section instead of the old 'db_file' setting.
# It enables mounting other storages into the music directory.
#
#database {
# plugin "simple"
# path "~/.local/share/mpd/db
# cache_directory "~/.local/share/mpd/cache"
#}
#
# An example of database config for a satellite setup
#
#music_directory "nfs://fileserver.local/srv/mp3"
#database {
# plugin "proxy"
# host "other.mpd.host"
......@@ -183,7 +196,6 @@
# Input #######################################################################
#
input {
plugin "curl"
# proxy "proxy.isp.com:8080"
......@@ -196,8 +208,8 @@ input {
# Audio Output ################################################################
#
# MPD supports various audio output types, as well as playing through multiple
# audio outputs at the same time, through multiple audio_output settings
# MPD supports various audio output types, as well as playing through multiple
# audio outputs at the same time, through multiple audio_output settings
# blocks. Setting this block is optional, though the server will only attempt
# autodetection for one sound card.
#
......@@ -280,6 +292,7 @@ input {
# name "My Pulse Output"
## server "remote_server" # optional
## sink "remote_server_sink" # optional
## media_role "media_role" #optional
#}
#
# An example of a winmm output (Windows multimedia API).
......@@ -293,6 +306,20 @@ input {
## mixer_type "hardware" # optional
#}
#
# An example of a wasapi output (Windows multimedia API).
#
#audio_output {
# type "wasapi"
# name "My WASAPI output"
## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional
# or
## device "0" # optional
## Exclusive mode blocks all other audio source, and get best audio quality without resampling.
## exclusive "no" # optional
## Enumerate all devices in log.
## enumerate "no" # optional
#}
#
# An example of an openal output.
#
#audio_output {
......@@ -348,7 +375,8 @@ input {
# the argument "off", "album", "track" or "auto". "auto" is a special mode that
# chooses between "track" and "album" depending on the current state of
# random playback. If random playback is enabled then "track" mode is used.
# See <http://www.replaygain.org> for more details about ReplayGain.
# See <https://wiki.hydrogenaud.io/index.php?title=Replaygain> for
# more details about ReplayGain.
# This setting is off by default.
#
#replaygain "album"
......@@ -373,7 +401,7 @@ input {
#replaygain_limit "yes"
#
# This setting enables on-the-fly normalization volume adjustment. This will
# result in the volume of all playing audio to be adjusted so the output has
# result in the volume of all playing audio to be adjusted so the output has
# equal "loudness". This setting is disabled by default.
#
#volume_normalization "no"
......@@ -382,7 +410,7 @@ input {
# Character Encoding ##########################################################
#
# If file or directory names do not display correctly for your locale then you
# If file or directory names do not display correctly for your locale then you
# may need to modify this setting.
#
#filesystem_charset "UTF-8"
......
option('documentation', type: 'boolean', value: false, description: 'Build documentation')
option('test', type: 'boolean', value: false, description: 'Build the unit tests and debug programs')
option('documentation', type: 'feature', description: 'Build documentation')
option('html_manual', type: 'boolean', value: true, description: 'Build the HTML manual')
option('manpages', type: 'boolean', value: true, description: 'Build manual pages')
option('syslog', type: 'feature', description: 'syslog support')
option('inotify', type: 'boolean', value: true, description: 'inotify support (for automatic database update)')
option('io_uring', type: 'feature', description: 'Linux io_uring support using liburing')
option('daemon', type: 'boolean', value: true, description: 'enable daemonization')
option('systemd', type: 'feature', description: 'systemd support')
......@@ -12,6 +13,13 @@ option('systemd_system_unit_dir', type: 'string', description: 'systemd system s
option('systemd_user_unit_dir', type: 'string', description: 'systemd user service directory')
#
# Options for developers
#
option('test', type: 'boolean', value: false, description: 'Build the unit tests and debug programs')
option('fuzzer', type: 'boolean', value: false, description: 'Build fuzzers (requires libFuzzer)')
#
# Android
#
......@@ -54,7 +62,10 @@ option('dsd', type: 'boolean', value: true, description: 'Support the DSD audio
#
option('database', type: 'boolean', value: true, description: 'enable support for the music database')
option('upnp', type: 'feature', description: 'UPnP client support')
option('upnp', type: 'combo',
choices: ['auto', 'pupnp', 'npupnp', 'disabled'],
value: 'auto',
description: 'UPnP client support')
option('libmpdclient', type: 'feature', description: 'libmpdclient support (for the proxy database plugin)')
#
......@@ -84,7 +95,11 @@ option('cdio_paranoia', type: 'feature', description: 'libcdio_paranoia input pl
option('curl', type: 'feature', description: 'HTTP client using CURL')
option('mms', type: 'feature', description: 'MMS protocol support using libmms')
option('nfs', type: 'feature', description: 'NFS protocol support using libnfs')
option('smbclient', type: 'feature', description: 'SMB support using libsmbclient')
# The "smbclient" plugin is disabled by default because libsmbclient
# has a serious bug which crashes MPD very quickly:
# https://bugzilla.samba.org/show_bug.cgi?id=11413
option('smbclient', type: 'feature', value: 'disabled', description: 'SMB support using libsmbclient')
#
# Commercial services
......@@ -92,7 +107,6 @@ option('smbclient', type: 'feature', description: 'SMB support using libsmbclien
option('qobuz', type: 'feature', description: 'Qobuz client')
option('soundcloud', type: 'feature', description: 'SoundCloud client')
option('tidal', type: 'feature', description: 'Tidal client')
#
# Archive plugins
......@@ -123,6 +137,7 @@ option('gme', type: 'feature', description: 'Game Music Emulator decoder plugin'
option('mad', type: 'feature', description: 'MP3 decoder using libmad')
option('mikmod', type: 'feature', description: 'MikMod decoder plugin')
option('modplug', type: 'feature', description: 'Modplug decoder plugin')
option('openmpt', type: 'feature', description: 'OpenMPT decoder plugin')
option('mpcdec', type: 'feature', description: 'Musepack decoder plugin')
option('mpg123', type: 'feature', description: 'MP3 decoder using libmpg123')
option('opus', type: 'feature', description: 'Opus decoder plugin')
......@@ -162,9 +177,11 @@ option('jack', type: 'feature', description: 'JACK output plugin')
option('openal', type: 'feature', description: 'OpenAL output plugin')
option('oss', type: 'feature', description: 'Open Sound System support')
option('pipe', type: 'boolean', value: true, description: 'Pipe output plugin')
option('pipewire', type: 'feature', description: 'PipeWire support')
option('pulse', type: 'feature', description: 'PulseAudio support')
option('recorder', type: 'boolean', value: true, description: 'Recorder output plugin')
option('shout', type: 'feature', description: 'Shoutcast streaming support using libshout')
option('snapcast', type: 'boolean', value: true, description: 'Snapcast output plugin')
option('sndio', type: 'feature', description: 'sndio output plugin')
option('solaris_output', type: 'feature', description: 'Solaris /dev/audio support')
......
......@@ -55,10 +55,10 @@ class AutotoolsProject(MakeProject):
subprocess.check_call(configure, cwd=build, env=toolchain.env)
return build
def build(self, toolchain):
def _build(self, toolchain):
build = self.configure(toolchain)
if self.subdirs is not None:
for subdir in self.subdirs:
MakeProject.build(self, toolchain, os.path.join(build, subdir))
self.build_make(toolchain, os.path.join(build, subdir))
else:
MakeProject.build(self, toolchain, build)
self.build_make(toolchain, build)
......@@ -12,7 +12,7 @@ class BoostProject(Project):
name='boost', version=version,
**kwargs)
def build(self, toolchain):
def _build(self, toolchain):
src = self.unpack(toolchain)
# install the headers manually; don't build any library
......@@ -21,3 +21,8 @@ class BoostProject(Project):
dest = os.path.join(includedir, 'boost')
shutil.rmtree(dest, ignore_errors=True)
shutil.copytree(os.path.join(src, 'boost'), dest)
# touch the boost/version.hpp file to ensure it's newer than
# the downloaded Boost tarball, to avoid reinstalling Boost on
# every run
os.utime(os.path.join(toolchain.install_prefix, self.installed))
import os
import subprocess
from build.project import Project
def __write_cmake_compiler(f, language, compiler):
s = compiler.split(' ', 1)
if len(s) == 2:
print(f'set(CMAKE_{language}_COMPILER_LAUNCHER {s[0]})', file=f)
compiler = s[1]
print(f'set(CMAKE_{language}_COMPILER {compiler})', file=f)
def __write_cmake_toolchain_file(f, toolchain):
if '-darwin' in toolchain.actual_arch:
cmake_system_name = 'Darwin'
elif toolchain.is_windows:
cmake_system_name = 'Windows'
else:
cmake_system_name = 'Linux'
f.write(f"""
set(CMAKE_SYSTEM_NAME {cmake_system_name})
set(CMAKE_SYSTEM_PROCESSOR {toolchain.actual_arch.split('-', 1)[0]})
set(CMAKE_C_COMPILER_TARGET {toolchain.actual_arch})
set(CMAKE_CXX_COMPILER_TARGET {toolchain.actual_arch})
set(CMAKE_C_FLAGS "{toolchain.cflags} {toolchain.cppflags}")
set(CMAKE_CXX_FLAGS "{toolchain.cxxflags} {toolchain.cppflags}")
""")
__write_cmake_compiler(f, 'C', toolchain.cc)
__write_cmake_compiler(f, 'CXX', toolchain.cxx)
def configure(toolchain, src, build, args=()):
cross_args = []
if toolchain.is_windows:
cross_args.append('-DCMAKE_SYSTEM_NAME=Windows')
cross_args.append('-DCMAKE_RC_COMPILER=' + toolchain.windres)
# Several targets need a sysroot to prevent pkg-config from
# looking for libraries on the build host (TODO: fix this
# properly); but we must not do that on Android because the NDK
# has a sysroot already
if '-android' not in toolchain.actual_arch and '-darwin' not in toolchain.actual_arch:
cross_args.append('-DCMAKE_SYSROOT=' + toolchain.install_prefix)
os.makedirs(build, exist_ok=True)
cmake_toolchain_file = os.path.join(build, 'cmake_toolchain_file')
with open(cmake_toolchain_file, 'w') as f:
__write_cmake_toolchain_file(f, toolchain)
configure = [
'cmake',
src,
'-DCMAKE_TOOLCHAIN_FILE=' + cmake_toolchain_file,
'-DCMAKE_INSTALL_PREFIX=' + toolchain.install_prefix,
'-DCMAKE_BUILD_TYPE=release',
'-DCMAKE_C_COMPILER=' + toolchain.cc,
'-DCMAKE_CXX_COMPILER=' + toolchain.cxx,
'-DCMAKE_C_FLAGS=' + toolchain.cflags + ' ' + toolchain.cppflags,
'-DCMAKE_CXX_FLAGS=' + toolchain.cxxflags + ' ' + toolchain.cppflags,
'-GNinja',
] + cross_args + args
......@@ -29,17 +65,22 @@ def configure(toolchain, src, build, args=()):
class CmakeProject(Project):
def __init__(self, url, md5, installed, configure_args=[],
windows_configure_args=[],
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args
self.windows_configure_args = windows_configure_args
def configure(self, toolchain):
src = self.unpack(toolchain)
build = self.make_build_path(toolchain)
configure(toolchain, src, build, self.configure_args)
configure_args = self.configure_args
if toolchain.is_windows:
configure_args = configure_args + self.windows_configure_args
configure(toolchain, src, build, configure_args)
return build
def build(self, toolchain):
def _build(self, toolchain):
build = self.configure(toolchain)
subprocess.check_call(['ninja', 'install'],
cwd=build, env=toolchain.env)
......@@ -10,12 +10,7 @@ class FfmpegProject(Project):
self.configure_args = configure_args
self.cppflags = cppflags
def _filter_cflags(self, flags):
# FFmpeg expects the GNU as syntax
flags = flags.replace(' -integrated-as ', ' -no-integrated-as ')
return flags
def build(self, toolchain):
def _build(self, toolchain):
src = self.unpack(toolchain)
build = self.make_build_path(toolchain)
......@@ -36,8 +31,8 @@ class FfmpegProject(Project):
'--cc=' + toolchain.cc,
'--cxx=' + toolchain.cxx,
'--nm=' + toolchain.nm,
'--extra-cflags=' + self._filter_cflags(toolchain.cflags) + ' ' + toolchain.cppflags + ' ' + self.cppflags,
'--extra-cxxflags=' + self._filter_cflags(toolchain.cxxflags) + ' ' + toolchain.cppflags + ' ' + self.cppflags,
'--extra-cflags=' + toolchain.cflags + ' ' + toolchain.cppflags + ' ' + self.cppflags,
'--extra-cxxflags=' + toolchain.cxxflags + ' ' + toolchain.cppflags + ' ' + self.cppflags,
'--extra-ldflags=' + toolchain.ldflags,
'--extra-libs=' + toolchain.libs,
'--ar=' + toolchain.ar,
......
import os, shutil
import re
from .project import Project
# This class installs just the public headers and a fake pkg-config
# file which defines the macro "DYNAMIC_JACK". This tells MPD's JACK
# output plugin to load the libjack64.dll dynamically using
# LoadLibrary(). This kludge avoids the runtime DLL dependency for
# users who don't use JACK, but still allows using the system JACK
# client library.
#
# The problem with JACK is that it uses an extremely fragile shared
# memory protocol to communicate with the daemon. One needs to use
# daemon and client library from the same build. That's why we don't
# build libjack statically here; it would probably not be compatible
# with the user's JACK daemon.
class JackProject(Project):
def __init__(self, url, md5, installed,
**kwargs):
m = re.match(r'.*/v([\d.]+)\.tar\.gz$', url)
self.version = m.group(1)
Project.__init__(self, url, md5, installed,
name='jack2', version=self.version,
base='jack2-' + self.version,
**kwargs)
def _build(self, toolchain):
src = self.unpack(toolchain)
includes = ['jack.h', 'ringbuffer.h', 'systemdeps.h', 'transport.h', 'types.h', 'weakmacros.h']
includedir = os.path.join(toolchain.install_prefix, 'include', 'jack')
os.makedirs(includedir, exist_ok=True)
for i in includes:
shutil.copyfile(os.path.join(src, 'common', 'jack', i),
os.path.join(includedir, i))
with open(os.path.join(toolchain.install_prefix, 'lib', 'pkgconfig', 'jack.pc'), 'w') as f:
print("prefix=" + toolchain.install_prefix, file=f)
print("", file=f)
print("Name: jack", file=f)
print("Description: dummy", file=f)
print("Version: " + self.version, file=f)
print("Libs: ", file=f)
print("Cflags: -DDYNAMIC_JACK", file=f)
......@@ -7,37 +7,27 @@ from build.meson import MesonProject
from build.cmake import CmakeProject
from build.autotools import AutotoolsProject
from build.ffmpeg import FfmpegProject
from build.openssl import OpenSSLProject
from build.boost import BoostProject
from build.jack import JackProject
libmpdclient = MesonProject(
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.18.tar.xz',
'4cb01e1f567e0169aca94875fb6e1200e7f5ce35b63a4df768ec1591fb1081fa',
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.20.tar.xz',
'18793f68e939c3301e34d8fcadea1f7daa24143941263cecadb80126194e277d',
'lib/libmpdclient.a',
)
libogg = AutotoolsProject(
'http://downloads.xiph.org/releases/ogg/libogg-1.3.4.tar.xz',
'c163bc12bc300c401b6aa35907ac682671ea376f13ae0969a220f7ddf71893fe',
libogg = CmakeProject(
'http://downloads.xiph.org/releases/ogg/libogg-1.3.5.tar.xz',
'c4d91be36fc8e54deae7575241e03f4211eb102afb3fc0775fbbc1b740016705',
'lib/libogg.a',
[
'--disable-shared', '--enable-static',
'-DBUILD_SHARED_LIBS=OFF',
'-DINSTALL_DOCS=OFF',
'-DINSTALL_CMAKE_PACKAGE_MODULE=OFF',
],
)
libvorbis = AutotoolsProject(
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.6.tar.xz',
'af00bb5a784e7c9e69f56823de4637c350643deedaf333d0fa86ecdba6fcb415',
'lib/libvorbis.a',
[
'--disable-shared', '--enable-static',
],
edits={
# this option is not understood by clang
'configure': lambda data: data.replace('-mno-ieee-fp', ' '),
}
)
opus = AutotoolsProject(
'https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz',
'65b58e1e25b2a114157014736a3d9dfeaad8d41be1c8179866f144a2fb44ff9d',
......@@ -53,8 +43,8 @@ opus = AutotoolsProject(
)
flac = AutotoolsProject(
'http://downloads.xiph.org/releases/flac/flac-1.3.3.tar.xz',
'213e82bd716c9de6db2f98bcadbc4c24c7e2efe8c75939a1a84e28539c4e1748',
'http://downloads.xiph.org/releases/flac/flac-1.3.4.tar.xz',
'8ff0607e75a322dd7cd6ec48f4f225471404ae2730d0ea945127b1355155e737',
'lib/libFLAC.a',
[
'--disable-shared', '--enable-static',
......@@ -65,8 +55,8 @@ flac = AutotoolsProject(
)
zlib = ZlibProject(
'http://zlib.net/zlib-1.2.11.tar.xz',
'4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066',
'http://zlib.net/zlib-1.2.12.tar.xz',
'7db46b8d7726232a621befaab4a1c870f00a90805511c0e0090441dac57def18',
'lib/libz.a',
)
......@@ -121,18 +111,31 @@ libmodplug = AutotoolsProject(
],
)
libopenmpt = AutotoolsProject(
'https://lib.openmpt.org/files/libopenmpt/src/libopenmpt-0.5.12+release.autotools.tar.gz',
'892aea7a599b5d21842bebf463b5aafdad5711be7008dd84401920c6234820af',
'lib/libopenmpt.a',
[
'--disable-shared', '--enable-static',
'--disable-openmpt123',
'--without-mpg123', '--without-ogg', '--without-vorbis', '--without-vorbisfile',
'--without-portaudio', '--without-portaudiocpp', '--without-sndfile',
],
base='libopenmpt-0.5.12+release.autotools',
)
wildmidi = CmakeProject(
'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.3',
'498e5a96455bb4b91b37188ad6dcb070824e92c44f5ed452b90adbaec8eef3c5',
'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.4',
'6f267c8d331e9859906837e2c197093fddec31829d2ebf7b958cf6b7ae935430',
'lib/libWildMidi.a',
[
'-DBUILD_SHARED_LIBS=OFF',
'-DWANT_PLAYER=OFF',
'-DWANT_STATIC=ON',
],
base='wildmidi-wildmidi-0.4.3',
base='wildmidi-wildmidi-0.4.4',
name='wildmidi',
version='0.4.3',
version='0.4.4',
)
gme = CmakeProject(
......@@ -148,8 +151,8 @@ gme = CmakeProject(
)
ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-4.2.3.tar.xz',
'9df6c90aed1337634c1fb026fb01c154c29c82a64ea71291ff2da9aacb9aad31',
'http://ffmpeg.org/releases/ffmpeg-5.0.1.tar.xz',
'ef2efae259ce80a240de48ec85ecb062cecca26e4352ffb3fda562c21a93007b',
'lib/libavcodec.a',
[
'--disable-shared', '--enable-static',
......@@ -174,6 +177,8 @@ ffmpeg = FfmpegProject(
'--disable-filters',
'--disable-v4l2_m2m',
'--disable-vulkan',
'--disable-parser=bmp',
'--disable-parser=cavsvideo',
'--disable-parser=dvbsub',
......@@ -376,48 +381,50 @@ ffmpeg = FfmpegProject(
],
)
curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.70.0.tar.xz',
'032f43f2674008c761af19bf536374128c16241fb234699a55f9fb603fcfbae7',
'lib/libcurl.a',
[
'--disable-shared', '--enable-static',
'--disable-debug',
'--enable-http',
'--enable-ipv6',
'--disable-ftp', '--disable-file',
'--disable-ldap', '--disable-ldaps',
'--disable-rtsp', '--disable-proxy', '--disable-dict', '--disable-telnet',
'--disable-tftp', '--disable-pop3', '--disable-imap', '--disable-smtp',
'--disable-smb',
'--disable-gopher',
'--disable-manual',
'--disable-threaded-resolver', '--disable-verbose', '--disable-sspi',
'--disable-crypto-auth', '--disable-ntlm-wb', '--disable-tls-srp', '--disable-cookies',
'--disable-doh',
'--disable-mime',
'--disable-netrc',
'--disable-progress-meter',
'--disable-alt-svc',
'--without-ssl', '--without-gnutls', '--without-nss', '--without-libssh2',
],
patches='src/lib/curl/patches',
openssl = OpenSSLProject(
'https://www.openssl.org/source/openssl-3.0.5.tar.gz',
'aa7d8d9bef71ad6525c55ba11e5f4397889ce49c2c9349dcea6d3e4f0b024a7a',
'include/openssl/ossl_typ.h',
)
libexpat = AutotoolsProject(
'https://github.com/libexpat/libexpat/releases/download/R_2_2_9/expat-2.2.9.tar.bz2',
'f1063084dc4302a427dabcca499c8312b3a32a29b7d2506653ecc8f950a9a237',
'lib/libexpat.a',
curl = CmakeProject(
'https://curl.se/download/curl-7.84.0.tar.xz',
'2d118b43f547bfe5bae806d8d47b4e596ea5b25a6c1f080aef49fbcd817c5db8',
'lib/libcurl.a',
[
'--disable-shared', '--enable-static',
'--without-docbook',
'-DBUILD_CURL_EXE=OFF',
'-DBUILD_SHARED_LIBS=OFF',
'-DCURL_DISABLE_VERBOSE_STRINGS=ON',
'-DCURL_DISABLE_LDAP=ON',
'-DCURL_DISABLE_TELNET=ON',
'-DCURL_DISABLE_DICT=ON',
'-DCURL_DISABLE_FILE=ON',
'-DCURL_DISABLE_FTP=ON',
'-DCURL_DISABLE_TFTP=ON',
'-DCURL_DISABLE_LDAPS=ON',
'-DCURL_DISABLE_RTSP=ON',
'-DCURL_DISABLE_PROXY=ON',
'-DCURL_DISABLE_POP3=ON',
'-DCURL_DISABLE_IMAP=ON',
'-DCURL_DISABLE_SMTP=ON',
'-DCURL_DISABLE_GOPHER=ON',
'-DCURL_DISABLE_COOKIES=ON',
'-DCURL_DISABLE_CRYPTO_AUTH=ON',
'-DCURL_DISABLE_ALTSVC=ON',
'-DCMAKE_USE_LIBSSH2=OFF',
'-DCURL_WINDOWS_SSPI=OFF',
'-DCURL_DISABLE_NTLM=ON',
'-DBUILD_TESTING=OFF',
],
windows_configure_args=[
'-DCURL_USE_SCHANNEL=ON',
],
patches='src/lib/curl/patches',
)
libnfs = AutotoolsProject(
'https://github.com/sahlberg/libnfs/archive/libnfs-4.0.0.tar.gz',
'6ee77e9fe220e2d3e3b1f53cfea04fb319828cc7dbb97dd9df09e46e901d797d',
'https://github.com/sahlberg/libnfs/archive/libnfs-5.0.1.tar.gz',
'7ef445410b42f36b9bad426608b53ccb9ccca4101e545c383f564c11db672ca8',
'lib/libnfs.a',
[
'--disable-shared', '--enable-static',
......@@ -428,12 +435,18 @@ libnfs = AutotoolsProject(
'--disable-utils', '--disable-examples',
],
base='libnfs-libnfs-4.0.0',
base='libnfs-libnfs-5.0.1',
autoreconf=True,
)
jack = JackProject(
'https://github.com/jackaudio/jack2/archive/v1.9.17.tar.gz',
'38f674bbc57852a8eb3d9faa1f96a0912d26f7d5df14c11005ad499c8ae352f2',
'lib/pkgconfig/jack.pc',
)
boost = BoostProject(
'https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.bz2',
'4eb3b8d442b426dc35346235c8733b5ae35ba431690e38c6a8263dce9fcbb402',
'https://boostorg.jfrog.io/artifactory/main/release/1.79.0/source/boost_1_79_0.tar.bz2',
'475d589d51a7f8b3ba2ba4eda022b170e562ca3b760ee922c146b6c65856ef39',
'include/boost/version.hpp',
)
......@@ -22,7 +22,7 @@ class MakeProject(Project):
subprocess.check_call(['/usr/bin/make'] + args,
cwd=wd, env=toolchain.env)
def build(self, toolchain, wd, install=True):
def build_make(self, toolchain, wd, install=True):
self.make(toolchain, wd, self.get_make_args(toolchain))
if install:
self.make(toolchain, wd, self.get_make_install_args(toolchain))
import os.path, subprocess, sys
import platform
from build.project import Project
......@@ -34,41 +35,48 @@ def make_cross_file(toolchain):
path = os.path.join(toolchain.build_path, 'meson.cross')
os.makedirs(toolchain.build_path, exist_ok=True)
with open(path, 'w') as f:
f.write("""
f.write(f"""
[binaries]
c = '%s'
cpp = '%s'
ar = '%s'
strip = '%s'
pkgconfig = '%s'
%s
c = '{toolchain.cc}'
cpp = '{toolchain.cxx}'
ar = '{toolchain.ar}'
strip = '{toolchain.strip}'
pkgconfig = '{toolchain.pkg_config}'
""")
[properties]
root = '%s'
if toolchain.is_windows and platform.system() != 'Windows':
f.write(f"windres = '{toolchain.windres}'\n")
c_args = %s
c_link_args = %s
# Run unit tests with WINE when cross-building for Windows
print("exe_wrapper = 'wine'", file=f)
cpp_args = %s
cpp_link_args = %s
f.write(f"""
[properties]
root = '{toolchain.install_prefix}'
""")
if 'android' in toolchain.arch:
f.write("""
# Keep Meson from executing Android-x86 test binariees
needs_exe_wrapper = true
""")
f.write(f"""
[built-in options]
c_args = {repr((toolchain.cppflags + ' ' + toolchain.cflags).split())}
c_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
cpp_args = {repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split())}
cpp_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
""")
f.write(f"""
[host_machine]
system = '%s'
cpu_family = '%s'
cpu = '%s'
endian = '%s'
""" % (toolchain.cc, toolchain.cxx, toolchain.ar, toolchain.strip,
toolchain.pkg_config,
windres,
toolchain.install_prefix,
repr((toolchain.cppflags + ' ' + toolchain.cflags).split()),
repr(toolchain.ldflags.split() + toolchain.libs.split()),
repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split()),
repr(toolchain.ldflags.split() + toolchain.libs.split()),
system, cpu_family, cpu, endian))
system = '{system}'
cpu_family = '{cpu_family}'
cpu = '{cpu}'
endian = '{endian}'
""")
return path
def configure(toolchain, src, build, args=()):
......@@ -79,11 +87,6 @@ def configure(toolchain, src, build, args=()):
'--prefix', toolchain.install_prefix,
# this is necessary because Meson uses Debian's build machine
# MultiArch path (e.g. "lib/x86_64-linux-gnu") for cross
# builds, which is obviously wrong
'--libdir', 'lib',
'--buildtype', 'plain',
'--default-library=static',
......@@ -91,7 +94,12 @@ def configure(toolchain, src, build, args=()):
'--cross-file', cross_file,
] + args
subprocess.check_call(configure, env=toolchain.env)
env = toolchain.env.copy()
# Meson 0.54 requires the BOOST_ROOT environment variable
env['BOOST_ROOT'] = toolchain.install_prefix
subprocess.check_call(configure, env=env)
class MesonProject(Project):
def __init__(self, url, md5, installed, configure_args=[],
......@@ -105,7 +113,7 @@ class MesonProject(Project):
configure(toolchain, src, build, self.configure_args)
return build
def build(self, toolchain):
def _build(self, toolchain):
build = self.configure(toolchain)
subprocess.check_call(['ninja', 'install'],
cwd=build, env=toolchain.env)
import subprocess
from build.makeproject import MakeProject
class OpenSSLProject(MakeProject):
def __init__(self, url, md5, installed,
**kwargs):
MakeProject.__init__(self, url, md5, installed, install_target='install_dev', **kwargs)
def get_make_args(self, toolchain):
return MakeProject.get_make_args(self, toolchain) + [
'CC=' + toolchain.cc,
'CFLAGS=' + toolchain.cflags,
'CPPFLAGS=' + toolchain.cppflags,
'AR=' + toolchain.ar,
'RANLIB=' + toolchain.ranlib,
'build_libs',
]
def get_make_install_args(self, toolchain):
# OpenSSL's Makefile runs "ranlib" during installation
return MakeProject.get_make_install_args(self, toolchain) + [
'RANLIB=' + toolchain.ranlib,
]
def _build(self, toolchain):
src = self.unpack(toolchain, out_of_tree=False)
# OpenSSL has a weird target architecture scheme with lots of
# hard-coded architectures; this table translates between our
# "toolchain_arch" (HOST_TRIPLET) and the OpenSSL target
openssl_archs = {
# not using "android-*" because those OpenSSL targets want
# to know where the SDK is, but our own build scripts
# prepared everything already to look like a regular Linux
# build
'arm-linux-androideabi': 'linux-generic32',
'aarch64-linux-android': 'linux-aarch64',
'i686-linux-android': 'linux-x86-clang',
'x86_64-linux-android': 'linux-x86_64-clang',
# Kobo
'arm-linux-gnueabihf': 'linux-generic32',
# Windows
'i686-w64-mingw32': 'mingw',
'x86_64-w64-mingw32': 'mingw64',
}
openssl_arch = openssl_archs[toolchain.arch]
subprocess.check_call(['./Configure',
'no-shared',
'no-module', 'no-engine', 'no-static-engine',
'no-async',
'no-tests',
'no-asm', # "asm" causes build failures on Windows
openssl_arch,
'--prefix=' + toolchain.install_prefix],
cwd=src, env=toolchain.env)
self.build_make(toolchain, src)
......@@ -20,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]?[\d.]*)$', self.base)
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*(?:-(?:alpha|beta)\d+)?)(\+.*)?$', self.base)
if name is None: name = m.group(1)
if version is None: version = m.group(2)
......@@ -79,3 +79,6 @@ class Project:
pass
os.makedirs(path, exist_ok=True)
return path
def build(self, toolchain):
self._build(toolchain)
......@@ -7,5 +7,11 @@ def untar(tarball_path, parent_path, base):
except FileNotFoundError:
pass
os.makedirs(parent_path, exist_ok=True)
subprocess.check_call(['/bin/tar', 'xfC', tarball_path, parent_path])
try:
subprocess.check_call(['/bin/tar', 'xfC', tarball_path, parent_path])
except FileNotFoundError:
import tarfile
tar = tarfile.open(tarball_path)
tar.extractall(path=parent_path)
tar.close()
return path
......@@ -31,6 +31,8 @@ def guess_digest_algorithm(digest):
return hashlib.sha1
elif l == 64:
return hashlib.sha256
elif l == 128:
return hashlib.sha512
else:
return None
......
......@@ -7,7 +7,7 @@ class ZlibProject(Project):
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
def build(self, toolchain):
def _build(self, toolchain):
src = self.unpack(toolchain, out_of_tree=False)
subprocess.check_call(['/usr/bin/make', '--quiet',
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -95,7 +95,7 @@ public:
constexpr double ToDoubleS() const {
return double(count()) / 1000.;
};
}
constexpr bool IsZero() const {
return count() == 0;
......@@ -199,7 +199,7 @@ public:
constexpr double ToDoubleS() const {
return double(count()) / 1000.;
};
}
constexpr bool IsZero() const {
return count() == 0;
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -37,11 +37,16 @@
#include "fs/Traits.hxx"
#include "fs/FileSystem.hxx"
#include "fs/StandardDirectory.hxx"
#include "system/Error.hxx"
#include "util/RuntimeError.hxx"
#include "event/Features.h"
#include "io/uring/Features.h"
#include "util/Domain.hxx"
#include "util/OptionDef.hxx"
#include "util/OptionParser.hxx"
#include "Version.h"
#ifdef _WIN32
#include "system/Error.hxx"
#endif
#ifdef ENABLE_DATABASE
#include "db/Registry.hxx"
......@@ -55,6 +60,7 @@
#include "neighbor/NeighborPlugin.hxx"
#endif
#include "encoder/Features.h"
#ifdef ENABLE_ENCODER
#include "encoder/EncoderList.hxx"
#include "encoder/EncoderPlugin.hxx"
......@@ -65,9 +71,6 @@
#include "archive/ArchivePlugin.hxx"
#endif
#include <stdio.h>
#include <stdlib.h>
namespace {
#ifdef _WIN32
constexpr auto CONFIG_FILE_LOCATION = Path::FromFS(PATH_LITERAL("mpd\\mpd.conf"));
......@@ -77,12 +80,15 @@ constexpr auto USER_CONFIG_FILE_LOCATION1 = Path::FromFS(PATH_LITERAL(".mpdconf"
constexpr auto USER_CONFIG_FILE_LOCATION2 = Path::FromFS(PATH_LITERAL(".mpd/mpd.conf"));
constexpr auto USER_CONFIG_FILE_LOCATION_XDG = Path::FromFS(PATH_LITERAL("mpd/mpd.conf"));
#endif
}
} // namespace
enum Option {
OPTION_KILL,
OPTION_NO_CONFIG,
OPTION_NO_DAEMON,
#ifdef __linux__
OPTION_SYSTEMD,
#endif
OPTION_STDOUT,
OPTION_STDERR,
OPTION_VERBOSE,
......@@ -95,6 +101,9 @@ static constexpr OptionDef option_defs[] = {
{"kill", "kill the currently running mpd session"},
{"no-config", "don't read from config"},
{"no-daemon", "don't detach from console"},
#ifdef __linux__
{"systemd", "systemd service mode"},
#endif
{"stdout", nullptr}, // hidden, compatibility with old versions
{"stderr", "print messages to stderr"},
{"verbose", 'v', "verbose logging"},
......@@ -105,13 +114,13 @@ static constexpr OptionDef option_defs[] = {
static constexpr Domain cmdline_domain("cmdline");
gcc_noreturn
static void version(void)
[[noreturn]]
static void version()
{
printf("Music Player Daemon " VERSION " (%s)"
"\n"
"Copyright 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
"Copyright 2008-2018 Max Kellermann <max.kellermann@gmail.com>\n"
"Copyright 2008-2021 Max Kellermann <max.kellermann@gmail.com>\n"
"This is free software; see the source for copying conditions. There is NO\n"
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
GIT_VERSION);
......@@ -145,15 +154,19 @@ static void version(void)
"Decoders plugins:\n");
decoder_plugins_for_each([](const DecoderPlugin &plugin){
printf(" [%s]", plugin.name);
printf(" [%s]", plugin.name);
const char *const*suffixes = plugin.suffixes;
if (suffixes != nullptr)
for (; *suffixes != nullptr; ++suffixes)
printf(" %s", *suffixes);
const char *const*suffixes = plugin.suffixes;
if (suffixes != nullptr)
for (; *suffixes != nullptr; ++suffixes)
printf(" %s", *suffixes);
printf("\n");
});
if (plugin.protocols != nullptr)
for (const auto &i : plugin.protocols())
printf(" %s", i.c_str());
printf("\n");
});
printf("\n"
"Filters:\n"
......@@ -202,6 +215,9 @@ static void version(void)
"\n"
"Input plugins:\n"
" file"
#ifdef HAVE_URING
" io_uring"
#endif
#ifdef ENABLE_ARCHIVE
" archive"
#endif
......@@ -255,7 +271,7 @@ static void version(void)
#endif
"\n");
exit(EXIT_SUCCESS);
std::exit(EXIT_SUCCESS);
}
static void PrintOption(const OptionDef &opt)
......@@ -271,8 +287,8 @@ static void PrintOption(const OptionDef &opt)
opt.GetDescription());
}
gcc_noreturn
static void help(void)
[[noreturn]]
static void help()
{
printf("Usage:\n"
" mpd [OPTION...] [path/to/mpd.conf]\n"
......@@ -282,10 +298,10 @@ static void help(void)
"Options:\n");
for (const auto &i : option_defs)
if(i.HasDescription() == true) // hide hidden options from help print
if(i.HasDescription()) // hide hidden options from help print
PrintOption(i);
exit(EXIT_SUCCESS);
std::exit(EXIT_SUCCESS);
}
class ConfigLoader
......@@ -296,7 +312,7 @@ public:
explicit ConfigLoader(ConfigData &_config) noexcept
:config(_config) {}
bool TryFile(const Path path);
bool TryFile(Path path);
bool TryFile(const AllocatedPath &base_path, Path path);
};
......@@ -318,7 +334,7 @@ bool ConfigLoader::TryFile(const AllocatedPath &base_path, Path path)
}
void
ParseCommandLine(int argc, char **argv, struct options &options,
ParseCommandLine(int argc, char **argv, CommandLineOptions &options,
ConfigData &config)
{
bool use_config_file = true;
......@@ -339,6 +355,13 @@ ParseCommandLine(int argc, char **argv, struct options &options,
options.daemon = false;
break;
#ifdef __linux__
case OPTION_SYSTEMD:
options.daemon = false;
options.systemd = true;
break;
#endif
case OPTION_STDOUT:
case OPTION_STDERR:
options.log_stderr = true;
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -22,15 +22,20 @@
struct ConfigData;
struct options {
struct CommandLineOptions {
bool kill = false;
bool daemon = true;
#ifdef __linux__
bool systemd = false;
#endif
bool log_stderr = false;
bool verbose = false;
};
void
ParseCommandLine(int argc, char **argv, struct options &options,
ParseCommandLine(int argc, char **argv, CommandLineOptions &options,
ConfigData &config);
#endif
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -19,4 +19,4 @@
#include "GitVersion.hxx"
char GIT_VERSION[] = "@VCS_TAG@";
const char GIT_VERSION[] = "@VCS_TAG@";
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -20,6 +20,6 @@
#ifndef MPD_GIT_VERSION_HXX
#define MPD_GIT_VERSION_HXX
extern char GIT_VERSION[];
extern const char GIT_VERSION[];
#endif
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -26,12 +26,12 @@
#include "Main.hxx"
#include "Instance.hxx"
#include <assert.h>
#include <cassert>
void
idle_add(unsigned flags)
{
assert(flags != 0);
instance->EmitIdle(flags);
global_instance->EmitIdle(flags);
}
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -25,9 +25,9 @@
#include "IdleFlags.hxx"
#include "util/ASCII.hxx"
#include <assert.h>
#include <cassert>
static const char *const idle_names[] = {
static constexpr const char * idle_names[] = {
"database",
"stored_playlist",
"playlist",
......@@ -42,7 +42,7 @@ static const char *const idle_names[] = {
"neighbor",
"mount",
"partition",
nullptr
nullptr,
};
const char*const*
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -25,8 +25,6 @@
#ifndef MPD_IDLE_FLAGS_HXX
#define MPD_IDLE_FLAGS_HXX
#include "util/Compiler.h"
/** song database has been updated*/
static constexpr unsigned IDLE_DATABASE = 0x1;
......@@ -73,7 +71,7 @@ static constexpr unsigned IDLE_PARTITION = 0x2000;
/**
* Get idle names
*/
gcc_const
[[gnu::const]]
const char*const*
idle_get_names() noexcept;
......@@ -81,7 +79,7 @@ idle_get_names() noexcept;
* Parse an idle name and return its mask. Returns 0 if the given
* name is unknown.
*/
gcc_nonnull_all gcc_pure
[[gnu::nonnull]] [[gnu::pure]]
unsigned
idle_parse_name(const char *name) noexcept;
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -20,12 +20,15 @@
#include "config.h"
#include "Instance.hxx"
#include "Partition.hxx"
#include "Idle.hxx"
#include "IdleFlags.hxx"
#include "StateFile.hxx"
#include "Stats.hxx"
#include "client/List.hxx"
#include "input/cache/Manager.hxx"
#ifdef ENABLE_CURL
#include "RemoteTagCache.hxx"
#include "util/UriUtil.hxx"
#include "util/UriExtract.hxx"
#endif
#ifdef ENABLE_DATABASE
......@@ -34,14 +37,20 @@
#include "db/update/Service.hxx"
#include "storage/StorageInterface.hxx"
#ifdef ENABLE_INOTIFY
#include "db/update/InotifyUpdate.hxx"
#endif
#ifdef ENABLE_NEIGHBOR_PLUGINS
#include "neighbor/Glue.hxx"
#endif
#ifdef ENABLE_SQLITE
#include "sticker/StickerDatabase.hxx"
#include "sticker/Database.hxx"
#include "sticker/SongSticker.hxx"
#endif
#endif
#include <exception>
Instance::Instance()
:rtio_thread(true),
#ifdef ENABLE_SYSTEMD_DAEMON
......@@ -65,6 +74,13 @@ Instance::~Instance() noexcept
#endif
}
void
Instance::OnStateModified() noexcept
{
if (state_file)
state_file->CheckModified();
}
Partition *
Instance::FindPartition(const char *name) noexcept
{
......@@ -75,6 +91,20 @@ Instance::FindPartition(const char *name) noexcept
return nullptr;
}
void
Instance::DeletePartition(Partition &partition) noexcept
{
// TODO: use boost::intrusive::list to avoid this loop
for (auto i = partitions.begin();; ++i) {
assert(i != partitions.end());
if (&*i == &partition) {
partitions.erase(i);
break;
}
}
}
#ifdef ENABLE_DATABASE
const Database &
......@@ -88,7 +118,7 @@ Instance::GetDatabaseOrThrow() const
}
void
Instance::OnDatabaseModified()
Instance::OnDatabaseModified() noexcept
{
assert(database != nullptr);
......@@ -101,15 +131,15 @@ Instance::OnDatabaseModified()
}
void
Instance::OnDatabaseSongRemoved(const char *uri)
Instance::OnDatabaseSongRemoved(const char *uri) noexcept
{
assert(database != nullptr);
#ifdef ENABLE_SQLITE
/* if the song has a sticker, remove it */
if (sticker_enabled()) {
if (HasStickerDatabase()) {
try {
sticker_song_delete(uri);
sticker_song_delete(*sticker_database, uri);
} catch (...) {
}
}
......@@ -124,17 +154,15 @@ Instance::OnDatabaseSongRemoved(const char *uri)
#ifdef ENABLE_NEIGHBOR_PLUGINS
void
Instance::FoundNeighbor(gcc_unused const NeighborInfo &info) noexcept
Instance::FoundNeighbor([[maybe_unused]] const NeighborInfo &info) noexcept
{
for (auto &partition : partitions)
partition.EmitIdle(IDLE_NEIGHBOR);
EmitIdle(IDLE_NEIGHBOR);
}
void
Instance::LostNeighbor(gcc_unused const NeighborInfo &info) noexcept
Instance::LostNeighbor([[maybe_unused]] const NeighborInfo &info) noexcept
{
for (auto &partition : partitions)
partition.EmitIdle(IDLE_NEIGHBOR);
EmitIdle(IDLE_NEIGHBOR);
}
#endif
......@@ -166,3 +194,18 @@ Instance::OnRemoteTag(const char *uri, const Tag &tag) noexcept
}
#endif
void
Instance::OnIdle(unsigned flags) noexcept
{
/* broadcast to all partitions */
for (auto &partition : partitions)
partition.EmitIdle(flags);
}
void
Instance::FlushCaches() noexcept
{
if (input_cache)
input_cache->Flush();
}
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -24,7 +24,6 @@
#include "event/Loop.hxx"
#include "event/Thread.hxx"
#include "event/MaskMonitor.hxx"
#include "util/Compiler.h"
#ifdef ENABLE_SYSTEMD_DAEMON
#include "lib/systemd/Watchdog.hxx"
......@@ -44,6 +43,9 @@ class NeighborGlue;
#include "db/Ptr.hxx"
class Storage;
class UpdateService;
#ifdef ENABLE_INOTIFY
class InotifyUpdate;
#endif
#endif
#include <memory>
......@@ -53,6 +55,8 @@ class ClientList;
struct Partition;
class StateFile;
class RemoteTagCache;
class StickerDatabase;
class InputCacheManager;
/**
* A utility class which, when used as the first base class, ensures
......@@ -97,10 +101,16 @@ struct Instance final
Systemd::Watchdog systemd_watchdog;
#endif
std::unique_ptr<InputCacheManager> input_cache;
/**
* Monitor for global idle events to be broadcasted to all
* partitions.
*/
MaskMonitor idle_monitor;
#ifdef ENABLE_NEIGHBOR_PLUGINS
NeighborGlue *neighbors;
std::unique_ptr<NeighborGlue> neighbors;
#endif
#ifdef ENABLE_DATABASE
......@@ -113,17 +123,25 @@ struct Instance final
Storage *storage = nullptr;
UpdateService *update = nullptr;
#ifdef ENABLE_INOTIFY
std::unique_ptr<InotifyUpdate> inotify_update;
#endif
#endif
#ifdef ENABLE_CURL
std::unique_ptr<RemoteTagCache> remote_tag_cache;
#endif
ClientList *client_list;
std::unique_ptr<ClientList> client_list;
std::list<Partition> partitions;
StateFile *state_file = nullptr;
std::unique_ptr<StateFile> state_file;
#ifdef ENABLE_SQLITE
std::unique_ptr<StickerDatabase> sticker_database;
#endif
Instance();
~Instance() noexcept;
......@@ -131,21 +149,36 @@ struct Instance final
/**
* Wrapper for EventLoop::Break(). Call to initiate shutdown.
*/
void Break() {
void Break() noexcept {
event_loop.Break();
}
void EmitIdle(unsigned mask) {
/**
* Emit an "idle" event to all clients of all partitions.
*
* This method can be called from any thread.
*/
void EmitIdle(unsigned mask) noexcept {
idle_monitor.OrMask(mask);
}
/**
* Notify the #Instance that the state has been modified, and
* the #StateFile may need to be saved.
*
* This method must be called from the main thread.
*/
void OnStateModified() noexcept;
/**
* Find a #Partition with the given name. Returns nullptr if
* no such partition was found.
*/
gcc_pure
[[gnu::pure]]
Partition *FindPartition(const char *name) noexcept;
void DeletePartition(Partition &partition) noexcept;
void BeginShutdownPartitions() noexcept;
#ifdef ENABLE_DATABASE
......@@ -154,7 +187,7 @@ struct Instance final
* if this MPD configuration has no database (no
* music_directory was configured).
*/
Database *GetDatabase() {
Database *GetDatabase() noexcept {
return database.get();
}
......@@ -166,6 +199,12 @@ struct Instance final
const Database &GetDatabaseOrThrow() const;
#endif
#ifdef ENABLE_SQLITE
bool HasStickerDatabase() const noexcept {
return sticker_database != nullptr;
}
#endif
void BeginShutdownUpdate() noexcept;
#ifdef ENABLE_CURL
......@@ -176,10 +215,13 @@ struct Instance final
}
#endif
void FlushCaches() noexcept;
private:
#ifdef ENABLE_DATABASE
void OnDatabaseModified() override;
void OnDatabaseSongRemoved(const char *uri) override;
/* virtual methods from class DatabaseListener */
void OnDatabaseModified() noexcept override;
void OnDatabaseSongRemoved(const char *uri) noexcept override;
#endif
#ifdef ENABLE_NEIGHBOR_PLUGINS
......@@ -194,7 +236,7 @@ private:
#endif
/* callback for #idle_monitor */
void OnIdle(unsigned mask);
void OnIdle(unsigned mask) noexcept;
};
#endif
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -25,17 +25,19 @@
#include "config/Data.hxx"
#include "config/Option.hxx"
#include "config/Net.hxx"
#include "lib/fmt/ExceptionFormatter.hxx"
#include "lib/fmt/PathFormatter.hxx"
#include "net/AllocatedSocketAddress.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "net/SocketUtil.hxx"
#include "system/Error.hxx"
#include "util/RuntimeError.hxx"
#include "fs/AllocatedPath.hxx"
#include "fs/StandardDirectory.hxx"
#include "fs/XDG.hxx"
#include "util/Domain.hxx"
#include "util/RuntimeError.hxx"
#include <sys/stat.h>
#include <string.h>
#include <assert.h>
#ifdef ENABLE_SYSTEMD_DAEMON
#include <systemd/sd-daemon.h>
......@@ -43,6 +45,10 @@
#define DEFAULT_PORT 6600
#if defined(USE_XDG) && defined(HAVE_UN)
static constexpr Domain listen_domain("listen");
#endif
int listen_port;
#ifdef ENABLE_SYSTEMD_DAEMON
......@@ -80,13 +86,10 @@ ListenXdgRuntimeDir(ClientListener &listener) noexcept
use $XDG_RUNTIME_DIR */
return false;
Path xdg_runtime_dir = Path::FromFS(getenv("XDG_RUNTIME_DIR"));
if (xdg_runtime_dir.IsNull())
const auto mpd_runtime_dir = GetAppRuntimeDir();
if (mpd_runtime_dir.IsNull())
return false;
const auto mpd_runtime_dir = xdg_runtime_dir / Path::FromFS("mpd");
mkdir(mpd_runtime_dir.c_str(), 0700);
const auto socket_path = mpd_runtime_dir / Path::FromFS("socket");
unlink(socket_path.c_str());
......@@ -100,9 +103,9 @@ ListenXdgRuntimeDir(ClientListener &listener) noexcept
listener.AddFD(std::move(fd), std::move(address));
return true;
} catch (...) {
FormatError(std::current_exception(),
"Failed to listen on '%s' (not fatal)",
socket_path.c_str());
FmtError(listen_domain,
"Failed to listen on '{}' (not fatal): {}",
socket_path, std::current_exception());
return false;
}
#else
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -22,8 +22,9 @@
#include "client/Client.hxx"
#include "fs/AllocatedPath.hxx"
#include "ls.hxx"
#include "util/UriUtil.hxx"
#include "storage/Registry.hxx"
#include "util/ASCII.hxx"
#include "util/UriExtract.hxx"
#ifdef ENABLE_DATABASE
#include "storage/StorageInterface.hxx"
......@@ -42,18 +43,19 @@ LocateFileUri(const char *uri, const Client *client
#ifdef ENABLE_DATABASE
if (storage != nullptr) {
const char *suffix = storage->MapToRelativeUTF8(uri);
if (suffix != nullptr)
const auto suffix = storage->MapToRelativeUTF8(uri);
if (suffix.data() != nullptr)
/* this path was relative to the music
directory */
return LocatedUri(LocatedUri::Type::RELATIVE, suffix);
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
return {LocatedUri::Type::RELATIVE, suffix.data()};
}
#endif
if (client != nullptr)
client->AllowFile(path);
return LocatedUri(LocatedUri::Type::PATH, uri, std::move(path));
return {LocatedUri::Type::PATH, uri, std::move(path)};
}
static LocatedUri
......@@ -65,9 +67,13 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
{
switch (kind) {
case UriPluginKind::INPUT:
case UriPluginKind::STORAGE: // TODO: separate check for storage plugins
if (!uri_supported_scheme(uri))
throw std::runtime_error("Unsupported URI scheme");
throw std::invalid_argument("Unsupported URI scheme");
break;
case UriPluginKind::STORAGE:
/* plugin support will be checked after the
Storage::MapToRelativeUTF8() call */
break;
case UriPluginKind::PLAYLIST:
......@@ -80,13 +86,18 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
#ifdef ENABLE_DATABASE
if (storage != nullptr) {
const char *suffix = storage->MapToRelativeUTF8(uri);
if (suffix != nullptr)
return LocatedUri(LocatedUri::Type::RELATIVE, suffix);
const auto suffix = storage->MapToRelativeUTF8(uri);
if (suffix.data() != nullptr)
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
return {LocatedUri::Type::RELATIVE, suffix.data()};
}
if (kind == UriPluginKind::STORAGE &&
GetStoragePluginByUri(uri) == nullptr)
throw std::invalid_argument("Unsupported URI scheme");
#endif
return LocatedUri(LocatedUri::Type::ABSOLUTE, uri);
return {LocatedUri::Type::ABSOLUTE, uri};
}
LocatedUri
......@@ -101,7 +112,7 @@ LocateUri(UriPluginKind kind,
const char *path_utf8 = StringAfterPrefixCaseASCII(uri, "file://");
if (path_utf8 != nullptr) {
if (!PathTraitsUTF8::IsAbsolute(path_utf8))
throw std::runtime_error("Malformed file:// URI");
throw std::invalid_argument("Malformed file:// URI");
return LocateFileUri(path_utf8, client
#ifdef ENABLE_DATABASE
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -21,7 +21,6 @@
#define MPD_LOCATE_URI_HXX
#include "config.h"
#include "util/Compiler.h"
#include "fs/AllocatedPath.hxx"
#ifdef _WIN32
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -17,197 +17,35 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "LogV.hxx"
#include "Log.hxx"
#include "lib/fmt/ExceptionFormatter.hxx"
#include "util/Domain.hxx"
#include <exception>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fmt/format.h>
static constexpr Domain exception_domain("exception");
void
LogFormatV(const Domain &domain, LogLevel level,
const char *fmt, va_list ap) noexcept
{
char msg[1024];
vsnprintf(msg, sizeof(msg), fmt, ap);
Log(domain, level, msg);
}
void
LogFormat(const Domain &domain, LogLevel level, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, level, fmt, ap);
va_end(ap);
}
void
FormatDebug(const Domain &domain, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, LogLevel::DEBUG, fmt, ap);
va_end(ap);
}
void
FormatInfo(const Domain &domain, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, LogLevel::INFO, fmt, ap);
va_end(ap);
}
void
FormatDefault(const Domain &domain, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, LogLevel::DEFAULT, fmt, ap);
va_end(ap);
}
void
FormatWarning(const Domain &domain, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, LogLevel::WARNING, fmt, ap);
va_end(ap);
}
void
FormatError(const Domain &domain, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
LogFormatV(domain, LogLevel::ERROR, fmt, ap);
va_end(ap);
}
void
LogError(const std::exception &e) noexcept
{
Log(exception_domain, LogLevel::ERROR, e.what());
try {
std::rethrow_if_nested(e);
} catch (const std::exception &nested) {
LogError(nested, "nested");
} catch (...) {
Log(exception_domain, LogLevel::ERROR,
"Unrecognized nested exception");
}
}
void
LogError(const std::exception &e, const char *msg) noexcept
LogVFmt(LogLevel level, const Domain &domain,
fmt::string_view format_str, fmt::format_args args) noexcept
{
FormatError(exception_domain, "%s: %s", msg, e.what());
try {
std::rethrow_if_nested(e);
} catch (const std::exception &nested) {
LogError(nested);
} catch (...) {
Log(exception_domain, LogLevel::ERROR,
"Unrecognized nested exception");
}
fmt::memory_buffer buffer;
#if FMT_VERSION >= 80000
fmt::vformat_to(std::back_inserter(buffer), format_str, args);
#else
fmt::vformat_to(buffer, format_str, args);
#endif
Log(level, domain, {buffer.data(), buffer.size()});
}
void
FormatError(const std::exception &e, const char *fmt, ...) noexcept
Log(LogLevel level, const std::exception_ptr &ep) noexcept
{
char msg[1024];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
LogError(e, msg);
Log(level, exception_domain, GetFullMessage(ep));
}
void
LogError(const std::exception_ptr &ep) noexcept
Log(LogLevel level, const std::exception_ptr &ep, const char *msg) noexcept
{
try {
std::rethrow_exception(ep);
} catch (const std::exception &e) {
LogError(e);
} catch (...) {
Log(exception_domain, LogLevel::ERROR,
"Unrecognized exception");
}
}
void
LogError(const std::exception_ptr &ep, const char *msg) noexcept
{
try {
std::rethrow_exception(ep);
} catch (const std::exception &e) {
LogError(e, msg);
} catch (...) {
FormatError(exception_domain,
"%s: Unrecognized exception", msg);
}
}
void
FormatError(const std::exception_ptr &ep, const char *fmt, ...) noexcept
{
char msg[1024];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
LogError(ep, msg);
}
void
LogErrno(const Domain &domain, int e, const char *msg) noexcept
{
LogFormat(domain, LogLevel::ERROR, "%s: %s", msg, strerror(e));
}
void
LogErrno(const Domain &domain, const char *msg) noexcept
{
LogErrno(domain, errno, msg);
}
static void
FormatErrnoV(const Domain &domain, int e, const char *fmt, va_list ap) noexcept
{
char msg[1024];
vsnprintf(msg, sizeof(msg), fmt, ap);
LogErrno(domain, e, msg);
}
void
FormatErrno(const Domain &domain, int e, const char *fmt, ...) noexcept
{
va_list ap;
va_start(ap, fmt);
FormatErrnoV(domain, e, fmt, ap);
va_end(ap);
}
void
FormatErrno(const Domain &domain, const char *fmt, ...) noexcept
{
const int e = errno;
va_list ap;
va_start(ap, fmt);
FormatErrnoV(domain, e, fmt, ap);
va_end(ap);
LogFmt(level, exception_domain, "{}: {}", msg, ep);
}
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -21,101 +21,130 @@
#define MPD_LOG_HXX
#include "LogLevel.hxx"
#include "util/Compiler.h"
#include <fmt/core.h>
#if FMT_VERSION < 70000 || FMT_VERSION >= 80000
#include <fmt/format.h>
#endif
#include <exception>
#include <string_view>
#include <utility>
class Domain;
void
Log(const Domain &domain, LogLevel level, const char *msg) noexcept;
Log(LogLevel level, const Domain &domain, std::string_view msg) noexcept;
gcc_printf(3,4)
void
LogFormat(const Domain &domain, LogLevel level, const char *fmt, ...) noexcept;
LogVFmt(LogLevel level, const Domain &domain,
fmt::string_view format_str, fmt::format_args args) noexcept;
static inline void
LogDebug(const Domain &domain, const char *msg) noexcept
template<typename S, typename... Args>
void
LogFmt(LogLevel level, const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
Log(domain, LogLevel::DEBUG, msg);
#if FMT_VERSION >= 90000
return LogVFmt(level, domain, format_str,
fmt::make_format_args(args...));
#elif FMT_VERSION >= 70000
return LogVFmt(level, domain, fmt::to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str,
args...));
#else
/* expensive fallback for older libfmt versions */
const auto result = fmt::format(format_str, args...);
return Log(level, domain, result);
#endif
}
gcc_printf(2,3)
template<typename S, typename... Args>
void
FormatDebug(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogInfo(const Domain &domain, const char *msg) noexcept
FmtDebug(const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
Log(domain, LogLevel::INFO, msg);
LogFmt(LogLevel::DEBUG, domain, format_str, args...);
}
gcc_printf(2,3)
template<typename S, typename... Args>
void
FormatInfo(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogDefault(const Domain &domain, const char *msg) noexcept
FmtInfo(const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
Log(domain, LogLevel::DEFAULT, msg);
LogFmt(LogLevel::INFO, domain, format_str, args...);
}
gcc_printf(2,3)
template<typename S, typename... Args>
void
FormatDefault(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogWarning(const Domain &domain, const char *msg) noexcept
FmtNotice(const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
Log(domain, LogLevel::WARNING, msg);
LogFmt(LogLevel::NOTICE, domain, format_str, args...);
}
gcc_printf(2,3)
template<typename S, typename... Args>
void
FormatWarning(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogError(const Domain &domain, const char *msg) noexcept
FmtWarning(const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
Log(domain, LogLevel::ERROR, msg);
LogFmt(LogLevel::WARNING, domain, format_str, args...);
}
template<typename S, typename... Args>
void
LogError(const std::exception &e) noexcept;
void
LogError(const std::exception &e, const char *msg) noexcept;
FmtError(const Domain &domain,
const S &format_str, Args&&... args) noexcept
{
LogFmt(LogLevel::ERROR, domain, format_str, args...);
}
gcc_printf(2,3)
void
FormatError(const std::exception &e, const char *fmt, ...) noexcept;
Log(LogLevel level, const std::exception_ptr &ep) noexcept;
void
LogError(const std::exception_ptr &ep) noexcept;
Log(LogLevel level, const std::exception_ptr &ep, const char *msg) noexcept;
void
LogError(const std::exception_ptr &ep, const char *msg) noexcept;
static inline void
LogDebug(const Domain &domain, const char *msg) noexcept
{
Log(LogLevel::DEBUG, domain, msg);
}
gcc_printf(2,3)
void
FormatError(const std::exception_ptr &ep, const char *fmt, ...) noexcept;
static inline void
LogInfo(const Domain &domain, const char *msg) noexcept
{
Log(LogLevel::INFO, domain, msg);
}
gcc_printf(2,3)
void
FormatError(const Domain &domain, const char *fmt, ...) noexcept;
static inline void
LogNotice(const Domain &domain, const char *msg) noexcept
{
Log(LogLevel::NOTICE, domain, msg);
}
void
LogErrno(const Domain &domain, int e, const char *msg) noexcept;
static inline void
LogWarning(const Domain &domain, const char *msg) noexcept
{
Log(LogLevel::WARNING, domain, msg);
}
void
LogErrno(const Domain &domain, const char *msg) noexcept;
static inline void
LogError(const Domain &domain, const char *msg) noexcept
{
Log(LogLevel::ERROR, domain, msg);
}
gcc_printf(3,4)
void
FormatErrno(const Domain &domain, int e, const char *fmt, ...) noexcept;
inline void
LogError(const std::exception_ptr &ep) noexcept
{
Log(LogLevel::ERROR, ep);
}
gcc_printf(2,3)
void
FormatErrno(const Domain &domain, const char *fmt, ...) noexcept;
inline void
LogError(const std::exception_ptr &ep, const char *msg) noexcept
{
Log(LogLevel::ERROR, ep, msg);
}
#endif /* LOG_H */
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -19,11 +19,14 @@
#include "LogBackend.hxx"
#include "Log.hxx"
#include "util/Compiler.h"
#include "util/Domain.hxx"
#include "util/StringStrip.hxx"
#include "Version.h"
#include "config.h"
#include <assert.h>
#include <cassert>
#include <stdio.h>
#include <string.h>
#include <time.h>
......@@ -45,7 +48,7 @@ ToAndroidLogLevel(LogLevel log_level) noexcept
return ANDROID_LOG_DEBUG;
case LogLevel::INFO:
case LogLevel::DEFAULT:
case LogLevel::NOTICE:
return ANDROID_LOG_INFO;
case LogLevel::WARNING:
......@@ -61,7 +64,7 @@ ToAndroidLogLevel(LogLevel log_level) noexcept
#else
static LogLevel log_threshold = LogLevel::INFO;
static LogLevel log_threshold = LogLevel::NOTICE;
static bool enable_timestamp;
......@@ -101,15 +104,14 @@ log_date() noexcept
* characters.
*/
static int
chomp_length(const char *p) noexcept
chomp_length(std::string_view p) noexcept
{
size_t length = strlen(p);
return StripRight(p, length);
return StripRight(p.data(), p.size());
}
#ifdef HAVE_SYSLOG
gcc_const
[[gnu::const]]
static int
ToSysLogLevel(LogLevel log_level) noexcept
{
......@@ -120,7 +122,7 @@ ToSysLogLevel(LogLevel log_level) noexcept
case LogLevel::INFO:
return LOG_INFO;
case LogLevel::DEFAULT:
case LogLevel::NOTICE:
return LOG_NOTICE;
case LogLevel::WARNING:
......@@ -135,11 +137,11 @@ ToSysLogLevel(LogLevel log_level) noexcept
}
static void
SysLog(const Domain &domain, LogLevel log_level, const char *message) noexcept
SysLog(const Domain &domain, LogLevel log_level, std::string_view message) noexcept
{
syslog(ToSysLogLevel(log_level), "%s: %.*s",
domain.GetName(),
chomp_length(message), message);
chomp_length(message), message.data());
}
void
......@@ -159,12 +161,12 @@ LogFinishSysLog() noexcept
#endif
static void
FileLog(const Domain &domain, const char *message) noexcept
FileLog(const Domain &domain, std::string_view message) noexcept
{
fprintf(stderr, "%s%s: %.*s\n",
enable_timestamp ? log_date() : "",
domain.GetName(),
chomp_length(message), message);
chomp_length(message), message.data());
#ifdef _WIN32
/* force-flush the log file, because setvbuf() does not seem
......@@ -176,14 +178,20 @@ FileLog(const Domain &domain, const char *message) noexcept
#endif /* !ANDROID */
void
Log(const Domain &domain, LogLevel level, const char *msg) noexcept
Log(LogLevel level, const Domain &domain, std::string_view msg) noexcept
{
#ifdef ANDROID
__android_log_print(ToAndroidLogLevel(level), "MPD",
"%s: %s", domain.GetName(), msg);
if (logListener != nullptr)
"%s: %.*s", domain.GetName(),
(int)msg.size(), msg.data());
if (logListener != nullptr) {
char buffer[1024];
snprintf(buffer, sizeof(buffer), "%s: %.*s",
domain.GetName(), (int)msg.size(), msg.data());
logListener->OnLog(Java::GetEnv(), ToAndroidLogLevel(level),
"%s: %s", domain.GetName(), msg);
buffer);
}
#else
if (level < log_threshold)
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -28,24 +28,24 @@
#include "fs/FileSystem.hxx"
#include "util/Domain.hxx"
#include "util/RuntimeError.hxx"
#include "util/StringAPI.hxx"
#include "system/Error.hxx"
#include <cassert>
#ifdef ENABLE_SYSTEMD_DAEMON
#include <systemd/sd-daemon.h>
#endif
#include <assert.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#define LOG_LEVEL_SECURE LogLevel::INFO
#define LOG_DATE_BUF_SIZE 16
#define LOG_DATE_LEN (LOG_DATE_BUF_SIZE - 1)
gcc_unused
[[maybe_unused]]
static constexpr Domain log_domain("log");
#ifndef ANDROID
......@@ -63,7 +63,7 @@ static void redirect_logs(int fd)
}
static int
open_log_file(void)
open_log_file()
{
assert(!out_path.IsNull());
......@@ -93,17 +93,24 @@ log_init_file(int line)
}
static inline LogLevel
parse_log_level(const char *value, int line)
parse_log_level(const char *value)
{
if (0 == strcmp(value, "default"))
return LogLevel::DEFAULT;
if (0 == strcmp(value, "secure"))
return LOG_LEVEL_SECURE;
else if (0 == strcmp(value, "verbose"))
if (StringIsEqual(value, "notice") ||
/* deprecated name: */
StringIsEqual(value, "default"))
return LogLevel::NOTICE;
else if (StringIsEqual(value, "info") ||
/* deprecated since MPD 0.22: */
StringIsEqual(value, "secure"))
return LogLevel::INFO;
else if (StringIsEqual(value, "verbose"))
return LogLevel::DEBUG;
else if (StringIsEqual(value, "warning"))
return LogLevel::WARNING;
else if (StringIsEqual(value, "error"))
return LogLevel::ERROR;
else
throw FormatRuntimeError("unknown log level \"%s\" at line %d",
value, line);
throw FormatRuntimeError("unknown log level \"%s\"", value);
}
#endif
......@@ -132,9 +139,12 @@ log_init(const ConfigData &config, bool verbose, bool use_stdout)
#else
if (verbose)
SetLogThreshold(LogLevel::DEBUG);
else if (const auto &param = config.GetParam(ConfigOption::LOG_LEVEL))
SetLogThreshold(parse_log_level(param->value.c_str(),
param->line));
else
SetLogThreshold(config.With(ConfigOption::LOG_LEVEL, [](const char *s){
return s != nullptr
? parse_log_level(s)
: LogLevel::NOTICE;
}));
if (use_stdout) {
out_fd = STDOUT_FILENO;
......@@ -157,7 +167,7 @@ log_init(const ConfigData &config, bool verbose, bool use_stdout)
throw std::runtime_error("config parameter 'log_file' not found");
#endif
#ifdef HAVE_SYSLOG
} else if (strcmp(param->value.c_str(), "syslog") == 0) {
} else if (StringIsEqual(param->value.c_str(), "syslog")) {
LogInitSysLog();
#endif
} else {
......@@ -224,22 +234,22 @@ cycle_log_files() noexcept
if (out_path.IsNull())
return 0;
FormatDebug(log_domain, "Cycling log files");
LogDebug(log_domain, "Cycling log files");
close_log_files();
fd = open_log_file();
if (fd < 0) {
const std::string out_path_utf8 = out_path.ToUTF8();
FormatError(log_domain,
"error re-opening log file: %s",
out_path_utf8.c_str());
FmtError(log_domain,
"error re-opening log file: {}",
out_path_utf8);
return -1;
}
redirect_logs(fd);
close(fd);
FormatDebug(log_domain, "Done cycling log files");
LogDebug(log_domain, "Done cycling log files");
return 0;
#endif
}
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -42,7 +42,7 @@ enum class LogLevel {
/**
* Interesting informational message.
*/
DEFAULT,
NOTICE,
/**
* Warning: something may be wrong.
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -20,18 +20,16 @@
#ifndef MPD_MAIN_HXX
#define MPD_MAIN_HXX
class EventLoop;
class Context;
struct Instance;
#ifdef ANDROID
#include "android/LogListener.hxx"
extern Context *context;
extern class Context *context;
extern LogListener *logListener;
#endif
extern Instance *instance;
extern Instance *global_instance;
#ifndef ANDROID
......@@ -42,7 +40,7 @@ extern Instance *instance;
* after doing some initialization.
*/
int
mpd_main(int argc, char *argv[]) noexcept;
mpd_main(int argc, char *argv[]);
#endif
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -33,7 +33,7 @@
#include "Main.hxx"
#endif
#include <assert.h>
#include <cassert>
/**
* The absolute path of the playlist directory encoded in the
......@@ -66,10 +66,10 @@ map_uri_fs(const char *uri) noexcept
assert(uri != nullptr);
assert(*uri != '/');
if (instance->storage == nullptr)
if (global_instance->storage == nullptr)
return nullptr;
const auto music_dir_fs = instance->storage->MapFS("");
const auto music_dir_fs = global_instance->storage->MapFS("");
if (music_dir_fs.IsNull())
return nullptr;
......@@ -84,16 +84,16 @@ std::string
map_fs_to_utf8(Path path_fs) noexcept
{
if (path_fs.IsAbsolute()) {
if (instance->storage == nullptr)
return std::string();
if (global_instance->storage == nullptr)
return {};
const auto music_dir_fs = instance->storage->MapFS("");
const auto music_dir_fs = global_instance->storage->MapFS("");
if (music_dir_fs.IsNull())
return std::string();
return {};
auto relative = music_dir_fs.Relative(path_fs);
if (relative == nullptr || StringIsEmpty(relative))
return std::string();
return {};
path_fs = Path::FromFS(relative);
}
......@@ -119,7 +119,7 @@ map_spl_utf8_to_fs(const char *name) noexcept
filename_utf8.append(PLAYLIST_FILE_SUFFIX);
const auto filename_fs =
AllocatedPath::FromUTF8(filename_utf8.c_str());
AllocatedPath::FromUTF8(filename_utf8);
if (filename_fs.IsNull())
return nullptr;
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -24,7 +24,6 @@
#ifndef MPD_MAPPER_HXX
#define MPD_MAPPER_HXX
#include "util/Compiler.h"
#include "config.h"
#include <string>
......@@ -44,7 +43,7 @@ mapper_init(AllocatedPath &&playlist_dir);
* is basically done by converting the URI to the file system charset
* and prepending the music directory.
*/
gcc_pure
[[gnu::pure]]
AllocatedPath
map_uri_fs(const char *uri) noexcept;
......@@ -56,7 +55,7 @@ map_uri_fs(const char *uri) noexcept;
* @return the relative path in UTF-8, or an empty string if mapping
* failed
*/
gcc_pure
[[gnu::pure]]
std::string
map_fs_to_utf8(Path path_fs) noexcept;
......@@ -65,7 +64,7 @@ map_fs_to_utf8(Path path_fs) noexcept;
/**
* Returns the playlist directory.
*/
gcc_const
[[gnu::const]]
const AllocatedPath &
map_spl_path() noexcept;
......@@ -75,7 +74,7 @@ map_spl_path() noexcept;
*
* @return the path in file system encoding, or nullptr if mapping failed
*/
gcc_pure
[[gnu::pure]]
AllocatedPath
map_spl_utf8_to_fs(const char *name) noexcept;
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -20,7 +20,7 @@
#include "MusicBuffer.hxx"
#include "MusicChunk.hxx"
#include <assert.h>
#include <cassert>
MusicBuffer::MusicBuffer(unsigned num_chunks)
:buffer(num_chunks) {
......@@ -29,8 +29,8 @@ MusicBuffer::MusicBuffer(unsigned num_chunks)
MusicChunkPtr
MusicBuffer::Allocate() noexcept
{
const std::lock_guard<Mutex> protect(mutex);
return MusicChunkPtr(buffer.Allocate(), MusicChunkDeleter(*this));
const std::scoped_lock<Mutex> protect(mutex);
return {buffer.Allocate(), MusicChunkDeleter(*this)};
}
void
......@@ -44,7 +44,7 @@ MusicBuffer::Return(MusicChunk *chunk) noexcept
chunk->next.reset();
chunk->other.reset();
const std::lock_guard<Mutex> protect(mutex);
const std::scoped_lock<Mutex> protect(mutex);
assert(!chunk->other || !chunk->other->other);
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -54,7 +54,7 @@ public:
#endif
bool IsFull() const noexcept {
const std::lock_guard<Mutex> protect(mutex);
const std::scoped_lock<Mutex> protect(mutex);
return buffer.IsFull();
}
......@@ -63,7 +63,7 @@ public:
* is the same value which was passed to the constructor
* music_buffer_new().
*/
gcc_pure
[[gnu::pure]]
unsigned GetSize() const noexcept {
return buffer.GetCapacity();
}
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -18,10 +18,10 @@
*/
#include "MusicChunk.hxx"
#include "AudioFormat.hxx"
#include "pcm/AudioFormat.hxx"
#include "tag/Tag.hxx"
#include <assert.h>
#include <cassert>
MusicChunkInfo::MusicChunkInfo() noexcept = default;
MusicChunkInfo::~MusicChunkInfo() noexcept = default;
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -26,14 +26,13 @@
#include "util/WritableBuffer.hxx"
#ifndef NDEBUG
#include "AudioFormat.hxx"
#include "pcm/AudioFormat.hxx"
#endif
#include <cstddef>
#include <cstdint>
#include <memory>
#include <stdint.h>
#include <stddef.h>
static constexpr size_t CHUNK_SIZE = 4096;
struct AudioFormat;
......@@ -43,15 +42,7 @@ struct MusicChunk;
/**
* Meta information for #MusicChunk.
*/
struct alignas(8) MusicChunkInfo {
/* align to multiple of 8 bytes, which adds padding at the
end, so the size of MusicChunk::data is also a multiple of
8 bytes; this is a workaround for a bug in the DSD_U32 and
DoP converters which require processing 8 bytes at a time,
discarding the remainder */
/* TODO: once all converters have been fixed, we should remove
this workaround */
struct MusicChunkInfo {
/** the next chunk in a linked list */
MusicChunkPtr next;
......@@ -114,7 +105,7 @@ struct alignas(8) MusicChunkInfo {
* Checks if the audio format if the chunk is equal to the
* specified audio_format.
*/
gcc_pure
[[gnu::pure]]
bool CheckFormat(AudioFormat audio_format) const noexcept;
#endif
};
......@@ -127,10 +118,6 @@ struct MusicChunk : MusicChunkInfo {
/** the data (probably PCM) */
uint8_t data[CHUNK_SIZE - sizeof(MusicChunkInfo)];
/* TODO: remove this check once all converters have been fixed
(see comment in struct MusicChunkInfo for details) */
static_assert(sizeof(data) % 8 == 0, "Wrong alignment");
/**
* Prepares appending to the music chunk. Returns a buffer
* where you may write into. After you are finished, call
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -20,12 +20,14 @@
#include "MusicPipe.hxx"
#include "MusicChunk.hxx"
#include <cassert>
#ifndef NDEBUG
bool
MusicPipe::Contains(const MusicChunk *chunk) const noexcept
{
const std::lock_guard<Mutex> protect(mutex);
const std::scoped_lock<Mutex> protect(mutex);
for (const MusicChunk *i = head.get(); i != nullptr; i = i->next.get())
if (i == chunk)
......@@ -39,7 +41,7 @@ MusicPipe::Contains(const MusicChunk *chunk) const noexcept
MusicChunkPtr
MusicPipe::Shift() noexcept
{
const std::lock_guard<Mutex> protect(mutex);
const std::scoped_lock<Mutex> protect(mutex);
auto chunk = std::move(head);
if (chunk != nullptr) {
......@@ -79,7 +81,7 @@ MusicPipe::Push(MusicChunkPtr chunk) noexcept
assert(!chunk->IsEmpty());
assert(chunk->length == 0 || chunk->audio_format.IsValid());
const std::lock_guard<Mutex> protect(mutex);
const std::scoped_lock<Mutex> protect(mutex);
assert(size > 0 || !audio_format.IsDefined());
assert(!audio_format.IsDefined() ||
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -22,14 +22,11 @@
#include "MusicChunkPtr.hxx"
#include "thread/Mutex.hxx"
#include "util/Compiler.h"
#ifndef NDEBUG
#include "AudioFormat.hxx"
#include "pcm/AudioFormat.hxx"
#endif
#include <assert.h>
/**
* A queue of #MusicChunk objects. One party appends chunks at the
* tail, and the other consumes them from the head.
......@@ -61,7 +58,7 @@ public:
* Checks if the audio format if the chunk is equal to the specified
* audio_format.
*/
gcc_pure
[[gnu::pure]]
bool CheckFormat(AudioFormat other) const noexcept {
return !audio_format.IsDefined() ||
audio_format == other;
......@@ -70,7 +67,7 @@ public:
/**
* Checks if the specified chunk is enqueued in the music pipe.
*/
gcc_pure
[[gnu::pure]]
bool Contains(const MusicChunk *chunk) const noexcept;
#endif
......@@ -78,9 +75,9 @@ public:
* Returns the first #MusicChunk from the pipe. Returns
* nullptr if the pipe is empty.
*/
gcc_pure
[[gnu::pure]]
const MusicChunk *Peek() const noexcept {
const std::lock_guard<Mutex> protect(mutex);
const std::scoped_lock<Mutex> protect(mutex);
return head.get();
}
......@@ -102,13 +99,13 @@ public:
/**
* Returns the number of chunks currently in this pipe.
*/
gcc_pure
[[gnu::pure]]
unsigned GetSize() const noexcept {
const std::lock_guard<Mutex> protect(mutex);
const std::scoped_lock<Mutex> protect(mutex);
return size;
}
gcc_pure
[[gnu::pure]]
bool IsEmpty() const noexcept {
return GetSize() == 0;
}
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -20,24 +20,34 @@
#include "config.h"
#include "Partition.hxx"
#include "Instance.hxx"
#include "Log.hxx"
#include "lib/fmt/ExceptionFormatter.hxx"
#include "song/DetachedSong.hxx"
#include "mixer/Volume.hxx"
#include "IdleFlags.hxx"
#include "client/Listener.hxx"
#include "client/Client.hxx"
#include "input/cache/Manager.hxx"
#include "util/Domain.hxx"
static constexpr Domain cache_domain("cache");
Partition::Partition(Instance &_instance,
const char *_name,
unsigned max_length,
unsigned buffer_chunks,
AudioFormat configured_audio_format,
const ReplayGainConfig &replay_gain_config)
const ReplayGainConfig &replay_gain_config) noexcept
:instance(_instance),
name(_name),
listener(new ClientListener(instance.event_loop, *this)),
idle_monitor(instance.event_loop, BIND_THIS_METHOD(OnIdleMonitor)),
global_events(instance.event_loop, BIND_THIS_METHOD(OnGlobalEvent)),
playlist(max_length, *this),
outputs(*this),
pc(*this, outputs, buffer_chunks,
outputs(pc, *this),
pc(*this, outputs,
instance.input_cache.get(),
buffer_chunks,
configured_audio_format, replay_gain_config)
{
UpdateEffectiveReplayGainMode();
......@@ -46,13 +56,52 @@ Partition::Partition(Instance &_instance,
Partition::~Partition() noexcept = default;
void
Partition::EmitIdle(unsigned mask)
Partition::BeginShutdown() noexcept
{
pc.Kill();
listener.reset();
}
static void
PrefetchSong(InputCacheManager &cache, const char *uri) noexcept
{
instance.EmitIdle(mask);
if (cache.Contains(uri))
return;
FmtDebug(cache_domain, "Prefetch '{}'", uri);
try {
cache.Prefetch(uri);
} catch (...) {
FmtError(cache_domain,
"Prefetch '{}' failed: {}",
uri, std::current_exception());
}
}
static void
PrefetchSong(InputCacheManager &cache, const DetachedSong &song) noexcept
{
PrefetchSong(cache, song.GetURI());
}
inline void
Partition::PrefetchQueue() noexcept
{
if (!instance.input_cache)
return;
auto &cache = *instance.input_cache;
int next = playlist.GetNextPosition();
if (next >= 0)
PrefetchSong(cache, playlist.queue.Get(next));
// TODO: prefetch more songs
}
void
Partition::UpdateEffectiveReplayGainMode()
Partition::UpdateEffectiveReplayGainMode() noexcept
{
auto mode = replay_gain_mode;
if (mode == ReplayGainMode::AUTO)
......@@ -68,7 +117,7 @@ Partition::UpdateEffectiveReplayGainMode()
#ifdef ENABLE_DATABASE
const Database *
Partition::GetDatabase() const
Partition::GetDatabase() const noexcept
{
return instance.GetDatabase();
}
......@@ -80,7 +129,7 @@ Partition::GetDatabaseOrThrow() const
}
void
Partition::DatabaseModified(const Database &db)
Partition::DatabaseModified(const Database &db) noexcept
{
playlist.DatabaseModified(db);
EmitIdle(IDLE_DATABASE);
......@@ -89,7 +138,7 @@ Partition::DatabaseModified(const Database &db)
#endif
void
Partition::TagModified()
Partition::TagModified() noexcept
{
auto song = pc.LockReadTaggedSong();
if (song)
......@@ -103,31 +152,35 @@ Partition::TagModified(const char *uri, const Tag &tag) noexcept
}
void
Partition::SyncWithPlayer()
Partition::SyncWithPlayer() noexcept
{
playlist.SyncWithPlayer(pc);
/* TODO: invoke this function in batches, to let the hard disk
spin down in between */
PrefetchQueue();
}
void
Partition::BorderPause()
Partition::BorderPause() noexcept
{
playlist.BorderPause(pc);
}
void
Partition::OnQueueModified()
Partition::OnQueueModified() noexcept
{
EmitIdle(IDLE_PLAYLIST);
}
void
Partition::OnQueueOptionsChanged()
Partition::OnQueueOptionsChanged() noexcept
{
EmitIdle(IDLE_OPTIONS);
}
void
Partition::OnQueueSongStarted()
Partition::OnQueueSongStarted() noexcept
{
EmitIdle(IDLE_PLAYER);
}
......@@ -151,7 +204,7 @@ Partition::OnBorderPause() noexcept
}
void
Partition::OnMixerVolumeChanged(gcc_unused Mixer &mixer, gcc_unused int volume)
Partition::OnMixerVolumeChanged(Mixer &, int) noexcept
{
InvalidateHardwareVolume();
......@@ -160,7 +213,19 @@ Partition::OnMixerVolumeChanged(gcc_unused Mixer &mixer, gcc_unused int volume)
}
void
Partition::OnGlobalEvent(unsigned mask)
Partition::OnIdleMonitor(unsigned mask) noexcept
{
/* send "idle" notifications to all subscribed
clients */
for (auto &client : clients)
client.IdleAdd(mask);
if (mask & (IDLE_PLAYLIST|IDLE_PLAYER|IDLE_MIXER|IDLE_OUTPUT))
instance.OnStateModified();
}
void
Partition::OnGlobalEvent(unsigned mask) noexcept
{
if ((mask & SYNC_WITH_PLAYER) != 0)
SyncWithPlayer();
......
/*
* Copyright 2003-2018 The Music Player Daemon Project
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
......@@ -27,19 +27,23 @@
#include "mixer/Listener.hxx"
#include "player/Control.hxx"
#include "player/Listener.hxx"
#include "protocol/RangeArg.hxx"
#include "ReplayGainMode.hxx"
#include "SingleMode.hxx"
#include "Chrono.hxx"
#include "util/Compiler.h"
#include "config.h"
#include <boost/intrusive/list.hpp>
#include <string>
#include <memory>
struct Instance;
struct RangeArg;
class MultipleOutputs;
class SongLoader;
class ClientListener;
class Client;
/**
* A partition of the Music Player Daemon. It is a separate unit with
......@@ -56,6 +60,16 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
std::unique_ptr<ClientListener> listener;
boost::intrusive::list<Client,
boost::intrusive::base_hook<boost::intrusive::list_base_hook<boost::intrusive::tag<Partition>,
boost::intrusive::link_mode<boost::intrusive::normal_link>>>,
boost::intrusive::constant_time_size<false>> clients;
/**
* Monitor for idle events local to this partition.
*/
MaskMonitor idle_monitor;
MaskMonitor global_events;
struct playlist playlist;
......@@ -71,17 +85,34 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
unsigned max_length,
unsigned buffer_chunks,
AudioFormat configured_audio_format,
const ReplayGainConfig &replay_gain_config);
const ReplayGainConfig &replay_gain_config) noexcept;
~Partition() noexcept;
void EmitGlobalEvent(unsigned mask) {
void BeginShutdown() noexcept;
void EmitGlobalEvent(unsigned mask) noexcept {
global_events.OrMask(mask);
}
void EmitIdle(unsigned mask);
/**
* Emit an "idle" event to all clients of this partition.
*
* This method can be called from any thread.
*/
void EmitIdle(unsigned mask) noexcept {
idle_monitor.OrMask(mask);
}
/**
* Populate the #InputCacheManager with soon-to-be-played song
* files.
*
* Errors will be logged.
*/
void PrefetchQueue() noexcept;
void ClearQueue() {
void ClearQueue() noexcept {
playlist.Clear(pc);
}
......@@ -104,24 +135,20 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
* @param start the position of the first song to delete
* @param end the position after the last song to delete
*/
void DeleteRange(unsigned start, unsigned end) {
playlist.DeleteRange(pc, start, end);
void DeleteRange(RangeArg range) {
playlist.DeleteRange(pc, range);
}
void StaleSong(const char *uri) {
void StaleSong(const char *uri) noexcept {
playlist.StaleSong(pc, uri);
}
void Shuffle(unsigned start, unsigned end) {
playlist.Shuffle(pc, start, end);
}
void MoveRange(unsigned start, unsigned end, int to) {
playlist.MoveRange(pc, start, end, to);
void Shuffle(RangeArg range) {
playlist.Shuffle(pc, range);
}
void MoveId(unsigned id, int to) {
playlist.MoveId(pc, id, to);
void MoveRange(RangeArg range, unsigned to) {
playlist.MoveRange(pc, range, to);
}
void SwapPositions(unsigned song1, unsigned song2) {
......@@ -132,17 +159,15 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
playlist.SwapIds(pc, id1, id2);
}
void SetPriorityRange(unsigned start_position, unsigned end_position,
uint8_t priority) {
playlist.SetPriorityRange(pc, start_position, end_position,
priority);
void SetPriorityRange(RangeArg position_range, uint8_t priority) {
playlist.SetPriorityRange(pc, position_range, priority);
}
void SetPriorityId(unsigned song_id, uint8_t priority) {
playlist.SetPriorityId(pc, song_id, priority);
}
void Stop() {
void Stop() noexcept {
playlist.Stop(pc);
}
......@@ -174,27 +199,27 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
playlist.SeekCurrent(pc, seek_time, relative);
}
void SetRepeat(bool new_value) {
void SetRepeat(bool new_value) noexcept {
playlist.SetRepeat(pc, new_value);
}
bool GetRandom() const {
bool GetRandom() const noexcept {
return playlist.GetRandom();
}
void SetRandom(bool new_value) {
void SetRandom(bool new_value) noexcept {
playlist.SetRandom(pc, new_value);
}
void SetSingle(SingleMode new_value) {
void SetSingle(SingleMode new_value) noexcept {
playlist.SetSingle(pc, new_value);
}
void SetConsume(bool new_value) {
void SetConsume(bool new_value) noexcept {
playlist.SetConsume(new_value);
}
void SetReplayGainMode(ReplayGainMode mode) {
void SetReplayGainMode(ReplayGainMode mode) noexcept {
replay_gain_mode = mode;
UpdateEffectiveReplayGainMode();
}
......@@ -203,7 +228,7 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
* Publishes the effective #ReplayGainMode to all subsystems.
* #ReplayGainMode::AUTO is substituted.
*/
void UpdateEffectiveReplayGainMode();
void UpdateEffectiveReplayGainMode() noexcept;
#ifdef ENABLE_DATABASE
/**
......@@ -211,7 +236,7 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
* if this MPD configuration has no database (no
* music_directory was configured).
*/
const Database *GetDatabase() const;
const Database *GetDatabase() const noexcept;
const Database &GetDatabaseOrThrow() const;
......@@ -219,14 +244,14 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
* The database has been modified. Propagate the change to
* all subsystems.
*/
void DatabaseModified(const Database &db);
void DatabaseModified(const Database &db) noexcept;
#endif
/**
* A tag in the play queue has been modified by the player
* thread. Propagate the change to all subsystems.
*/
void TagModified();
void TagModified() noexcept;
/**
* The tag of the given song has been modified. Propagate the
......@@ -237,19 +262,19 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
/**
* Synchronize the player with the play queue.
*/
void SyncWithPlayer();
void SyncWithPlayer() noexcept;
/**
* Border pause has just been enabled. Change single mode to off
* if it was one-shot.
*/
void BorderPause();
void BorderPause() noexcept;
private:
/* virtual methods from class QueueListener */
void OnQueueModified() override;
void OnQueueOptionsChanged() override;
void OnQueueSongStarted() override;
void OnQueueModified() noexcept override;
void OnQueueOptionsChanged() noexcept override;
void OnQueueSongStarted() noexcept override;
/* virtual methods from class PlayerListener */
void OnPlayerSync() noexcept override;
......@@ -257,10 +282,13 @@ private:
void OnBorderPause() noexcept override;
/* virtual methods from class MixerListener */
void OnMixerVolumeChanged(Mixer &mixer, int volume) override;
void OnMixerVolumeChanged(Mixer &mixer, int volume) noexcept override;
/* callback for #idle_monitor */
void OnIdleMonitor(unsigned mask) noexcept;
/* callback for #global_events */
void OnGlobalEvent(unsigned mask);
void OnGlobalEvent(unsigned mask) noexcept;
};
#endif
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment