AlsaOutputPlugin.cxx 23 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2017 The Music Player Daemon Project
3
 * http://www.musicpd.org
4 5 6 7 8 9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
14 15 16 17
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 19
 */

20
#include "config.h"
21
#include "AlsaOutputPlugin.hxx"
22
#include "lib/alsa/AllowedFormat.hxx"
23
#include "lib/alsa/HwSetup.hxx"
24
#include "lib/alsa/NonBlock.hxx"
25
#include "lib/alsa/PeriodBuffer.hxx"
26
#include "lib/alsa/Version.hxx"
27
#include "../OutputAPI.hxx"
Max Kellermann's avatar
Max Kellermann committed
28
#include "mixer/MixerList.hxx"
29
#include "pcm/PcmExport.hxx"
30
#include "thread/Mutex.hxx"
31
#include "thread/Cond.hxx"
32
#include "util/Manual.hxx"
33
#include "util/RuntimeError.hxx"
34
#include "util/Domain.hxx"
35
#include "util/ConstBuffer.hxx"
36
#include "util/StringView.hxx"
37
#include "event/MultiSocketMonitor.hxx"
38
#include "event/DeferEvent.hxx"
39
#include "event/Call.hxx"
40
#include "Log.hxx"
41

42
#include <alsa/asoundlib.h>
43

44 45
#include <boost/lockfree/spsc_queue.hpp>

46
#include <string>
47
#include <forward_list>
48

49 50
static const char default_device[] = "default";

51
static constexpr unsigned MPD_ALSA_BUFFER_TIME_US = 500000;
52

53
class AlsaOutput final
54 55 56
	: AudioOutput, MultiSocketMonitor {

	DeferEvent defer_invalidate_sockets;
57

58
	Manual<PcmExport> pcm_export;
59

60 61 62 63
	/**
	 * The configured name of the ALSA device; empty for the
	 * default device
	 */
64
	const std::string device;
65

66
#ifdef ENABLE_DSD
67
	/**
Max Kellermann's avatar
Max Kellermann committed
68
	 * Enable DSD over PCM according to the DoP standard?
69
	 *
70
	 * @see http://dsd-guide.com/dop-open-standard
71
	 */
72
	bool dop_setting;
73
#endif
74

Max Kellermann's avatar
Max Kellermann committed
75
	/** libasound's buffer_time setting (in microseconds) */
76
	const unsigned buffer_time;
Max Kellermann's avatar
Max Kellermann committed
77 78

	/** libasound's period_time setting (in microseconds) */
79
	const unsigned period_time;
Max Kellermann's avatar
Max Kellermann committed
80

81
	/** the mode flags passed to snd_pcm_open */
82
	int mode = 0;
83

84 85
	std::forward_list<Alsa::AllowedFormat> allowed_formats;

86 87 88 89 90
	/**
	 * Protects #dop_setting and #allowed_formats.
	 */
	mutable Mutex attributes_mutex;

Max Kellermann's avatar
Max Kellermann committed
91
	/** the libasound PCM device handle */
Max Kellermann's avatar
Max Kellermann committed
92
	snd_pcm_t *pcm;
Max Kellermann's avatar
Max Kellermann committed
93

94
#ifndef NDEBUG
95 96 97 98
	/**
	 * The size of one audio frame passed to method play().
	 */
	size_t in_frame_size;
99
#endif
100 101 102 103 104

	/**
	 * The size of one audio frame passed to libasound.
	 */
	size_t out_frame_size;
105 106 107 108 109 110

	/**
	 * The size of one period, in number of frames.
	 */
	snd_pcm_uframes_t period_frames;

111 112 113 114 115 116 117 118 119 120 121
	/**
	 * Is this a buggy alsa-lib version, which needs a workaround
	 * for the snd_pcm_drain() bug always returning -EAGAIN?  See
	 * alsa-lib commits fdc898d41135 and e4377b16454f for details.
	 * This bug was fixed in alsa-lib version 1.1.4.
	 *
	 * The workaround is to re-enable blocking mode for the
	 * snd_pcm_drain() call.
	 */
	bool work_around_drain_bug;

122
	/**
123 124
	 * After Open(), has this output been activated by a Play()
	 * command?
125
	 *
126
	 * Protected by #mutex.
127
	 */
128
	bool active;
129

130
	/**
131 132 133 134 135 136 137 138
	 * Do we need to call snd_pcm_prepare() before the next write?
	 * It means that we put the device to SND_PCM_STATE_SETUP by
	 * calling snd_pcm_drop().
	 *
	 * Without this flag, we could easily recover after a failed
	 * optimistic write (returning -EBADFD), but the Raspberry Pi
	 * audio driver is infamous for generating ugly artefacts from
	 * this.
139
	 */
140
	bool must_prepare;
141

142 143
	bool drain;

144 145 146 147 148
	/**
	 * This buffer gets allocated after opening the ALSA device.
	 * It contains silence samples, enough to fill one period (see
	 * #period_frames).
	 */
149
	uint8_t *silence;
150

151 152 153 154 155 156 157 158 159 160
	/**
	 * For PrepareAlsaPcmSockets().
	 */
	ReusableArray<pollfd> pfd_buffer;

	/**
	 * For copying data from OutputThread to IOThread.
	 */
	boost::lockfree::spsc_queue<uint8_t> *ring_buffer;

161
	Alsa::PeriodBuffer period_buffer;
162 163

	/**
164
	 * Protects #cond, #error, #active, #drain.
165 166 167 168 169 170 171 172 173 174 175 176
	 */
	mutable Mutex mutex;

	/**
	 * Used to wait when #ring_buffer is full.  It will be
	 * signalled each time data is popped from the #ring_buffer,
	 * making space for more data.
	 */
	Cond cond;

	std::exception_ptr error;

177
public:
178
	AlsaOutput(EventLoop &loop, const ConfigBlock &block);
179

180
	~AlsaOutput() noexcept {
181 182 183 184
		/* free libasound's config cache */
		snd_config_update_free_global();
	}

185 186
	using MultiSocketMonitor::GetEventLoop;

187
	gcc_pure
188
	const char *GetDevice() const noexcept {
189 190 191
		return device.empty() ? default_device : device.c_str();
	}

192 193 194 195
	static AudioOutput *Create(EventLoop &event_loop,
				   const ConfigBlock &block) {
		return new AlsaOutput(event_loop, block);
	}
196

197
private:
198 199 200
	const std::map<std::string, std::string> GetAttributes() const noexcept override;
	void SetAttribute(std::string &&name, std::string &&value) override;

201 202
	void Enable() override;
	void Disable() noexcept override;
203

204 205
	void Open(AudioFormat &audio_format) override;
	void Close() noexcept override;
206

207 208 209
	size_t Play(const void *chunk, size_t size) override;
	void Drain() override;
	void Cancel() noexcept override;
210

211 212 213 214 215 216 217 218
	/**
	 * Set up the snd_pcm_t object which was opened by the caller.
	 * Set up the configured settings and the audio format.
	 *
	 * Throws #std::runtime_error on error.
	 */
	void Setup(AudioFormat &audio_format, PcmExport::Params &params);

219
#ifdef ENABLE_DSD
220 221
	void SetupDop(AudioFormat audio_format,
		      PcmExport::Params &params);
222 223
#endif

224 225 226 227 228
	void SetupOrDop(AudioFormat &audio_format, PcmExport::Params &params
#ifdef ENABLE_DSD
			, bool dop
#endif
			);
229

230 231 232 233 234 235
	gcc_pure
	bool LockIsActive() const noexcept {
		const std::lock_guard<Mutex> lock(mutex);
		return active;
	}

236 237 238 239 240
	/**
	 * Activate the output by registering the sockets in the
	 * #EventLoop.  Before calling this, filling the ring buffer
	 * has no effect; nothing will be played, and no code will be
	 * run on #EventLoop's thread.
241 242
	 *
	 * Caller must hold the mutex.
243 244 245
	 *
	 * @return true if Activate() was called, false if the mutex
	 * was never unlocked
246
	 */
247
	bool Activate() noexcept {
248
		if (active)
249
			return false;
250

251 252
		active = true;

253
		const ScopeUnlock unlock(mutex);
254
		defer_invalidate_sockets.Schedule();
255
		return true;
256 257
	}

258
	void ClearRingBuffer() noexcept {
259 260 261 262
		std::array<uint8_t, 1024> buffer;
		while (ring_buffer->pop(&buffer.front(), buffer.size())) {}
	}

263
	int Recover(int err) noexcept;
264 265

	/**
266 267 268 269
	 * Drain all buffers.  To be run in #EventLoop's thread.
	 *
	 * @return true if draining is complete, false if this method
	 * needs to be called again later
270
	 */
271
	bool DrainInternal() noexcept;
272 273 274 275 276

	/**
	 * Stop playback immediately, dropping all buffers.  To be run
	 * in #EventLoop's thread.
	 */
277
	void CancelInternal() noexcept;
278

279 280 281 282
	/**
	 * @return false if no data was moved
	 */
	bool CopyRingToPeriodBuffer() noexcept {
283
		if (period_buffer.IsFull())
284
			return false;
285 286 287 288

		size_t nbytes = ring_buffer->pop(period_buffer.GetTail(),
						 period_buffer.GetSpaceBytes());
		if (nbytes == 0)
289
			return false;
290 291 292 293 294 295 296

		period_buffer.AppendBytes(nbytes);

		const std::lock_guard<Mutex> lock(mutex);
		/* notify the OutputThread that there is now
		   room in ring_buffer */
		cond.signal();
297 298

		return true;
299 300
	}

301
	snd_pcm_sframes_t WriteFromPeriodBuffer() noexcept {
302 303 304 305 306 307 308 309 310 311 312
		assert(!period_buffer.IsEmpty());

		auto frames_written = snd_pcm_writei(pcm, period_buffer.GetHead(),
						     period_buffer.GetFrames(out_frame_size));
		if (frames_written > 0)
			period_buffer.ConsumeFrames(frames_written,
						    out_frame_size);

		return frames_written;
	}

313 314 315
	void LockCaughtError() noexcept {
		const std::lock_guard<Mutex> lock(mutex);
		error = std::current_exception();
316
		active = false;
317 318 319
		cond.signal();
	}

320
	/* virtual methods from class MultiSocketMonitor */
321 322
	std::chrono::steady_clock::duration PrepareSockets() noexcept override;
	void DispatchSockets() noexcept override;
Max Kellermann's avatar
Max Kellermann committed
323
};
324

325
static constexpr Domain alsa_output_domain("alsa_output");
326

327
AlsaOutput::AlsaOutput(EventLoop &_loop, const ConfigBlock &block)
328
	:AudioOutput(FLAG_ENABLE_DISABLE),
329 330
	 MultiSocketMonitor(_loop),
	 defer_invalidate_sockets(_loop, BIND_THIS_METHOD(InvalidateSockets)),
331
	 device(block.GetBlockValue("device", "")),
332
#ifdef ENABLE_DSD
333 334 335
	 dop_setting(block.GetBlockValue("dop", false) ||
		     /* legacy name from MPD 0.18 and older: */
		     block.GetBlockValue("dsd_usb", false)),
336
#endif
337 338 339
	 buffer_time(block.GetPositiveValue("buffer_time",
					    MPD_ALSA_BUFFER_TIME_US)),
	 period_time(block.GetPositiveValue("period_time", 0u))
340
{
341
#ifdef SND_PCM_NO_AUTO_RESAMPLE
342
	if (!block.GetBlockValue("auto_resample", true))
343
		mode |= SND_PCM_NO_AUTO_RESAMPLE;
344
#endif
345

346
#ifdef SND_PCM_NO_AUTO_CHANNELS
347
	if (!block.GetBlockValue("auto_channels", true))
348
		mode |= SND_PCM_NO_AUTO_CHANNELS;
349
#endif
350

351
#ifdef SND_PCM_NO_AUTO_FORMAT
352
	if (!block.GetBlockValue("auto_format", true))
353
		mode |= SND_PCM_NO_AUTO_FORMAT;
354
#endif
355 356 357 358 359

	const char *allowed_formats_string =
		block.GetBlockValue("allowed_formats", nullptr);
	if (allowed_formats_string != nullptr)
		allowed_formats = Alsa::AllowedFormat::ParseList(allowed_formats_string);
360 361
}

362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
const std::map<std::string, std::string>
AlsaOutput::GetAttributes() const noexcept
{
	const std::lock_guard<Mutex> lock(attributes_mutex);

	return {
		std::make_pair("allowed_formats",
			       Alsa::ToString(allowed_formats)),
#ifdef ENABLE_DSD
		std::make_pair("dop", dop_setting ? "1" : "0"),
#endif
	};
}

void
AlsaOutput::SetAttribute(std::string &&name, std::string &&value)
{
	if (name == "allowed_formats") {
		const std::lock_guard<Mutex> lock(attributes_mutex);
		allowed_formats = Alsa::AllowedFormat::ParseList({value.data(), value.length()});
#ifdef ENABLE_DSD
	} else if (name == "dop") {
		const std::lock_guard<Mutex> lock(attributes_mutex);
		if (value == "0")
			dop_setting = false;
		else if (value == "1")
			dop_setting = true;
		else
			throw std::invalid_argument("Bad 'dop' value");
#endif
	} else
		AudioOutput::SetAttribute(std::move(name), std::move(value));
}

396
void
397
AlsaOutput::Enable()
398
{
399
	pcm_export.Construct();
400 401
}

402 403
void
AlsaOutput::Disable() noexcept
404
{
405
	pcm_export.Destruct();
406 407
}

Max Kellermann's avatar
Max Kellermann committed
408
static bool
409
alsa_test_default_device()
410
{
Avuton Olrich's avatar
Avuton Olrich committed
411
	snd_pcm_t *handle;
412

413
	int ret = snd_pcm_open(&handle, default_device,
414
			       SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
Avuton Olrich's avatar
Avuton Olrich committed
415
	if (ret) {
416 417 418
		FormatError(alsa_output_domain,
			    "Error opening default ALSA device: %s",
			    snd_strerror(-ret));
419
		return false;
Avuton Olrich's avatar
Avuton Olrich committed
420 421
	} else
		snd_pcm_close(handle);
422

423
	return true;
424 425
}

426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
/**
 * Wrapper for snd_pcm_sw_params().
 */
static void
AlsaSetupSw(snd_pcm_t *pcm, snd_pcm_uframes_t start_threshold,
	    snd_pcm_uframes_t avail_min)
{
	snd_pcm_sw_params_t *swparams;
	snd_pcm_sw_params_alloca(&swparams);

	int err = snd_pcm_sw_params_current(pcm, swparams);
	if (err < 0)
		throw FormatRuntimeError("snd_pcm_sw_params_current() failed: %s",
					 snd_strerror(-err));

	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));

	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));

	err = snd_pcm_sw_params(pcm, swparams);
	if (err < 0)
		throw FormatRuntimeError("snd_pcm_sw_params() failed: %s",
					 snd_strerror(-err));
}

458 459 460
inline void
AlsaOutput::Setup(AudioFormat &audio_format,
		  PcmExport::Params &params)
461
{
462 463 464
	const auto hw_result = Alsa::SetupHw(pcm,
					     buffer_time, period_time,
					     audio_format, params);
465

466 467 468
	FormatDebug(alsa_output_domain, "format=%s (%s)",
		    snd_pcm_format_name(hw_result.format),
		    snd_pcm_format_description(hw_result.format));
Avuton Olrich's avatar
Avuton Olrich committed
469

470
	FormatDebug(alsa_output_domain, "buffer_size=%u period_size=%u",
471 472 473 474 475
		    (unsigned)hw_result.buffer_size,
		    (unsigned)hw_result.period_size);

	AlsaSetupSw(pcm, hw_result.buffer_size - hw_result.period_size,
		    hw_result.period_size);
476

477
	auto alsa_period_size = hw_result.period_size;
478 479 480 481 482 483 484 485
	if (alsa_period_size == 0)
		/* this works around a SIGFPE bug that occurred when
		   an ALSA driver indicated period_size==0; this
		   caused a division by zero in alsa_play().  By using
		   the fallback "1", we make sure that this won't
		   happen again. */
		alsa_period_size = 1;

486
	period_frames = alsa_period_size;
487

488
	silence = new uint8_t[snd_pcm_frames_to_bytes(pcm, alsa_period_size)];
489
	snd_pcm_format_set_silence(hw_result.format, silence,
490
				   alsa_period_size * audio_format.channels);
491

492 493
}

494 495
#ifdef ENABLE_DSD

496
inline void
497
AlsaOutput::SetupDop(const AudioFormat audio_format,
498
		     PcmExport::Params &params)
499
{
500
	assert(audio_format.format == SampleFormat::DSD);
501

502
	/* pass 24 bit to AlsaSetup() */
503

504 505
	AudioFormat dop_format = audio_format;
	dop_format.format = SampleFormat::S24_P32;
506

507
	const AudioFormat check = dop_format;
508

509
	Setup(dop_format, params);
510

511
	/* if the device allows only 32 bit, shift all DoP
512 513 514 515
	   samples left by 8 bit and leave the lower 8 bit cleared;
	   the DSD-over-USB documentation does not specify whether
	   this is legal, but there is anecdotical evidence that this
	   is possible (and the only option for some devices) */
516
	params.shift8 = dop_format.format == SampleFormat::S32;
517 518
	if (dop_format.format == SampleFormat::S32)
		dop_format.format = SampleFormat::S24_P32;
519

520
	if (dop_format != check) {
521 522
		/* no bit-perfect playback, which is required
		   for DSD over USB */
523
		delete[] silence;
524
		throw std::runtime_error("Failed to configure DSD-over-PCM");
525 526 527
	}
}

528 529
#endif

530
inline void
531 532 533 534 535
AlsaOutput::SetupOrDop(AudioFormat &audio_format, PcmExport::Params &params
#ifdef ENABLE_DSD
		       , bool dop
#endif
		       )
536
{
537
#ifdef ENABLE_DSD
538 539 540 541
	std::exception_ptr dop_error;
	if (dop && audio_format.format == SampleFormat::DSD) {
		try {
			params.dop = true;
542
			SetupDop(audio_format, params);
543 544 545
			return;
		} catch (...) {
			dop_error = std::current_exception();
546
			params.dop = false;
547
		}
548
	}
549

550 551
	try {
#endif
552
		Setup(audio_format, params);
553
#ifdef ENABLE_DSD
554 555 556 557 558 559 560 561 562
	} catch (...) {
		if (dop_error)
			/* if DoP was attempted, prefer returning the
			   original DoP error instead of the fallback
			   error */
			std::rethrow_exception(dop_error);
		else
			throw;
	}
563
#endif
564 565
}

566 567 568 569 570 571 572 573
static constexpr bool
MaybeDmix(snd_pcm_type_t type)
{
	return type == SND_PCM_TYPE_DMIX || type == SND_PCM_TYPE_PLUG;
}

gcc_pure
static bool
574
MaybeDmix(snd_pcm_t *pcm) noexcept
575 576 577 578
{
	return MaybeDmix(snd_pcm_type(pcm));
}

579 580 581 582 583 584 585 586 587 588 589 590 591
static const Alsa::AllowedFormat &
BestMatch(const std::forward_list<Alsa::AllowedFormat> &haystack,
	  const AudioFormat &needle)
{
	assert(!haystack.empty());

	for (const auto &i : haystack)
		if (needle.MatchMask(i.format))
			return i;

	return haystack.front();
}

592
void
593
AlsaOutput::Open(AudioFormat &audio_format)
594
{
595
#ifdef ENABLE_DSD
596
	bool dop;
597 598
#endif

599 600
	{
		const std::lock_guard<Mutex> lock(attributes_mutex);
601
#ifdef ENABLE_DSD
602
		dop = dop_setting;
603
#endif
604 605 606 607 608 609 610 611 612

		if (!allowed_formats.empty()) {
			const auto &a = BestMatch(allowed_formats,
						  audio_format);
			audio_format.ApplyMask(a.format);
#ifdef ENABLE_DSD
			dop = a.dop;
#endif
		}
613 614
	}

615 616
	int err = snd_pcm_open(&pcm, GetDevice(),
			       SND_PCM_STREAM_PLAYBACK, mode);
617 618 619
	if (err < 0)
		throw FormatRuntimeError("Failed to open ALSA device \"%s\": %s",
					 GetDevice(), snd_strerror(err));
620

621
	FormatDebug(alsa_output_domain, "opened %s type=%s",
622 623
		    snd_pcm_name(pcm),
		    snd_pcm_type_name(snd_pcm_type(pcm)));
624

625 626 627
	PcmExport::Params params;
	params.alsa_channel_order = true;

628
	try {
629 630 631 632 633
		SetupOrDop(audio_format, params
#ifdef ENABLE_DSD
			   , dop
#endif
			   );
634
	} catch (...) {
635
		snd_pcm_close(pcm);
636 637
		std::throw_with_nested(FormatRuntimeError("Error opening ALSA device \"%s\"",
							  GetDevice()));
638 639
	}

640 641 642
	work_around_drain_bug = MaybeDmix(pcm) &&
		GetRuntimeAlsaVersion() < MakeAlsaVersion(1, 1, 4);

643 644
	snd_pcm_nonblock(pcm, 1);

645 646 647 648 649
#ifdef ENABLE_DSD
	if (params.dop)
		FormatDebug(alsa_output_domain, "DoP (DSD over PCM) enabled");
#endif

650 651 652 653
	pcm_export->Open(audio_format.format,
			 audio_format.channels,
			 params);

654
#ifndef NDEBUG
655
	in_frame_size = audio_format.GetFrameSize();
656
#endif
657
	out_frame_size = pcm_export->GetFrameSize(audio_format);
658

659 660 661 662 663 664 665 666
	drain = false;

	size_t period_size = period_frames * out_frame_size;
	ring_buffer = new boost::lockfree::spsc_queue<uint8_t>(period_size * 4);

	period_buffer.Allocate(period_frames, out_frame_size);

	active = false;
667
	must_prepare = false;
668 669
}

670
inline int
671
AlsaOutput::Recover(int err) noexcept
Avuton Olrich's avatar
Avuton Olrich committed
672 673
{
	if (err == -EPIPE) {
674
		FormatDebug(alsa_output_domain,
675 676
			    "Underrun on ALSA device \"%s\"",
			    GetDevice());
Avuton Olrich's avatar
Avuton Olrich committed
677
	} else if (err == -ESTRPIPE) {
678 679
		FormatDebug(alsa_output_domain,
			    "ALSA device \"%s\" was suspended",
680
			    GetDevice());
681 682
	}

683
	switch (snd_pcm_state(pcm)) {
Warren Dukes's avatar
Warren Dukes committed
684
	case SND_PCM_STATE_PAUSED:
685
		err = snd_pcm_pause(pcm, /* disable */ 0);
Warren Dukes's avatar
Warren Dukes committed
686 687
		break;
	case SND_PCM_STATE_SUSPENDED:
688
		err = snd_pcm_resume(pcm);
689 690 691
		if (err == -EAGAIN)
			return 0;
		/* fall-through to snd_pcm_prepare: */
692 693 694
#if GCC_CHECK_VERSION(7,0)
		[[fallthrough]];
#endif
695
	case SND_PCM_STATE_OPEN:
696 697
	case SND_PCM_STATE_SETUP:
	case SND_PCM_STATE_XRUN:
698
		period_buffer.Rewind();
699
		err = snd_pcm_prepare(pcm);
Warren Dukes's avatar
Warren Dukes committed
700
		break;
701 702
	case SND_PCM_STATE_DISCONNECTED:
		break;
Max Kellermann's avatar
Max Kellermann committed
703
	/* this is no error, so just keep running */
704
	case SND_PCM_STATE_PREPARED:
Max Kellermann's avatar
Max Kellermann committed
705
	case SND_PCM_STATE_RUNNING:
706
	case SND_PCM_STATE_DRAINING:
Max Kellermann's avatar
Max Kellermann committed
707 708
		err = 0;
		break;
709 710 711 712 713 714

	default:
		/* this default case is just here to work around
		   -Wswitch due to SND_PCM_STATE_PRIVATE1 (libasound
		   1.1.6) */
		break;
715 716 717 718 719
	}

	return err;
}

720
inline bool
721
AlsaOutput::DrainInternal() noexcept
722
{
723 724 725 726
	if (snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING) {
		CancelInternal();
		return true;
	}
727

728 729 730 731 732
	/* drain ring_buffer */
	CopyRingToPeriodBuffer();

	auto period_position = period_buffer.GetPeriodPosition(out_frame_size);
	if (period_position > 0)
733 734
		/* generate some silence to finish the partial
		   period */
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
		period_buffer.FillWithSilence(silence, out_frame_size);

	/* drain period_buffer */
	if (!period_buffer.IsEmpty()) {
		auto frames_written = WriteFromPeriodBuffer();
		if (frames_written < 0 && errno != EAGAIN) {
			CancelInternal();
			return true;
		}

		if (!period_buffer.IsEmpty())
			/* need to call WriteFromPeriodBuffer() again
			   in the next iteration, so don't finish the
			   drain just yet */
			return false;
750 751
	}

752
	/* .. and finally drain the ALSA hardware buffer */
753 754 755 756 757 758 759 760

	if (work_around_drain_bug) {
		snd_pcm_nonblock(pcm, 0);
		bool result = snd_pcm_drain(pcm) != -EAGAIN;
		snd_pcm_nonblock(pcm, 1);
		return result;
	}

761 762
	return snd_pcm_drain(pcm) != -EAGAIN;
}
763

764
void
765 766 767 768
AlsaOutput::Drain()
{
	const std::lock_guard<Mutex> lock(mutex);

769 770 771
	if (error)
		std::rethrow_exception(error);

772 773
	drain = true;

774
	Activate();
775

776
	while (drain && active)
777
		cond.wait(mutex);
778 779 780

	if (error)
		std::rethrow_exception(error);
781 782
}

783
inline void
784
AlsaOutput::CancelInternal() noexcept
Avuton Olrich's avatar
Avuton Olrich committed
785
{
786
	must_prepare = true;
787

788
	snd_pcm_drop(pcm);
789 790

	pcm_export->Reset();
791 792
	period_buffer.Clear();
	ClearRingBuffer();
793 794 795 796 797 798 799 800

	{
		const std::lock_guard<Mutex> lock(mutex);
		active = false;
	}

	MultiSocketMonitor::Reset();
	defer_invalidate_sockets.Cancel();
801 802
}

803 804
void
AlsaOutput::Cancel() noexcept
805
{
806
	if (!LockIsActive()) {
807 808 809 810 811 812 813 814 815 816
		/* early cancel, quick code path without thread
		   synchronization */

		pcm_export->Reset();
		assert(period_buffer.IsEmpty());
		ClearRingBuffer();

		return;
	}

817
	BlockingCall(GetEventLoop(), [this](){
818 819
			CancelInternal();
		});
820 821
}

822 823
void
AlsaOutput::Close() noexcept
Avuton Olrich's avatar
Avuton Olrich committed
824
{
825
	/* make sure the I/O thread isn't inside DispatchSockets() */
826
	BlockingCall(GetEventLoop(), [this](){
827
			MultiSocketMonitor::Reset();
828
			defer_invalidate_sockets.Cancel();
829 830 831 832
		});

	period_buffer.Free();
	delete ring_buffer;
833 834
	snd_pcm_close(pcm);
	delete[] silence;
835 836
}

837
size_t
838
AlsaOutput::Play(const void *chunk, size_t size)
839
{
840 841
	assert(size > 0);
	assert(size % in_frame_size == 0);
842

843 844 845 846 847 848 849 850 851
	const auto e = pcm_export->Export({chunk, size});
	if (e.size == 0)
		/* the DoP (DSD over PCM) filter converts two frames
		   at a time and ignores the last odd frame; if there
		   was only one frame (e.g. the last frame in the
		   file), the result is empty; to avoid an endless
		   loop, bail out here, and pretend the one frame has
		   been played */
		return size;
852

853
	const std::lock_guard<Mutex> lock(mutex);
854 855

	while (true) {
856 857 858
		if (error)
			std::rethrow_exception(error);

859 860
		size_t bytes_written = ring_buffer->push((const uint8_t *)e.data,
							 e.size);
861 862 863 864 865 866
		if (bytes_written > 0)
			return pcm_export->CalcSourceSize(bytes_written);

		/* now that the ring_buffer is full, we can activate
		   the socket handlers to trigger the first
		   snd_pcm_writei() */
867
		if (Activate())
868 869 870 871 872
			/* since everything may have changed while the
			   mutex was unlocked, we need to skip the
			   cond.wait() call below and check the new
			   status */
			continue;
873

874 875 876 877 878
		/* wait for the DispatchSockets() to make room in the
		   ring_buffer */
		cond.wait(mutex);
	}
}
879

880
std::chrono::steady_clock::duration
881
AlsaOutput::PrepareSockets() noexcept
882
{
883
	if (!LockIsActive()) {
884 885
		ClearSocketList();
		return std::chrono::steady_clock::duration(-1);
886 887
	}

888 889 890 891 892 893 894
	try {
		return PrepareAlsaPcmSockets(*this, pcm, pfd_buffer);
	} catch (...) {
		ClearSocketList();
		LockCaughtError();
		return std::chrono::steady_clock::duration(-1);
	}
895 896
}

897
void
898
AlsaOutput::DispatchSockets() noexcept
899 900 901
try {
	{
		const std::lock_guard<Mutex> lock(mutex);
902 903 904

		assert(active);

905 906 907 908 909 910 911 912 913 914 915 916 917 918
		if (drain) {
			{
				ScopeUnlock unlock(mutex);
				if (!DrainInternal())
					return;

				MultiSocketMonitor::InvalidateSockets();
			}

			drain = false;
			cond.signal();
			return;
		}
	}
919

920 921
	if (must_prepare) {
		must_prepare = false;
922

923
		int err = snd_pcm_prepare(pcm);
924 925 926
		if (err < 0)
			throw FormatRuntimeError("snd_pcm_prepare() failed: %s",
						 snd_strerror(-err));
927 928
	}

929 930
	CopyRingToPeriodBuffer();

931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
	if (period_buffer.IsEmpty()) {
		if (snd_pcm_state(pcm) == SND_PCM_STATE_PREPARED) {
			/* at SND_PCM_STATE_PREPARED (not yet switched
			   to SND_PCM_STATE_RUNNING), we have no
			   pressure to fill the ALSA buffer, because
			   no xrun can possibly occur; and if no data
			   is available right now, we can easily wait
			   until some is available; so we just stop
			   monitoring the ALSA file descriptor, and
			   let it be reactivated by Play()/Activate()
			   whenever more data arrives */

			{
				const std::lock_guard<Mutex> lock(mutex);
				active = false;
			}

			/* avoid race condition: see if data has
			   arrived meanwhile before disabling the
			   event (but after clearing the "active"
			   flag) */
			if (!CopyRingToPeriodBuffer()) {
				MultiSocketMonitor::Reset();
				defer_invalidate_sockets.Cancel();
			}

			return;
		}

960 961 962
		/* insert some silence if the buffer has not enough
		   data yet, to avoid ALSA xrun */
		period_buffer.FillWithSilence(silence, out_frame_size);
963
	}
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979

	auto frames_written = WriteFromPeriodBuffer();
	if (frames_written < 0) {
		if (frames_written == -EAGAIN || frames_written == -EINTR)
			/* try again in the next DispatchSockets()
			   call which is still scheduled */
			return;

		if (Recover(frames_written) < 0)
			throw FormatRuntimeError("snd_pcm_writei() failed: %s",
						 snd_strerror(-frames_written));

		/* recovered; try again in the next DispatchSockets()
		   call */
		return;
	}
980
} catch (...) {
981
	MultiSocketMonitor::Reset();
982
	LockCaughtError();
983 984
}

985
const struct AudioOutputPlugin alsa_output_plugin = {
986 987
	"alsa",
	alsa_test_default_device,
988
	&AlsaOutput::Create,
989
	&alsa_mixer_plugin,
990
};