output/alsa: add option "close_on_pause"
This allows keeping the ALSA PCM open even if playback is paused. As a side effect, this allows using the "always_on" option with ALSA outputs, because "always_on" pauses the output. Closes https://github.com/MusicPlayerDaemon/MPD/issues/1623
This commit is contained in:
parent
7ab789fbaf
commit
0a035f3ce0
1
NEWS
1
NEWS
|
@ -32,6 +32,7 @@ ver 0.24 (not yet released)
|
||||||
- alsa: limit ALSA buffer time to 2 seconds
|
- alsa: limit ALSA buffer time to 2 seconds
|
||||||
- alsa: set up a channel map
|
- alsa: set up a channel map
|
||||||
- alsa: support the alsa-lib 1.2.11 API
|
- alsa: support the alsa-lib 1.2.11 API
|
||||||
|
- alsa: add option "close_on_pause"
|
||||||
- curl: add "connect_timeout" configuration
|
- curl: add "connect_timeout" configuration
|
||||||
* decoder
|
* decoder
|
||||||
- ffmpeg: require FFmpeg 4.0 or later
|
- ffmpeg: require FFmpeg 4.0 or later
|
||||||
|
|
|
@ -896,6 +896,11 @@ The `Advanced Linux Sound Architecture (ALSA) <http://www.alsa-project.org/>`_ p
|
||||||
|
|
||||||
Example: "96000:16:* 192000:24:* dsd64:*=dop *:dsd:*".
|
Example: "96000:16:* 192000:24:* dsd64:*=dop *:dsd:*".
|
||||||
|
|
||||||
|
* - **close_on_pause yes|no**
|
||||||
|
- Close the ALSA device while playback is paused? This defaults
|
||||||
|
to *yes* because this allows other applications to use the
|
||||||
|
device while MPD is paused.
|
||||||
|
|
||||||
The according hardware mixer plugin understands the following settings:
|
The according hardware mixer plugin understands the following settings:
|
||||||
|
|
||||||
.. list-table::
|
.. list-table::
|
||||||
|
|
|
@ -532,7 +532,8 @@ The following table lists the audio_output options valid for all plugins:
|
||||||
- If set to yes, then :program:`MPD` attempts to keep this audio
|
- If set to yes, then :program:`MPD` attempts to keep this audio
|
||||||
output always open. Instead of closing at the end
|
output always open. Instead of closing at the end
|
||||||
of playback, it puts the device in "pause" mode. This works
|
of playback, it puts the device in "pause" mode. This works
|
||||||
only with output plugins that suport "pause" mode.
|
only with output plugins that suport "pause" mode (see
|
||||||
|
:ref:`ALSA option "close_on_pause" <alsa_plugin>`).
|
||||||
This may be useful for streaming servers, when you don't want
|
This may be useful for streaming servers, when you don't want
|
||||||
to disconnect all listeners even when playback is accidentally
|
to disconnect all listeners even when playback is accidentally
|
||||||
stopped.
|
stopped.
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <forward_list>
|
#include <forward_list>
|
||||||
|
|
||||||
|
@ -222,6 +223,15 @@ class AlsaOutput final
|
||||||
*/
|
*/
|
||||||
bool interrupted;
|
bool interrupted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the ALSA PCM while playback is paused? This defaults
|
||||||
|
* to true because this allows other applications to use the
|
||||||
|
* PCM while MPD is paused.
|
||||||
|
*/
|
||||||
|
const bool close_on_pause;
|
||||||
|
|
||||||
|
std::atomic_bool paused;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AlsaOutput(EventLoop &loop, const ConfigBlock &block);
|
AlsaOutput(EventLoop &loop, const ConfigBlock &block);
|
||||||
|
|
||||||
|
@ -261,6 +271,7 @@ private:
|
||||||
void Close() noexcept override;
|
void Close() noexcept override;
|
||||||
|
|
||||||
void Interrupt() noexcept override;
|
void Interrupt() noexcept override;
|
||||||
|
std::chrono::steady_clock::duration Delay() const noexcept override;
|
||||||
|
|
||||||
std::size_t Play(std::span<const std::byte> src) override;
|
std::size_t Play(std::span<const std::byte> src) override;
|
||||||
void Drain() override;
|
void Drain() override;
|
||||||
|
@ -414,7 +425,7 @@ GetAlsaOpenMode(const ConfigBlock &block)
|
||||||
}
|
}
|
||||||
|
|
||||||
AlsaOutput::AlsaOutput(EventLoop &_loop, const ConfigBlock &block)
|
AlsaOutput::AlsaOutput(EventLoop &_loop, const ConfigBlock &block)
|
||||||
:AudioOutput(FLAG_ENABLE_DISABLE),
|
:AudioOutput(FLAG_ENABLE_DISABLE|FLAG_PAUSE),
|
||||||
MultiSocketMonitor(_loop),
|
MultiSocketMonitor(_loop),
|
||||||
defer_invalidate_sockets(_loop, BIND_THIS_METHOD(InvalidateSockets)),
|
defer_invalidate_sockets(_loop, BIND_THIS_METHOD(InvalidateSockets)),
|
||||||
silence_timer(_loop, BIND_THIS_METHOD(OnSilenceTimer)),
|
silence_timer(_loop, BIND_THIS_METHOD(OnSilenceTimer)),
|
||||||
|
@ -422,16 +433,16 @@ AlsaOutput::AlsaOutput(EventLoop &_loop, const ConfigBlock &block)
|
||||||
buffer_time(block.GetPositiveValue("buffer_time",
|
buffer_time(block.GetPositiveValue("buffer_time",
|
||||||
MPD_ALSA_BUFFER_TIME_US)),
|
MPD_ALSA_BUFFER_TIME_US)),
|
||||||
period_time(block.GetPositiveValue("period_time", 0U)),
|
period_time(block.GetPositiveValue("period_time", 0U)),
|
||||||
mode(GetAlsaOpenMode(block))
|
mode(GetAlsaOpenMode(block)),
|
||||||
#ifdef ENABLE_DSD
|
#ifdef ENABLE_DSD
|
||||||
,
|
|
||||||
dop_setting(block.GetBlockValue("dop", false) ||
|
dop_setting(block.GetBlockValue("dop", false) ||
|
||||||
/* legacy name from MPD 0.18 and older: */
|
/* legacy name from MPD 0.18 and older: */
|
||||||
block.GetBlockValue("dsd_usb", false)),
|
block.GetBlockValue("dsd_usb", false)),
|
||||||
stop_dsd_silence(block.GetBlockValue("stop_dsd_silence", false)),
|
stop_dsd_silence(block.GetBlockValue("stop_dsd_silence", false)),
|
||||||
thesycon_dsd_workaround(block.GetBlockValue("thesycon_dsd_workaround",
|
thesycon_dsd_workaround(block.GetBlockValue("thesycon_dsd_workaround",
|
||||||
false))
|
false)),
|
||||||
#endif
|
#endif
|
||||||
|
close_on_pause(block.GetBlockValue("close_on_pause", true))
|
||||||
{
|
{
|
||||||
const char *allowed_formats_string =
|
const char *allowed_formats_string =
|
||||||
block.GetBlockValue("allowed_formats", nullptr);
|
block.GetBlockValue("allowed_formats", nullptr);
|
||||||
|
@ -752,6 +763,8 @@ Play_44_1_Silence(snd_pcm_t *pcm)
|
||||||
void
|
void
|
||||||
AlsaOutput::Open(AudioFormat &audio_format)
|
AlsaOutput::Open(AudioFormat &audio_format)
|
||||||
{
|
{
|
||||||
|
paused = false;
|
||||||
|
|
||||||
#ifdef ENABLE_DSD
|
#ifdef ENABLE_DSD
|
||||||
bool dop;
|
bool dop;
|
||||||
#endif
|
#endif
|
||||||
|
@ -866,6 +879,15 @@ AlsaOutput::Interrupt() noexcept
|
||||||
cond.notify_one();
|
cond.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::chrono::steady_clock::duration
|
||||||
|
AlsaOutput::Delay() const noexcept
|
||||||
|
{
|
||||||
|
if (paused)
|
||||||
|
return std::chrono::hours{1};
|
||||||
|
|
||||||
|
return AudioOutput::Delay();
|
||||||
|
}
|
||||||
|
|
||||||
inline int
|
inline int
|
||||||
AlsaOutput::Recover(int err) noexcept
|
AlsaOutput::Recover(int err) noexcept
|
||||||
{
|
{
|
||||||
|
@ -1119,9 +1141,13 @@ AlsaOutput::Pause() noexcept
|
||||||
std::lock_guard lock{mutex};
|
std::lock_guard lock{mutex};
|
||||||
interrupted = false;
|
interrupted = false;
|
||||||
|
|
||||||
/* not implemented - this override exists only to reset the
|
if (close_on_pause)
|
||||||
"interrupted" flag */
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// TODO use snd_pcm_pause()?
|
||||||
|
|
||||||
|
paused = true;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1188,6 +1214,8 @@ AlsaOutput::Play(std::span<const std::byte> src)
|
||||||
assert(!src.empty());
|
assert(!src.empty());
|
||||||
assert(src.size() % in_frame_size == 0);
|
assert(src.size() % in_frame_size == 0);
|
||||||
|
|
||||||
|
paused = false;
|
||||||
|
|
||||||
const size_t max_frames = LockWaitWriteAvailable();
|
const size_t max_frames = LockWaitWriteAvailable();
|
||||||
const size_t max_size = max_frames * in_frame_size;
|
const size_t max_size = max_frames * in_frame_size;
|
||||||
if (src.size() > max_size)
|
if (src.size() > max_size)
|
||||||
|
|
Loading…
Reference in New Issue