AlsaOutputPlugin.cxx 22.9 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
	int Recover(int err) noexcept;
259 260

	/**
261 262 263 264
	 * 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
265
	 */
266
	bool DrainInternal() noexcept;
267 268 269 270 271

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

274 275 276 277
	/**
	 * @return false if no data was moved
	 */
	bool CopyRingToPeriodBuffer() noexcept {
278
		if (period_buffer.IsFull())
279
			return false;
280 281 282 283

		size_t nbytes = ring_buffer->pop(period_buffer.GetTail(),
						 period_buffer.GetSpaceBytes());
		if (nbytes == 0)
284
			return false;
285 286 287 288 289 290 291

		period_buffer.AppendBytes(nbytes);

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

		return true;
294 295
	}

296
	snd_pcm_sframes_t WriteFromPeriodBuffer() noexcept {
297 298 299 300 301 302 303 304 305 306 307
		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;
	}

308 309 310
	void LockCaughtError() noexcept {
		const std::lock_guard<Mutex> lock(mutex);
		error = std::current_exception();
311
		active = false;
312 313 314
		cond.signal();
	}

315
	/* virtual methods from class MultiSocketMonitor */
316 317
	std::chrono::steady_clock::duration PrepareSockets() noexcept override;
	void DispatchSockets() noexcept override;
Max Kellermann's avatar
Max Kellermann committed
318
};
319

320
static constexpr Domain alsa_output_domain("alsa_output");
321

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

341
#ifdef SND_PCM_NO_AUTO_CHANNELS
342
	if (!block.GetBlockValue("auto_channels", true))
343
		mode |= SND_PCM_NO_AUTO_CHANNELS;
344
#endif
345

346
#ifdef SND_PCM_NO_AUTO_FORMAT
347
	if (!block.GetBlockValue("auto_format", true))
348
		mode |= SND_PCM_NO_AUTO_FORMAT;
349
#endif
350 351 352 353 354

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

357 358 359 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
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));
}

391
void
392
AlsaOutput::Enable()
393
{
394
	pcm_export.Construct();
395 396
}

397 398
void
AlsaOutput::Disable() noexcept
399
{
400
	pcm_export.Destruct();
401 402
}

Max Kellermann's avatar
Max Kellermann committed
403
static bool
404
alsa_test_default_device()
405
{
Avuton Olrich's avatar
Avuton Olrich committed
406
	snd_pcm_t *handle;
407

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

418
	return true;
419 420
}

421 422 423 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
/**
 * 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));
}

453 454 455
inline void
AlsaOutput::Setup(AudioFormat &audio_format,
		  PcmExport::Params &params)
456
{
457 458 459
	const auto hw_result = Alsa::SetupHw(pcm,
					     buffer_time, period_time,
					     audio_format, params);
460

461 462 463
	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
464

465
	FormatDebug(alsa_output_domain, "buffer_size=%u period_size=%u",
466 467 468 469 470
		    (unsigned)hw_result.buffer_size,
		    (unsigned)hw_result.period_size);

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

472
	auto alsa_period_size = hw_result.period_size;
473 474 475 476 477 478 479 480
	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;

481
	period_frames = alsa_period_size;
482

483
	silence = new uint8_t[snd_pcm_frames_to_bytes(pcm, alsa_period_size)];
484
	snd_pcm_format_set_silence(hw_result.format, silence,
485
				   alsa_period_size * audio_format.channels);
486

487 488
}

489 490
#ifdef ENABLE_DSD

491
inline void
492
AlsaOutput::SetupDop(const AudioFormat audio_format,
493
		     PcmExport::Params &params)
494
{
495
	assert(audio_format.format == SampleFormat::DSD);
496

497
	/* pass 24 bit to AlsaSetup() */
498

499 500
	AudioFormat dop_format = audio_format;
	dop_format.format = SampleFormat::S24_P32;
501

502
	const AudioFormat check = dop_format;
503

504
	Setup(dop_format, params);
505

506
	/* if the device allows only 32 bit, shift all DoP
507 508 509 510
	   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) */
511
	params.shift8 = dop_format.format == SampleFormat::S32;
512 513
	if (dop_format.format == SampleFormat::S32)
		dop_format.format = SampleFormat::S24_P32;
514

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

523 524
#endif

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

545 546
	try {
#endif
547
		Setup(audio_format, params);
548
#ifdef ENABLE_DSD
549 550 551 552 553 554 555 556 557
	} 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;
	}
558
#endif
559 560
}

561 562 563 564 565 566 567 568
static constexpr bool
MaybeDmix(snd_pcm_type_t type)
{
	return type == SND_PCM_TYPE_DMIX || type == SND_PCM_TYPE_PLUG;
}

gcc_pure
static bool
569
MaybeDmix(snd_pcm_t *pcm) noexcept
570 571 572 573
{
	return MaybeDmix(snd_pcm_type(pcm));
}

574 575 576 577 578 579 580 581 582 583 584 585 586
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();
}

587
void
588
AlsaOutput::Open(AudioFormat &audio_format)
589
{
590
#ifdef ENABLE_DSD
591
	bool dop;
592 593
#endif

594 595
	{
		const std::lock_guard<Mutex> lock(attributes_mutex);
596
#ifdef ENABLE_DSD
597
		dop = dop_setting;
598
#endif
599 600 601 602 603 604 605 606 607

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

610 611
	int err = snd_pcm_open(&pcm, GetDevice(),
			       SND_PCM_STREAM_PLAYBACK, mode);
612 613 614
	if (err < 0)
		throw FormatRuntimeError("Failed to open ALSA device \"%s\": %s",
					 GetDevice(), snd_strerror(err));
615

616
	FormatDebug(alsa_output_domain, "opened %s type=%s",
617 618
		    snd_pcm_name(pcm),
		    snd_pcm_type_name(snd_pcm_type(pcm)));
619

620 621 622
	PcmExport::Params params;
	params.alsa_channel_order = true;

623
	try {
624 625 626 627 628
		SetupOrDop(audio_format, params
#ifdef ENABLE_DSD
			   , dop
#endif
			   );
629
	} catch (...) {
630
		snd_pcm_close(pcm);
631 632
		std::throw_with_nested(FormatRuntimeError("Error opening ALSA device \"%s\"",
							  GetDevice()));
633 634
	}

635 636 637
	work_around_drain_bug = MaybeDmix(pcm) &&
		GetRuntimeAlsaVersion() < MakeAlsaVersion(1, 1, 4);

638 639
	snd_pcm_nonblock(pcm, 1);

640 641 642 643 644
#ifdef ENABLE_DSD
	if (params.dop)
		FormatDebug(alsa_output_domain, "DoP (DSD over PCM) enabled");
#endif

645 646 647 648
	pcm_export->Open(audio_format.format,
			 audio_format.channels,
			 params);

649
#ifndef NDEBUG
650
	in_frame_size = audio_format.GetFrameSize();
651
#endif
652
	out_frame_size = pcm_export->GetFrameSize(audio_format);
653

654 655 656 657 658 659 660 661
	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;
662
	must_prepare = false;
663 664
}

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

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

	default:
		/* this default case is just here to work around
		   -Wswitch due to SND_PCM_STATE_PRIVATE1 (libasound
		   1.1.6) */
		break;
710 711 712 713 714
	}

	return err;
}

715
inline bool
716
AlsaOutput::DrainInternal() noexcept
717
{
718 719 720 721
	if (snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING) {
		CancelInternal();
		return true;
	}
722

723 724 725 726 727
	/* drain ring_buffer */
	CopyRingToPeriodBuffer();

	auto period_position = period_buffer.GetPeriodPosition(out_frame_size);
	if (period_position > 0)
728 729
		/* generate some silence to finish the partial
		   period */
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
		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;
745 746
	}

747
	/* .. and finally drain the ALSA hardware buffer */
748 749 750 751 752 753 754 755

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

756 757
	return snd_pcm_drain(pcm) != -EAGAIN;
}
758

759
void
760 761 762 763
AlsaOutput::Drain()
{
	const std::lock_guard<Mutex> lock(mutex);

764 765 766
	if (error)
		std::rethrow_exception(error);

767 768
	drain = true;

769
	Activate();
770

771
	while (drain && active)
772
		cond.wait(mutex);
773 774 775

	if (error)
		std::rethrow_exception(error);
776 777
}

778
inline void
779
AlsaOutput::CancelInternal() noexcept
Avuton Olrich's avatar
Avuton Olrich committed
780
{
781
	must_prepare = true;
782

783
	snd_pcm_drop(pcm);
784 785

	pcm_export->Reset();
786
	period_buffer.Clear();
787
	ring_buffer->reset();
788 789 790 791 792 793 794 795

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

	MultiSocketMonitor::Reset();
	defer_invalidate_sockets.Cancel();
796 797
}

798 799
void
AlsaOutput::Cancel() noexcept
800
{
801
	if (!LockIsActive()) {
802 803 804 805 806
		/* early cancel, quick code path without thread
		   synchronization */

		pcm_export->Reset();
		assert(period_buffer.IsEmpty());
807
		ring_buffer->reset();
808 809 810 811

		return;
	}

812
	BlockingCall(GetEventLoop(), [this](){
813 814
			CancelInternal();
		});
815 816
}

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

	period_buffer.Free();
	delete ring_buffer;
828 829
	snd_pcm_close(pcm);
	delete[] silence;
830 831
}

832
size_t
833
AlsaOutput::Play(const void *chunk, size_t size)
834
{
835 836
	assert(size > 0);
	assert(size % in_frame_size == 0);
837

838 839 840 841 842 843 844 845 846
	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;
847

848
	const std::lock_guard<Mutex> lock(mutex);
849 850

	while (true) {
851 852 853
		if (error)
			std::rethrow_exception(error);

854 855
		size_t bytes_written = ring_buffer->push((const uint8_t *)e.data,
							 e.size);
856 857 858 859 860 861
		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() */
862
		if (Activate())
863 864 865 866 867
			/* 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;
868

869 870 871 872 873
		/* wait for the DispatchSockets() to make room in the
		   ring_buffer */
		cond.wait(mutex);
	}
}
874

875
std::chrono::steady_clock::duration
876
AlsaOutput::PrepareSockets() noexcept
877
{
878
	if (!LockIsActive()) {
879 880
		ClearSocketList();
		return std::chrono::steady_clock::duration(-1);
881 882
	}

883 884 885 886 887 888 889
	try {
		return PrepareAlsaPcmSockets(*this, pcm, pfd_buffer);
	} catch (...) {
		ClearSocketList();
		LockCaughtError();
		return std::chrono::steady_clock::duration(-1);
	}
890 891
}

892
void
893
AlsaOutput::DispatchSockets() noexcept
894 895 896
try {
	{
		const std::lock_guard<Mutex> lock(mutex);
897 898 899

		assert(active);

900 901 902 903 904 905 906 907 908 909 910 911 912 913
		if (drain) {
			{
				ScopeUnlock unlock(mutex);
				if (!DrainInternal())
					return;

				MultiSocketMonitor::InvalidateSockets();
			}

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

915 916
	if (must_prepare) {
		must_prepare = false;
917

918
		int err = snd_pcm_prepare(pcm);
919 920 921
		if (err < 0)
			throw FormatRuntimeError("snd_pcm_prepare() failed: %s",
						 snd_strerror(-err));
922 923
	}

924 925
	CopyRingToPeriodBuffer();

926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
	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;
		}

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

	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;
	}
975
} catch (...) {
976
	MultiSocketMonitor::Reset();
977
	LockCaughtError();
978 979
}

980
const struct AudioOutputPlugin alsa_output_plugin = {
981 982
	"alsa",
	alsa_test_default_device,
983
	&AlsaOutput::Create,
984
	&alsa_mixer_plugin,
985
};