Compare commits

...

25 Commits

Author SHA1 Message Date
Max Kellermann
f084bf7872 release v0.23.4 2021-11-11 10:16:36 +01:00
Max Kellermann
1112d3907a Revert "systemd: add "RuntimeDirectory" directive"
This reverts commit 552c30eae4.

It has caused various problems; for example, MPD wasn't able to write
the pid_file (which was already mitigated by commit a4e4217204).

And apparently, the socket file created in the same directory by
mpd.socket disappears when mpd.service (re)creates the directory.  I
could not reproduce this problem with 247.3, but maybe this is a bug
in older systemd versions?

Until we figure out why this happens, let's remove the
RuntimeDirectory directive.  A future MPD version may be launched as
regular user, not as root, which will eliminate one major problem with
RuntimeDirectory.
2021-11-11 10:16:13 +01:00
Max Kellermann
3464497880 command/database: add optional position parameter to "searchaddpl"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1328
2021-11-11 09:52:49 +01:00
Max Kellermann
651f57bced command/playlist: save only if at least one song was added 2021-11-11 09:50:31 +01:00
Max Kellermann
b4e72aba6c command/playlist: move code to SearchInsertIntoPlaylist() 2021-11-11 09:40:41 +01:00
0xC0ncord
061dd2dfef output/plugins: fix build error with clang and -stdlib=libc++
This fixes this build error observed with clang and -stdlib=libc++:

../mpd-0.23.3/src/output/plugins/PipeWireOutputPlugin.cxx:661:55: error: implicit instantiation of undefined template 'std::array<std::byte, 64>'
        std::array<std::byte, MAX_CHANNELS * MAX_INTERLEAVE> buffer;
                                                             ^
/usr/include/c++/v1/__tuple:219:64: note: template is declared here
template <class _Tp, size_t _Size> struct _LIBCPP_TEMPLATE_VIS array;
                                                               ^
2021-11-10 15:35:56 -05:00
Max Kellermann
5f4ec7de5b decoder/ffmpeg, lib/ffmpeg: make AVCodec pointers "const"
For libavcodec 59 support.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1333
2021-11-09 21:09:14 +01:00
Max Audron
6f81bb4b09 upnp: add option to configure interface for db plugin
Add an option to the UPnP database plugin to configure which interface
is used by upnp to discover servers.

upnp by default selects the first interface that is not loopback, which
in some cases might not be the desired interface. For example if wanting
to access a DLNA server over a VPN connection.

The "interface" option can now be set to the name of the desired
interface to achieve this.

The default behaviour remains unchanged.
2021-11-08 23:04:07 +01:00
Max Audron
4ed60a5711 upnp: expose interface configuration on UpnpInit2()
Adds the Interface Name as an argument to the *Init functions to make it
possible to select which interface is used by upnp to detect servers.

Currently "nullptr" is passed in to let the upnp library select an
interface, as before.
2021-11-08 22:53:01 +01:00
Max Kellermann
c93195c94b NEWS: fix typo 2021-11-05 14:45:43 +01:00
Max Kellermann
f30adac4bb doc/mpdconf.example: add comments recommending not to use log_file and pid_file 2021-11-05 09:06:27 +01:00
Max Kellermann
a4e4217204 Main: ignore the "pid_file" setting if started as systemd service
Commit 552c30eae caused problems for those people who still had a
"pid_file" setting (even though that is obsolete with systemd),
because now /run/mpd is owned by root:root (our mpd.service has no
User=mpd directive, so systemd starts MPD as root).

To work around this problem, and to be able to keep
RuntimeDirectory=mpd (which solved a problem of other MPD users), the
best compromise seems to just ignore the "pid_file" setting when it is
of no use.
2021-11-05 09:02:56 +01:00
Max Kellermann
8754d705a1 CommandLine: rename struct options 2021-11-05 08:57:12 +01:00
Max Kellermann
23d4a2d6a5 Main: pass struct options by reference 2021-11-05 08:56:05 +01:00
Max Kellermann
ce77b148d9 CommandLine: add option --systemd
This way, MPD can reliably detect whether it was started as systemd
service, which is better than checking sd_booted(), which only checks
whether systemd manages all services, but still MPD could be started
manually.
2021-11-05 08:51:49 +01:00
Max Kellermann
be3eca39e8 NEWS: add missing lines 2021-11-04 17:59:02 +01:00
Max Kellermann
3413b1aeb4 output/alsa: add option thesycon_dsd_workaround 2021-11-04 17:55:53 +01:00
Max Kellermann
356d13e9dd lib/alsa/HwSetup: add missing include 2021-11-04 17:55:15 +01:00
Max Kellermann
fa34bf0aaf Merge branch 'feature/win32-disable-openmpt123' of git://github.com/ibmibmibm/MPD 2021-11-04 15:11:26 +01:00
Max Kellermann
5d0941476a lib/alsa/Error: a std::system_error category for libasound errors 2021-11-04 14:59:00 +01:00
Max Kellermann
5ff0bbd0f8 lib/fmt/AudioFormatFormatter: add formatter for SampleFormat 2021-11-04 14:55:01 +01:00
Shen-Ta Hsieh
a3764e533c python/build/libs.py: disable building libopenmpt cli
Signed-off-by: Shen-Ta Hsieh <ibmibmibm.tw@gmail.com>
2021-11-04 21:54:12 +08:00
Shen-Ta Hsieh
3e05cba30e python/build/libs.py: update libopenmpt configure flags
Signed-off-by: Shen-Ta Hsieh <ibmibmibm.tw@gmail.com>
2021-11-04 15:23:24 +08:00
Max Kellermann
14b3c0f0af event/Loop: destruct the Uring::Manager in the destructor before assert()
Fixes assertion failure when the EventLoop gets destructed before
Run() was ever called.

Fixes https://bugs.debian.org/998310
2021-11-03 18:32:14 +01:00
Max Kellermann
67aff05051 increment version number to 0.23.4 2021-10-31 18:17:35 +01:00
37 changed files with 490 additions and 141 deletions

14
NEWS

@@ -1,3 +1,17 @@
ver 0.23.4 (2021/11/11)
* protocol
- add optional position parameter to "searchaddpl"
* decoder
- ffmpeg: support libavcodec 59
* output
- alsa: add option "thesycon_dsd_workaround" to work around device bug
* fix crash on debug builds if startup fails
* systemd
- remove "RuntimeDirectory" directive because it caused problems
- ignore the "pid_file" setting if started as systemd service
* Windows
- enable the "openmpt" decoder plugin
ver 0.23.3 (2021/10/31)
* protocol
- add optional position parameter to "add" and "playlistadd"

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="63"
android:versionName="0.23.3">
android:versionCode="64"
android:versionName="0.23.4">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>

@@ -38,7 +38,7 @@ author = 'Max Kellermann'
# built documents.
#
# The short X.Y version.
version = '0.23.3'
version = '0.23.4'
# The full version, including alpha/beta/rc tags.
#release = version + '~git'

@@ -26,22 +26,25 @@
# 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

@@ -61,6 +61,15 @@ upnp
Provides access to UPnP media servers.
.. list-table::
:widths: 20 80
:header-rows: 1
* - Setting
- Description
* - **interface**
- Interface used to discover media servers. Decided by upnp if left unconfigured.
Storage plugins
===============
@@ -841,6 +850,11 @@ The `Advanced Linux Sound Architecture (ALSA) <http://www.alsa-project.org/>`_ p
("stop" or "pause") in DSD mode (native DSD or DoP). This is a
workaround for some DACs which emit noise when stopping DSD
playback.
* - **thesycon_dsd_workaround yes|no**
- If enabled, enables a workaround for a bug in Thesycon USB
audio receivers. On these devices, playing DSD512 or PCM
causes all subsequent attempts to play other DSD rates to fail,
which can be fixed by briefly playing PCM at 44.1 kHz.
* - **allowed_formats F1 F2 ...**
- Specifies a list of allowed audio formats, separated by a space. All items may contain asterisks as a wild card, and may be followed by "=dop" to enable DoP (DSD over PCM) for this particular format. The first matching format is used, and if none matches, MPD chooses the best fallback of this list.

@@ -1225,7 +1225,7 @@ The music database
.. _command_searchaddpl:
:command:`searchaddpl {NAME} {FILTER} [sort {TYPE}] [window {START:END}]`
:command:`searchaddpl {NAME} {FILTER} [sort {TYPE}] [window {START:END}] [position POS]`
Search the database for songs matching
``FILTER`` (see :ref:`Filters <filter_syntax>`) and add them to
the playlist named ``NAME``.
@@ -1234,6 +1234,9 @@ The music database
Parameters have the same meaning as for :ref:`search <command_search>`.
The ``position`` parameter specifies where the songs will be
inserted. [#since_0_23_4]_
.. _command_update:
:command:`update [URI]`
@@ -1655,3 +1658,4 @@ client-to-client messages are local to the current partition.
.. [#since_0_23] Since :program:`MPD` 0.23
.. [#since_0_23_1] Since :program:`MPD` 0.23.1
.. [#since_0_23_3] Since :program:`MPD` 0.23.3
.. [#since_0_23_4] Since :program:`MPD` 0.23.4

@@ -1,7 +1,7 @@
project(
'mpd',
['c', 'cpp'],
version: '0.23.3',
version: '0.23.4',
meson_version: '>= 0.56.0',
default_options: [
'c_std=c11',
@@ -44,7 +44,7 @@ version_conf = configuration_data()
version_conf.set_quoted('PACKAGE', meson.project_name())
version_conf.set_quoted('PACKAGE_NAME', meson.project_name())
version_conf.set_quoted('VERSION', meson.project_version())
version_conf.set_quoted('PROTOCOL_VERSION', '0.23.3')
version_conf.set_quoted('PROTOCOL_VERSION', '0.23.4')
configure_file(output: 'Version.h', configuration: version_conf)
conf = configuration_data()

@@ -116,8 +116,12 @@ libopenmpt = AutotoolsProject(
'892aea7a599b5d21842bebf463b5aafdad5711be7008dd84401920c6234820af',
'lib/libopenmpt.a',
[
'--disable-shared', '--enable-static'
'--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(

@@ -86,6 +86,9 @@ enum Option {
OPTION_KILL,
OPTION_NO_CONFIG,
OPTION_NO_DAEMON,
#ifdef __linux__
OPTION_SYSTEMD,
#endif
OPTION_STDOUT,
OPTION_STDERR,
OPTION_VERBOSE,
@@ -98,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"},
@@ -328,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;
@@ -349,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;

@@ -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

@@ -142,14 +142,24 @@ struct Config {
#ifdef ENABLE_DAEMON
static void
glue_daemonize_init(const struct options *options,
glue_daemonize_init(const CommandLineOptions &options,
const ConfigData &config)
{
auto pid_file = config.GetPath(ConfigOption::PID_FILE);
#ifdef __linux__
if (options.systemd && pid_file != nullptr) {
pid_file = nullptr;
fprintf(stderr,
"Ignoring the 'pid_file' setting in systemd mode\n");
}
#endif
daemonize_init(config.GetString(ConfigOption::USER),
config.GetString(ConfigOption::GROUP),
config.GetPath(ConfigOption::PID_FILE));
std::move(pid_file));
if (options->kill)
if (options.kill)
daemonize_kill();
}
@@ -361,7 +371,8 @@ Instance::BeginShutdownPartitions() noexcept
}
static inline void
MainConfigured(const struct options &options, const ConfigData &raw_config)
MainConfigured(const CommandLineOptions &options,
const ConfigData &raw_config)
{
#ifdef ENABLE_DAEMON
daemonize_close_stdin();
@@ -384,7 +395,7 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
const Config config(raw_config);
#ifdef ENABLE_DAEMON
glue_daemonize_init(&options, raw_config);
glue_daemonize_init(options, raw_config);
#endif
TagLoadConfig(raw_config);
@@ -582,7 +593,7 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
static void
AndroidMain()
{
struct options options;
CommandLineOptions options;
ConfigData raw_config;
const auto sdcard = Environment::getExternalStorageDirectory();
@@ -642,7 +653,7 @@ Java_org_musicpd_Bridge_pause(JNIEnv *, jclass)
static inline void
MainOrThrow(int argc, char *argv[])
{
struct options options;
CommandLineOptions options;
ConfigData raw_config;
ParseCommandLine(argc, argv, options, raw_config);

@@ -199,13 +199,20 @@ handle_searchaddpl(Client &client, Request args, Response &)
{
const char *playlist = args.shift();
const unsigned position = ParseQueuePosition(args, UINT_MAX);
SongFilter filter;
const auto selection = ParseDatabaseSelection(args, true, filter);
const Database &db = client.GetDatabaseOrThrow();
search_add_to_playlist(db, client.GetStorage(),
playlist, selection);
if (position == UINT_MAX)
search_add_to_playlist(db, client.GetStorage(),
playlist, selection);
else
SearchInsertIntoPlaylist(db, client.GetStorage(), selection,
playlist, position);
return CommandResult::OK;
}

@@ -231,15 +231,14 @@ handle_playlistadd_position(Client &client, const char *playlist_name,
editor.Insert(position, uri);
} else {
#ifdef ENABLE_DATABASE
const auto &db = client.GetDatabaseOrThrow();
const auto *storage = client.GetStorage();
const DatabaseSelection selection(uri, true, nullptr);
db.Visit(selection, [&editor, &position, storage](const auto &song){
editor.Insert(position,
DatabaseDetachSong(storage, song));
++position;
});
if (SearchInsertIntoPlaylist(client.GetDatabaseOrThrow(),
client.GetStorage(),
selection,
editor, position) == 0)
/* no song was found, don't need to save */
return CommandResult::OK;
#else
(void)client;
r.Error(ACK_ERROR_NO_EXIST, "No database");

@@ -22,6 +22,7 @@
#include "PlaylistFile.hxx"
#include "Interface.hxx"
#include "song/DetachedSong.hxx"
#include "protocol/Ack.hxx"
#include <functional>
@@ -41,3 +42,42 @@ search_add_to_playlist(const Database &db, const Storage *storage,
const auto f = [=](auto && arg1) { return AddSong(storage, playlist_path_utf8, arg1); };
db.Visit(selection, f);
}
unsigned
SearchInsertIntoPlaylist(const Database &db, const Storage *storage,
const DatabaseSelection &selection,
PlaylistFileEditor &playlist,
unsigned position)
{
assert(position <= playlist.size());
unsigned n = 0;
db.Visit(selection, [&playlist, &position, &n, storage](const auto &song){
playlist.Insert(position + n,
DatabaseDetachSong(storage, song));
++position;
++n;
});
return n;
}
void
SearchInsertIntoPlaylist(const Database &db, const Storage *storage,
const DatabaseSelection &selection,
const char *playlist_name,
unsigned position)
{
PlaylistFileEditor editor{
playlist_name,
PlaylistFileEditor::LoadMode::TRY,
};
if (position > editor.size())
throw ProtocolError{ACK_ERROR_ARG, "Bad position"};
if (SearchInsertIntoPlaylist(db, storage, selection,
editor, position) > 0)
editor.Save();
}

@@ -25,6 +25,7 @@
class Database;
class Storage;
struct DatabaseSelection;
class PlaylistFileEditor;
gcc_nonnull(3)
void
@@ -32,4 +33,19 @@ search_add_to_playlist(const Database &db, const Storage *storage,
const char *playlist_path_utf8,
const DatabaseSelection &selection);
/**
* @return the number of songs added
*/
unsigned
SearchInsertIntoPlaylist(const Database &db, const Storage *storage,
const DatabaseSelection &selection,
PlaylistFileEditor &playlist,
unsigned position);
void
SearchInsertIntoPlaylist(const Database &db, const Storage *storage,
const DatabaseSelection &selection,
const char *playlist_name,
unsigned position);
#endif

@@ -39,6 +39,7 @@
#include "util/ConstBuffer.hxx"
#include "util/RecursiveMap.hxx"
#include "util/SplitString.hxx"
#include "config/Block.hxx"
#include <cassert>
#include <string>
@@ -76,10 +77,13 @@ class UpnpDatabase : public Database {
UpnpClient_Handle handle;
UPnPDeviceDirectory *discovery;
const char* interface;
public:
explicit UpnpDatabase(EventLoop &_event_loop) noexcept
explicit UpnpDatabase(EventLoop &_event_loop, const ConfigBlock &block) noexcept
:Database(upnp_db_plugin),
event_loop(_event_loop) {}
event_loop(_event_loop),
interface(block.GetBlockValue("interface", nullptr)) {}
static DatabasePtr Create(EventLoop &main_event_loop,
EventLoop &io_event_loop,
@@ -147,15 +151,15 @@ private:
DatabasePtr
UpnpDatabase::Create(EventLoop &, EventLoop &io_event_loop,
[[maybe_unused]] DatabaseListener &listener,
const ConfigBlock &) noexcept
const ConfigBlock &block) noexcept
{
return std::make_unique<UpnpDatabase>(io_event_loop);
return std::make_unique<UpnpDatabase>(io_event_loop, block);;
}
void
UpnpDatabase::Open()
{
handle = UpnpClientGlobalInit();
handle = UpnpClientGlobalInit(interface);
discovery = new UPnPDeviceDirectory(event_loop, handle);
try {

@@ -502,7 +502,7 @@ FfmpegDecode(DecoderClient &client, InputStream *input,
FmtDebug(ffmpeg_domain, "codec '{}'",
codec_descriptor->name);
AVCodec *codec = avcodec_find_decoder(codec_params.codec_id);
const AVCodec *codec = avcodec_find_decoder(codec_params.codec_id);
if (!codec) {
LogError(ffmpeg_domain, "Unsupported audio codec");

@@ -52,6 +52,13 @@ EventLoop::EventLoop(
EventLoop::~EventLoop() noexcept
{
#if defined(HAVE_URING) && !defined(NDEBUG)
/* if Run() was never called (maybe because startup failed and
an exception is pending), we need to destruct the
Uring::Manager here or else the assertions below fail */
uring.reset();
#endif
assert(defer.empty());
assert(idle.empty());
#ifdef HAVE_THREADED_EVENT_LOOP

@@ -26,6 +26,7 @@
#include "AlsaInputPlugin.hxx"
#include "lib/alsa/NonBlock.hxx"
#include "lib/alsa/Error.hxx"
#include "lib/alsa/Format.hxx"
#include "../InputPlugin.hxx"
#include "../AsyncInputStream.hxx"
@@ -332,28 +333,23 @@ AlsaInputStream::ConfigureCapture(AudioFormat audio_format)
snd_pcm_hw_params_alloca(&hw_params);
if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0)
throw FormatRuntimeError("Cannot initialize hardware parameter structure (%s)",
snd_strerror(err));
throw Alsa::MakeError(err, "snd_pcm_hw_params_any() failed");
if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
throw FormatRuntimeError("Cannot set access type (%s)",
snd_strerror(err));
throw Alsa::MakeError(err, "snd_pcm_hw_params_set_access() failed");
if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params,
ToAlsaPcmFormat(audio_format.format))) < 0)
throw FormatRuntimeError("Cannot set sample format (%s)",
snd_strerror(err));
throw Alsa::MakeError(err, "Cannot set sample format");
if ((err = snd_pcm_hw_params_set_channels(capture_handle,
hw_params, audio_format.channels)) < 0)
throw FormatRuntimeError("Cannot set channels (%s)",
snd_strerror(err));
throw Alsa::MakeError(err, "Cannot set channels");
if ((err = snd_pcm_hw_params_set_rate(capture_handle,
hw_params, audio_format.sample_rate, 0)) < 0)
throw FormatRuntimeError("Cannot set sample rate (%s)",
snd_strerror(err));
throw Alsa::MakeError(err, "Cannot set sample rate");
snd_pcm_uframes_t buffer_size_min, buffer_size_max;
snd_pcm_hw_params_get_buffer_size_min(hw_params, &buffer_size_min);
@@ -388,26 +384,22 @@ AlsaInputStream::ConfigureCapture(AudioFormat audio_format)
int direction = -1;
if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle,
hw_params, &period_size, &direction)) < 0)
throw FormatRuntimeError("Cannot set period size (%s)",
snd_strerror(err));
throw Alsa::MakeError(err, "Cannot set period size");
}
if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0)
throw FormatRuntimeError("Cannot set parameters (%s)",
snd_strerror(err));
throw Alsa::MakeError(err, "snd_pcm_hw_params() failed");
snd_pcm_uframes_t alsa_buffer_size;
err = snd_pcm_hw_params_get_buffer_size(hw_params, &alsa_buffer_size);
if (err < 0)
throw FormatRuntimeError("snd_pcm_hw_params_get_buffer_size() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_hw_params_get_buffer_size() failed");
snd_pcm_uframes_t alsa_period_size;
err = snd_pcm_hw_params_get_period_size(hw_params, &alsa_period_size,
nullptr);
if (err < 0)
throw FormatRuntimeError("snd_pcm_hw_params_get_period_size() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_hw_params_get_period_size() failed");
FmtDebug(alsa_input_domain, "buffer_size={} period_size={}",
alsa_buffer_size, alsa_period_size);
@@ -418,8 +410,7 @@ AlsaInputStream::ConfigureCapture(AudioFormat audio_format)
snd_pcm_sw_params_current(capture_handle, sw_params);
if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0)
throw FormatRuntimeError("unable to install sw params (%s)",
snd_strerror(err));
throw Alsa::MakeError(err, "snd_pcm_sw_params() failed");
}
inline void
@@ -430,8 +421,9 @@ AlsaInputStream::OpenDevice(const SourceSpec &spec)
if ((err = snd_pcm_open(&capture_handle, spec.GetDeviceName(),
SND_PCM_STREAM_CAPTURE,
SND_PCM_NONBLOCK | global_config.mode)) < 0)
throw FormatRuntimeError("Failed to open device: %s (%s)",
spec.GetDeviceName(), snd_strerror(err));
throw Alsa::MakeError(err,
fmt::format("Failed to open device {}",
spec.GetDeviceName()).c_str());
try {
ConfigureCapture(spec.GetAudioFormat());

44
src/lib/alsa/Error.cxx Normal file

@@ -0,0 +1,44 @@
/*
* Copyright 2021 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "Error.hxx"
#include <alsa/error.h>
namespace Alsa {
ErrorCategory error_category;
std::string
ErrorCategory::message(int condition) const
{
return snd_strerror(condition);
}
} // namespace Avahi

53
src/lib/alsa/Error.hxx Normal file

@@ -0,0 +1,53 @@
/*
* Copyright 2021 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <system_error>
namespace Alsa {
class ErrorCategory final : public std::error_category {
public:
const char *name() const noexcept override {
return "libasound";
}
std::string message(int condition) const override;
};
extern ErrorCategory error_category;
inline std::system_error
MakeError(int error, const char *msg) noexcept
{
return std::system_error(error, error_category, msg);
}
} // namespace Avahi

@@ -18,7 +18,9 @@
*/
#include "HwSetup.hxx"
#include "Error.hxx"
#include "Format.hxx"
#include "lib/fmt/AudioFormatFormatter.hxx"
#include "util/ByteOrder.hxx"
#include "util/Domain.hxx"
#include "util/RuntimeError.hxx"
@@ -185,29 +187,27 @@ SetupHw(snd_pcm_t *pcm,
/* configure HW params */
err = snd_pcm_hw_params_any(pcm, hwparams);
if (err < 0)
throw FormatRuntimeError("snd_pcm_hw_params_any() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_hw_params_any() failed");
err = snd_pcm_hw_params_set_access(pcm, hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0)
throw FormatRuntimeError("snd_pcm_hw_params_set_access() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_hw_params_set_access() failed");
err = SetupSampleFormat(pcm, hwparams,
audio_format.format, params);
if (err < 0)
throw FormatRuntimeError("Failed to configure format %s: %s",
sample_format_to_string(audio_format.format),
snd_strerror(-err));
throw Alsa::MakeError(err,
fmt::format("Failed to configure format {}",
audio_format.format).c_str());
unsigned int channels = audio_format.channels;
err = snd_pcm_hw_params_set_channels_near(pcm, hwparams,
&channels);
if (err < 0)
throw FormatRuntimeError("Failed to configure %i channels: %s",
(int)audio_format.channels,
snd_strerror(-err));
throw Alsa::MakeError(err,
fmt::format("Failed to configure {} channels",
audio_format.channels).c_str());
audio_format.channels = (int8_t)channels;
@@ -218,9 +218,9 @@ SetupHw(snd_pcm_t *pcm,
err = snd_pcm_hw_params_set_rate_near(pcm, hwparams,
&output_sample_rate, nullptr);
if (err < 0)
throw FormatRuntimeError("Failed to configure sample rate %u Hz: %s",
requested_sample_rate,
snd_strerror(-err));
throw Alsa::MakeError(err,
fmt::format("Failed to configure sample rate {} Hz",
requested_sample_rate).c_str());
if (output_sample_rate == 0)
throw FormatRuntimeError("Failed to configure sample rate %u Hz",
@@ -253,8 +253,7 @@ SetupHw(snd_pcm_t *pcm,
err = snd_pcm_hw_params_set_buffer_time_near(pcm, hwparams,
&buffer_time, nullptr);
if (err < 0)
throw FormatRuntimeError("snd_pcm_hw_params_set_buffer_time_near() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_hw_params_set_buffer_time_near() failed");
} else {
err = snd_pcm_hw_params_get_buffer_time(hwparams, &buffer_time,
nullptr);
@@ -275,32 +274,27 @@ SetupHw(snd_pcm_t *pcm,
err = snd_pcm_hw_params_set_period_time_near(pcm, hwparams,
&period_time, nullptr);
if (err < 0)
throw FormatRuntimeError("snd_pcm_hw_params_set_period_time_near() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_hw_params_set_period_time_near() failed");
}
err = snd_pcm_hw_params(pcm, hwparams);
if (err < 0)
throw FormatRuntimeError("snd_pcm_hw_params() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_hw_params() failed");
HwResult result;
err = snd_pcm_hw_params_get_format(hwparams, &result.format);
if (err < 0)
throw FormatRuntimeError("snd_pcm_hw_params_get_format() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_hw_params_get_format() failed");
err = snd_pcm_hw_params_get_buffer_size(hwparams, &result.buffer_size);
if (err < 0)
throw FormatRuntimeError("snd_pcm_hw_params_get_buffer_size() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_hw_params_get_buffer_size() failed");
err = snd_pcm_hw_params_get_period_size(hwparams, &result.period_size,
nullptr);
if (err < 0)
throw FormatRuntimeError("snd_pcm_hw_params_get_period_size() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_hw_params_get_period_size() failed");
return result;
}

@@ -18,6 +18,7 @@
*/
#include "NonBlock.hxx"
#include "Error.hxx"
#include "event/MultiSocketMonitor.hxx"
#include "util/RuntimeError.hxx"
@@ -29,8 +30,7 @@ AlsaNonBlockPcm::PrepareSockets(MultiSocketMonitor &m, snd_pcm_t *pcm)
if (count == 0)
throw std::runtime_error("snd_pcm_poll_descriptors_count() failed");
else
throw FormatRuntimeError("snd_pcm_poll_descriptors_count() failed: %s",
snd_strerror(-count));
throw Alsa::MakeError(count, "snd_pcm_poll_descriptors_count() failed");
}
struct pollfd *pfds = pfd_buffer.Get(count);
@@ -40,8 +40,7 @@ AlsaNonBlockPcm::PrepareSockets(MultiSocketMonitor &m, snd_pcm_t *pcm)
if (count == 0)
throw std::runtime_error("snd_pcm_poll_descriptors() failed");
else
throw FormatRuntimeError("snd_pcm_poll_descriptors() failed: %s",
snd_strerror(-count));
throw Alsa::MakeError(count, "snd_pcm_poll_descriptors() failed");
}
m.ReplaceSocketList(pfds, count);
@@ -71,8 +70,7 @@ AlsaNonBlockPcm::DispatchSockets(MultiSocketMonitor &m,
unsigned short dummy;
int err = snd_pcm_poll_descriptors_revents(pcm, pfds, i - pfds, &dummy);
if (err < 0)
throw FormatRuntimeError("snd_pcm_poll_descriptors_revents() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_poll_descriptors_revents() failed");
}
Event::Duration

@@ -14,6 +14,7 @@ conf.set('ENABLE_ALSA', true)
alsa = static_library(
'alsa',
'Version.cxx',
'Error.cxx',
'AllowedFormat.cxx',
'HwSetup.cxx',
'NonBlock.cxx',

@@ -36,7 +36,7 @@ class CodecContext {
public:
CodecContext() = default;
explicit CodecContext(AVCodec &codec)
explicit CodecContext(const AVCodec &codec)
:codec_context(avcodec_alloc_context3(&codec))
{
if (codec_context == nullptr)

@@ -35,6 +35,16 @@
#include <fmt/format.h>
template<>
struct fmt::formatter<SampleFormat> : formatter<string_view>
{
template<typename FormatContext>
auto format(const SampleFormat format, FormatContext &ctx) {
return formatter<string_view>::format(sample_format_to_string(format),
ctx);
}
};
template<>
struct fmt::formatter<AudioFormat> : formatter<string_view>
{

@@ -57,9 +57,9 @@ DoInit()
}
UpnpClient_Handle
UpnpClientGlobalInit()
UpnpClientGlobalInit(const char* iface)
{
UpnpGlobalInit();
UpnpGlobalInit(iface);
try {
const std::lock_guard<Mutex> protect(upnp_client_init_mutex);

@@ -23,7 +23,7 @@
#include "Compat.hxx"
UpnpClient_Handle
UpnpClientGlobalInit();
UpnpClientGlobalInit(const char* iface);
void
UpnpClientGlobalFinish() noexcept;

@@ -33,12 +33,13 @@ static Mutex upnp_init_mutex;
static unsigned upnp_ref;
static void
DoInit()
DoInit(const char* iface)
{
#ifdef UPNP_ENABLE_IPV6
auto code = UpnpInit2(nullptr, 0);
auto code = UpnpInit2(iface, 0);
#else
auto code = UpnpInit(nullptr, 0);
auto code = UpnpInit(iface, 0);
#endif
if (code != UPNP_E_SUCCESS)
throw FormatRuntimeError("UpnpInit() failed: %s",
@@ -53,12 +54,12 @@ DoInit()
}
void
UpnpGlobalInit()
UpnpGlobalInit(const char* iface)
{
const std::lock_guard<Mutex> protect(upnp_init_mutex);
if (upnp_ref == 0)
DoInit();
DoInit(iface);
++upnp_ref;
}

@@ -21,7 +21,7 @@
#define MPD_UPNP_INIT_HXX
void
UpnpGlobalInit();
UpnpGlobalInit(const char* iface);
void
UpnpGlobalFinish() noexcept;

@@ -18,6 +18,7 @@
*/
#include "lib/alsa/NonBlock.hxx"
#include "lib/alsa/Error.hxx"
#include "mixer/MixerInternal.hxx"
#include "mixer/Listener.hxx"
#include "output/OutputAPI.hxx"
@@ -264,16 +265,15 @@ AlsaMixer::Setup()
int err;
if ((err = snd_mixer_attach(handle, device)) < 0)
throw FormatRuntimeError("failed to attach to %s: %s",
device, snd_strerror(err));
throw Alsa::MakeError(err,
fmt::format("failed to attach to {}",
device).c_str());
if ((err = snd_mixer_selem_register(handle, nullptr, nullptr)) < 0)
throw FormatRuntimeError("snd_mixer_selem_register() failed: %s",
snd_strerror(err));
throw Alsa::MakeError(err, "snd_mixer_selem_register() failed");
if ((err = snd_mixer_load(handle)) < 0)
throw FormatRuntimeError("snd_mixer_load() failed: %s\n",
snd_strerror(err));
throw Alsa::MakeError(err, "snd_mixer_load() failed");
elem = alsa_mixer_lookup_elem(handle, control, index);
if (elem == nullptr)
@@ -294,8 +294,7 @@ AlsaMixer::Open()
err = snd_mixer_open(&handle, 0);
if (err < 0)
throw FormatRuntimeError("snd_mixer_open() failed: %s",
snd_strerror(err));
throw Alsa::MakeError(err, "snd_mixer_open() failed");
try {
Setup();
@@ -325,8 +324,7 @@ AlsaMixer::GetVolume()
err = snd_mixer_handle_events(handle);
if (err < 0)
throw FormatRuntimeError("snd_mixer_handle_events() failed: %s",
snd_strerror(err));
throw Alsa::MakeError(err, "snd_mixer_handle_events() failed");
int volume = GetPercentVolume();
if (resulting_volume >= 0 && volume == resulting_volume)
@@ -343,8 +341,7 @@ AlsaMixer::SetVolume(unsigned volume)
int err = set_normalized_playback_volume(elem, 0.01*volume, 1);
if (err < 0)
throw FormatRuntimeError("failed to set ALSA volume: %s",
snd_strerror(err));
throw Alsa::MakeError(err, "failed to set ALSA volume");
desired_volume = volume;
resulting_volume = GetPercentVolume();

@@ -74,7 +74,7 @@ private:
void
UpnpNeighborExplorer::Open()
{
auto handle = UpnpClientGlobalInit();
auto handle = UpnpClientGlobalInit(nullptr);
discovery = new UPnPDeviceDirectory(event_loop, handle, this);

@@ -20,6 +20,7 @@
#include "config.h"
#include "AlsaOutputPlugin.hxx"
#include "lib/alsa/AllowedFormat.hxx"
#include "lib/alsa/Error.hxx"
#include "lib/alsa/HwSetup.hxx"
#include "lib/alsa/NonBlock.hxx"
#include "lib/alsa/PeriodBuffer.hxx"
@@ -42,6 +43,10 @@
#include "event/Call.hxx"
#include "Log.hxx"
#ifdef ENABLE_DSD
#include "util/AllocatedArray.hxx"
#endif
#include <alsa/asoundlib.h>
#include <boost/lockfree/spsc_queue.hpp>
@@ -101,6 +106,16 @@ class AlsaOutput final
* Are we currently draining with #stop_dsd_silence?
*/
bool in_stop_dsd_silence;
/**
* Enable the DSD sync workaround for Thesycon USB audio
* receivers? On this device, playing DSD512 or PCM causes
* all subsequent attempts to play other DSD rates to fail,
* which can be fixed by briefly playing PCM at 44.1 kHz.
*/
const bool thesycon_dsd_workaround;
bool need_thesycon_dsd_workaround = thesycon_dsd_workaround;
#endif
/** libasound's buffer_time setting (in microseconds) */
@@ -432,6 +447,8 @@ AlsaOutput::AlsaOutput(EventLoop &_loop, const ConfigBlock &block)
/* legacy name from MPD 0.18 and older: */
block.GetBlockValue("dsd_usb", false)),
stop_dsd_silence(block.GetBlockValue("stop_dsd_silence", false)),
thesycon_dsd_workaround(block.GetBlockValue("thesycon_dsd_workaround",
false)),
#endif
buffer_time(block.GetPositiveValue("buffer_time",
MPD_ALSA_BUFFER_TIME_US)),
@@ -519,24 +536,20 @@ AlsaSetupSw(snd_pcm_t *pcm, snd_pcm_uframes_t start_threshold,
int err = snd_pcm_sw_params_current(pcm, swparams);
if (err < 0)
throw FormatRuntimeError("snd_pcm_sw_params_current() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_sw_params_current() failed");
err = snd_pcm_sw_params_set_start_threshold(pcm, swparams,
start_threshold);
if (err < 0)
throw FormatRuntimeError("snd_pcm_sw_params_set_start_threshold() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_sw_params_set_start_threshold() failed");
err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min);
if (err < 0)
throw FormatRuntimeError("snd_pcm_sw_params_set_avail_min() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_sw_params_set_avail_min() failed");
err = snd_pcm_sw_params(pcm, swparams);
if (err < 0)
throw FormatRuntimeError("snd_pcm_sw_params() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_sw_params() failed");
}
inline void
@@ -678,6 +691,97 @@ BestMatch(const std::forward_list<Alsa::AllowedFormat> &haystack,
return haystack.front();
}
#ifdef ENABLE_DSD
static void
Play_44_1_Silence(snd_pcm_t *pcm)
{
snd_pcm_hw_params_t *hw;
snd_pcm_hw_params_alloca(&hw);
int err;
err = snd_pcm_hw_params_any(pcm, hw);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_hw_params_any() failed");
err = snd_pcm_hw_params_set_access(pcm, hw,
SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_hw_params_set_access() failed");
err = snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_S16);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_hw_params_set_format() failed");
unsigned channels = 1;
err = snd_pcm_hw_params_set_channels_near(pcm, hw, &channels);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_hw_params_set_channels_near() failed");
constexpr snd_pcm_uframes_t rate = 44100;
err = snd_pcm_hw_params_set_rate(pcm, hw, rate, 0);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_hw_params_set_rate() failed");
snd_pcm_uframes_t buffer_size = 1;
err = snd_pcm_hw_params_set_buffer_size_near(pcm, hw, &buffer_size);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_hw_params_set_buffer_size_near() failed");
snd_pcm_uframes_t period_size = 1;
int dir = 0;
err = snd_pcm_hw_params_set_period_size_near(pcm, hw, &period_size,
&dir);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_hw_params_set_period_size_near() failed");
err = snd_pcm_hw_params(pcm, hw);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_hw_params() failed");
snd_pcm_sw_params_t *sw;
snd_pcm_sw_params_alloca(&sw);
err = snd_pcm_sw_params_current(pcm, sw);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_sw_params_current() failed");
err = snd_pcm_sw_params_set_start_threshold(pcm, sw, period_size);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_sw_params_set_start_threshold() failed");
err = snd_pcm_sw_params(pcm, sw);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_sw_params() failed");
err = snd_pcm_prepare(pcm);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_prepare() failed");
AllocatedArray<int16_t> buffer{channels * period_size};
std::fill(buffer.begin(), buffer.end(), 0);
/* play at least 250ms of silence */
for (snd_pcm_uframes_t remaining_frames = rate / 4;;) {
auto n = snd_pcm_writei(pcm, buffer.data(),
period_size);
if (n < 0)
throw Alsa::MakeError(err, "snd_pcm_writei() failed");
if (snd_pcm_uframes_t(n) >= remaining_frames)
break;
remaining_frames -= snd_pcm_uframes_t(n);
}
err = snd_pcm_drain(pcm);
if (err < 0)
throw Alsa::MakeError(err, "snd_pcm_drain() failed");
}
#endif
void
AlsaOutput::Open(AudioFormat &audio_format)
{
@@ -704,13 +808,30 @@ AlsaOutput::Open(AudioFormat &audio_format)
int err = snd_pcm_open(&pcm, GetDevice(),
SND_PCM_STREAM_PLAYBACK, mode);
if (err < 0)
throw FormatRuntimeError("Failed to open ALSA device \"%s\": %s",
GetDevice(), snd_strerror(err));
throw Alsa::MakeError(err,
fmt::format("Failed to open ALSA device \"{}\"",
GetDevice()).c_str());
FmtDebug(alsa_output_domain, "opened {} type={}",
snd_pcm_name(pcm),
snd_pcm_type_name(snd_pcm_type(pcm)));
#ifdef ENABLE_DSD
if (need_thesycon_dsd_workaround &&
audio_format.format == SampleFormat::DSD &&
audio_format.sample_rate <= 256 * 44100 / 8) {
LogDebug(alsa_output_domain, "Playing some 44.1 kHz silence");
try {
Play_44_1_Silence(pcm);
} catch (...) {
LogError(std::current_exception());
}
need_thesycon_dsd_workaround = false;
}
#endif
PcmExport::Params params;
params.alsa_channel_order = true;
@@ -735,6 +856,11 @@ AlsaOutput::Open(AudioFormat &audio_format)
use_dsd = audio_format.format == SampleFormat::DSD;
in_stop_dsd_silence = false;
if (thesycon_dsd_workaround &&
(!use_dsd ||
audio_format.sample_rate > 256 * 44100 / 8))
need_thesycon_dsd_workaround = true;
if (params.dsd_mode == PcmExport::DsdMode::DOP)
LogDebug(alsa_output_domain, "DoP (DSD over PCM) enabled");
#endif
@@ -897,8 +1023,8 @@ AlsaOutput::DrainInternal()
if (frames_written == -EAGAIN)
return false;
throw FormatRuntimeError("snd_pcm_writei() failed: %s",
snd_strerror(-frames_written));
throw Alsa::MakeError(frames_written,
"snd_pcm_writei() failed");
}
/* need to call CopyRingToPeriodBuffer() and
@@ -947,8 +1073,7 @@ AlsaOutput::DrainInternal()
else if (result == -EAGAIN)
return false;
else
throw FormatRuntimeError("snd_pcm_drain() failed: %s",
snd_strerror(-result));
throw Alsa::MakeError(result, "snd_pcm_drain() failed");
}
void
@@ -1147,8 +1272,7 @@ try {
int err = snd_pcm_prepare(pcm);
if (err < 0)
throw FormatRuntimeError("snd_pcm_prepare() failed: %s",
snd_strerror(-err));
throw Alsa::MakeError(err, "snd_pcm_prepare() failed");
}
{
@@ -1236,8 +1360,8 @@ try {
return;
if (Recover(frames_written) < 0)
throw FormatRuntimeError("snd_pcm_writei() failed: %s",
snd_strerror(-frames_written));
throw Alsa::MakeError(frames_written,
"snd_pcm_writei() failed");
/* recovered; try again in the next DispatchSockets()
call */

@@ -55,6 +55,7 @@
#include <boost/lockfree/spsc_queue.hpp>
#include <algorithm>
#include <array>
#include <stdexcept>
#include <string>

@@ -5,11 +5,7 @@ After=network.target sound.target
[Service]
Type=notify
ExecStart=@prefix@/bin/mpd --no-daemon
# Create /run/mpd (if MPD is launched without the socket unit and is
# configured to bind listener sockets there).
RuntimeDirectory=mpd
ExecStart=@prefix@/bin/mpd --systemd
# Enable this setting to ask systemd to watch over MPD, see
# systemd.service(5). This is disabled by default because it causes

@@ -5,11 +5,7 @@ After=network.target sound.target
[Service]
Type=notify
ExecStart=@prefix@/bin/mpd --no-daemon
# Create /run/user/$UID/mpd (if MPD is launched without the socket
# unit and is configured to bind listener sockets there).
RuntimeDirectory=mpd
ExecStart=@prefix@/bin/mpd --systemd
# Enable this setting to ask systemd to watch over MPD, see
# systemd.service(5). This is disabled by default because it causes

@@ -99,6 +99,7 @@ thirdparty_libs = [
libid3tag,
liblame,
libmodplug,
libopenmpt,
wildmidi,
gme,
ffmpeg,