AlsaOutputPlugin.cxx 21.5 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 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 "../OutputAPI.hxx"
Max Kellermann's avatar
Max Kellermann committed
23
#include "mixer/MixerList.hxx"
24 25
#include "pcm/PcmExport.hxx"
#include "util/Manual.hxx"
26 27
#include "util/Error.hxx"
#include "util/Domain.hxx"
28
#include "Log.hxx"
29

30
#include <alsa/asoundlib.h>
31

32 33
#include <string>

34 35 36
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API

37 38
static const char default_device[] = "default";

39
static constexpr unsigned MPD_ALSA_BUFFER_TIME_US = 500000;
40

41
#define MPD_ALSA_RETRY_NR 5
42

Avuton Olrich's avatar
Avuton Olrich committed
43 44
typedef snd_pcm_sframes_t alsa_writei_t(snd_pcm_t * pcm, const void *buffer,
					snd_pcm_uframes_t size);
45

46
struct AlsaOutput {
47
	AudioOutput base;
48

49
	Manual<PcmExport> pcm_export;
50

51 52 53 54 55
	/**
	 * The configured name of the ALSA device; empty for the
	 * default device
	 */
	std::string device;
56

Max Kellermann's avatar
Max Kellermann committed
57 58 59
	/** use memory mapped I/O? */
	bool use_mmap;

60 61 62 63 64 65 66 67
	/**
	 * Enable DSD over USB according to the dCS suggested
	 * standard?
	 *
	 * @see http://www.dcsltd.co.uk/page/assets/DSDoverUSB.pdf
	 */
	bool dsd_usb;

Max Kellermann's avatar
Max Kellermann committed
68 69 70 71 72 73
	/** libasound's buffer_time setting (in microseconds) */
	unsigned int buffer_time;

	/** libasound's period_time setting (in microseconds) */
	unsigned int period_time;

74 75 76
	/** the mode flags passed to snd_pcm_open */
	int mode;

Max Kellermann's avatar
Max Kellermann committed
77
	/** the libasound PCM device handle */
Max Kellermann's avatar
Max Kellermann committed
78
	snd_pcm_t *pcm;
Max Kellermann's avatar
Max Kellermann committed
79 80 81 82 83 84

	/**
	 * a pointer to the libasound writei() function, which is
	 * snd_pcm_writei() or snd_pcm_mmap_writei(), depending on the
	 * use_mmap configuration
	 */
Avuton Olrich's avatar
Avuton Olrich committed
85
	alsa_writei_t *writei;
Max Kellermann's avatar
Max Kellermann committed
86

87 88 89 90 91 92 93 94 95
	/**
	 * The size of one audio frame passed to method play().
	 */
	size_t in_frame_size;

	/**
	 * The size of one audio frame passed to libasound.
	 */
	size_t out_frame_size;
96 97 98 99 100 101 102 103 104 105

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

	/**
	 * The number of frames written in the current period.
	 */
	snd_pcm_uframes_t period_position;
106

107 108 109 110 111 112 113 114
	/**
	 * Set to non-zero when the Raspberry Pi workaround has been
	 * activated in alsa_recover(); decremented by each write.
	 * This will avoid activating it again, leading to an endless
	 * loop.  This problem was observed with a "RME Digi9636/52".
	 */
	unsigned pi_workaround;

115 116 117 118 119
	/**
	 * This buffer gets allocated after opening the ALSA device.
	 * It contains silence samples, enough to fill one period (see
	 * #period_frames).
	 */
120
	uint8_t *silence;
121

122 123 124
	AlsaOutput()
		:base(alsa_output_plugin),
		 mode(0), writei(snd_pcm_writei) {
125 126
	}

127
	bool Init(const config_param &param, Error &error) {
128
		return base.Configure(param, error);
129
	}
Max Kellermann's avatar
Max Kellermann committed
130
};
131

132
static constexpr Domain alsa_output_domain("alsa_output");
133

134
static const char *
135
alsa_device(const AlsaOutput *ad)
Avuton Olrich's avatar
Avuton Olrich committed
136
{
137
	return ad->device.empty() ? default_device : ad->device.c_str();
138 139
}

140
static void
141
alsa_configure(AlsaOutput *ad, const config_param &param)
142
{
143
	ad->device = param.GetBlockValue("device", "");
144

145
	ad->use_mmap = param.GetBlockValue("use_mmap", false);
146

147
	ad->dsd_usb = param.GetBlockValue("dsd_usb", false);
148

149 150 151
	ad->buffer_time = param.GetBlockValue("buffer_time",
					      MPD_ALSA_BUFFER_TIME_US);
	ad->period_time = param.GetBlockValue("period_time", 0u);
152

153
#ifdef SND_PCM_NO_AUTO_RESAMPLE
154
	if (!param.GetBlockValue("auto_resample", true))
155
		ad->mode |= SND_PCM_NO_AUTO_RESAMPLE;
156
#endif
157

158
#ifdef SND_PCM_NO_AUTO_CHANNELS
159
	if (!param.GetBlockValue("auto_channels", true))
160
		ad->mode |= SND_PCM_NO_AUTO_CHANNELS;
161
#endif
162

163
#ifdef SND_PCM_NO_AUTO_FORMAT
164
	if (!param.GetBlockValue("auto_format", true))
165
		ad->mode |= SND_PCM_NO_AUTO_FORMAT;
166
#endif
167 168
}

169
static AudioOutput *
170
alsa_init(const config_param &param, Error &error)
Avuton Olrich's avatar
Avuton Olrich committed
171
{
172
	AlsaOutput *ad = new AlsaOutput();
173

174
	if (!ad->Init(param, error)) {
175
		delete ad;
176
		return nullptr;
177 178
	}

Max Kellermann's avatar
Max Kellermann committed
179
	alsa_configure(ad, param);
180

181
	return &ad->base;
182 183
}

Max Kellermann's avatar
Max Kellermann committed
184
static void
185
alsa_finish(AudioOutput *ao)
Avuton Olrich's avatar
Avuton Olrich committed
186
{
187
	AlsaOutput *ad = (AlsaOutput *)ao;
188

189
	delete ad;
190 191 192

	/* free libasound's config cache */
	snd_config_update_free_global();
193 194
}

195
static bool
196
alsa_output_enable(AudioOutput *ao, gcc_unused Error &error)
197
{
198
	AlsaOutput *ad = (AlsaOutput *)ao;
199

200
	ad->pcm_export.Construct();
201 202 203 204
	return true;
}

static void
205
alsa_output_disable(AudioOutput *ao)
206
{
207
	AlsaOutput *ad = (AlsaOutput *)ao;
208

209
	ad->pcm_export.Destruct();
210 211
}

Max Kellermann's avatar
Max Kellermann committed
212 213
static bool
alsa_test_default_device(void)
214
{
Avuton Olrich's avatar
Avuton Olrich committed
215
	snd_pcm_t *handle;
216

217 218
	int ret = snd_pcm_open(&handle, default_device,
	                       SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
Avuton Olrich's avatar
Avuton Olrich committed
219
	if (ret) {
220 221 222
		FormatError(alsa_output_domain,
			    "Error opening default ALSA device: %s",
			    snd_strerror(-ret));
223
		return false;
Avuton Olrich's avatar
Avuton Olrich committed
224 225
	} else
		snd_pcm_close(handle);
226

227
	return true;
228 229
}

Max Kellermann's avatar
Max Kellermann committed
230
static snd_pcm_format_t
231
get_bitformat(SampleFormat sample_format)
232
{
233
	switch (sample_format) {
234 235
	case SampleFormat::UNDEFINED:
	case SampleFormat::DSD:
236 237
		return SND_PCM_FORMAT_UNKNOWN;

238
	case SampleFormat::S8:
239 240
		return SND_PCM_FORMAT_S8;

241
	case SampleFormat::S16:
242 243
		return SND_PCM_FORMAT_S16;

244
	case SampleFormat::S24_P32:
245 246
		return SND_PCM_FORMAT_S24;

247
	case SampleFormat::S32:
248
		return SND_PCM_FORMAT_S32;
249

250
	case SampleFormat::FLOAT:
251
		return SND_PCM_FORMAT_FLOAT;
252
	}
253 254

	assert(false);
255
	gcc_unreachable();
256 257
}

258 259 260 261 262 263 264 265 266
static snd_pcm_format_t
byteswap_bitformat(snd_pcm_format_t fmt)
{
	switch(fmt) {
	case SND_PCM_FORMAT_S16_LE: return SND_PCM_FORMAT_S16_BE;
	case SND_PCM_FORMAT_S24_LE: return SND_PCM_FORMAT_S24_BE;
	case SND_PCM_FORMAT_S32_LE: return SND_PCM_FORMAT_S32_BE;
	case SND_PCM_FORMAT_S16_BE: return SND_PCM_FORMAT_S16_LE;
	case SND_PCM_FORMAT_S24_BE: return SND_PCM_FORMAT_S24_LE;
267 268 269 270 271 272 273

	case SND_PCM_FORMAT_S24_3BE:
		return SND_PCM_FORMAT_S24_3LE;

	case SND_PCM_FORMAT_S24_3LE:
		return SND_PCM_FORMAT_S24_3BE;

274 275 276 277
	case SND_PCM_FORMAT_S32_BE: return SND_PCM_FORMAT_S32_LE;
	default: return SND_PCM_FORMAT_UNKNOWN;
	}
}
278

279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
static snd_pcm_format_t
alsa_to_packed_format(snd_pcm_format_t fmt)
{
	switch (fmt) {
	case SND_PCM_FORMAT_S24_LE:
		return SND_PCM_FORMAT_S24_3LE;

	case SND_PCM_FORMAT_S24_BE:
		return SND_PCM_FORMAT_S24_3BE;

	default:
		return SND_PCM_FORMAT_UNKNOWN;
	}
}

static int
alsa_try_format_or_packed(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
			  snd_pcm_format_t fmt, bool *packed_r)
{
	int err = snd_pcm_hw_params_set_format(pcm, hwparams, fmt);
	if (err == 0)
		*packed_r = false;

	if (err != -EINVAL)
		return err;

	fmt = alsa_to_packed_format(fmt);
	if (fmt == SND_PCM_FORMAT_UNKNOWN)
		return -EINVAL;

	err = snd_pcm_hw_params_set_format(pcm, hwparams, fmt);
	if (err == 0)
		*packed_r = true;

	return err;
}

316
/**
317 318
 * Attempts to configure the specified sample format, and tries the
 * reversed host byte order if was not supported.
319 320 321
 */
static int
alsa_output_try_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
322
		       SampleFormat sample_format,
323
		       bool *packed_r, bool *reverse_endian_r)
324 325 326 327 328
{
	snd_pcm_format_t alsa_format = get_bitformat(sample_format);
	if (alsa_format == SND_PCM_FORMAT_UNKNOWN)
		return -EINVAL;

329 330
	int err = alsa_try_format_or_packed(pcm, hwparams, alsa_format,
					    packed_r);
331
	if (err == 0)
332
		*reverse_endian_r = false;
333

334 335
	if (err != -EINVAL)
		return err;
336

337
	alsa_format = byteswap_bitformat(alsa_format);
338 339 340
	if (alsa_format == SND_PCM_FORMAT_UNKNOWN)
		return -EINVAL;

341
	err = alsa_try_format_or_packed(pcm, hwparams, alsa_format, packed_r);
342
	if (err == 0)
343
		*reverse_endian_r = true;
344 345 346 347

	return err;
}

348
/**
349
 * Configure a sample format, and probe other formats if that fails.
350
 */
351 352
static int
alsa_output_setup_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
353
			 AudioFormat &audio_format,
354
			 bool *packed_r, bool *reverse_endian_r)
355
{
356
	/* try the input format first */
357

358
	int err = alsa_output_try_format(pcm, hwparams,
359
					 audio_format.format,
360
					 packed_r, reverse_endian_r);
361

362
	/* if unsupported by the hardware, try other formats */
363

364 365 366 367 368 369
	static const SampleFormat probe_formats[] = {
		SampleFormat::S24_P32,
		SampleFormat::S32,
		SampleFormat::S16,
		SampleFormat::S8,
		SampleFormat::UNDEFINED,
370
	};
371

372
	for (unsigned i = 0;
373
	     err == -EINVAL && probe_formats[i] != SampleFormat::UNDEFINED;
374
	     ++i) {
375 376
		const SampleFormat mpd_format = probe_formats[i];
		if (mpd_format == audio_format.format)
377
			continue;
378

379
		err = alsa_output_try_format(pcm, hwparams, mpd_format,
380
					     packed_r, reverse_endian_r);
381
		if (err == 0)
382
			audio_format.format = mpd_format;
383
	}
384

385
	return err;
386 387 388 389 390 391 392
}

/**
 * Set up the snd_pcm_t object which was opened by the caller.  Set up
 * the configured settings and the audio format.
 */
static bool
393
alsa_setup(AlsaOutput *ad, AudioFormat &audio_format,
394
	   bool *packed_r, bool *reverse_endian_r, Error &error)
395
{
396 397
	unsigned int sample_rate = audio_format.sample_rate;
	unsigned int channels = audio_format.channels;
398
	int err;
399
	const char *cmd = nullptr;
400 401 402 403 404 405 406
	int retry = MPD_ALSA_RETRY_NR;
	unsigned int period_time, period_time_ro;
	unsigned int buffer_time;

	period_time_ro = period_time = ad->period_time;
configure_hw:
	/* configure HW params */
407
	snd_pcm_hw_params_t *hwparams;
408 409 410 411 412 413 414 415 416 417
	snd_pcm_hw_params_alloca(&hwparams);
	cmd = "snd_pcm_hw_params_any";
	err = snd_pcm_hw_params_any(ad->pcm, hwparams);
	if (err < 0)
		goto error;

	if (ad->use_mmap) {
		err = snd_pcm_hw_params_set_access(ad->pcm, hwparams,
						   SND_PCM_ACCESS_MMAP_INTERLEAVED);
		if (err < 0) {
418 419 420 421 422
			FormatWarning(alsa_output_domain,
				      "Cannot set mmap'ed mode on ALSA device \"%s\": %s",
				      alsa_device(ad), snd_strerror(-err));
			LogWarning(alsa_output_domain,
				   "Falling back to direct write mode");
423 424 425 426 427 428 429 430 431 432 433 434 435 436
			ad->use_mmap = false;
		} else
			ad->writei = snd_pcm_mmap_writei;
	}

	if (!ad->use_mmap) {
		cmd = "snd_pcm_hw_params_set_access";
		err = snd_pcm_hw_params_set_access(ad->pcm, hwparams,
						   SND_PCM_ACCESS_RW_INTERLEAVED);
		if (err < 0)
			goto error;
		ad->writei = snd_pcm_writei;
	}

437
	err = alsa_output_setup_format(ad->pcm, hwparams, audio_format,
438
				       packed_r, reverse_endian_r);
Avuton Olrich's avatar
Avuton Olrich committed
439
	if (err < 0) {
440 441 442 443 444
		error.Format(alsa_output_domain, err,
			     "ALSA device \"%s\" does not support format %s: %s",
			     alsa_device(ad),
			     sample_format_to_string(audio_format.format),
			     snd_strerror(-err));
445
		return false;
446 447
	}

448 449
	snd_pcm_format_t format;
	if (snd_pcm_hw_params_get_format(hwparams, &format) == 0)
450 451 452
		FormatDebug(alsa_output_domain,
			    "format=%s (%s)", snd_pcm_format_name(format),
			    snd_pcm_format_description(format));
453

Max Kellermann's avatar
Max Kellermann committed
454
	err = snd_pcm_hw_params_set_channels_near(ad->pcm, hwparams,
Avuton Olrich's avatar
Avuton Olrich committed
455 456
						  &channels);
	if (err < 0) {
457 458 459 460
		error.Format(alsa_output_domain, err,
			     "ALSA device \"%s\" does not support %i channels: %s",
			     alsa_device(ad), (int)audio_format.channels,
			     snd_strerror(-err));
461
		return false;
462
	}
463
	audio_format.channels = (int8_t)channels;
464

Max Kellermann's avatar
Max Kellermann committed
465
	err = snd_pcm_hw_params_set_rate_near(ad->pcm, hwparams,
466
					      &sample_rate, nullptr);
467
	if (err < 0 || sample_rate == 0) {
468 469 470
		error.Format(alsa_output_domain, err,
			     "ALSA device \"%s\" does not support %u Hz audio",
			     alsa_device(ad), audio_format.sample_rate);
471
		return false;
472
	}
473
	audio_format.sample_rate = sample_rate;
474

475 476 477 478 479 480
	snd_pcm_uframes_t buffer_size_min, buffer_size_max;
	snd_pcm_hw_params_get_buffer_size_min(hwparams, &buffer_size_min);
	snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size_max);
	unsigned buffer_time_min, buffer_time_max;
	snd_pcm_hw_params_get_buffer_time_min(hwparams, &buffer_time_min, 0);
	snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time_max, 0);
481 482 483
	FormatDebug(alsa_output_domain, "buffer: size=%u..%u time=%u..%u",
		    (unsigned)buffer_size_min, (unsigned)buffer_size_max,
		    buffer_time_min, buffer_time_max);
484 485 486 487 488 489 490

	snd_pcm_uframes_t period_size_min, period_size_max;
	snd_pcm_hw_params_get_period_size_min(hwparams, &period_size_min, 0);
	snd_pcm_hw_params_get_period_size_max(hwparams, &period_size_max, 0);
	unsigned period_time_min, period_time_max;
	snd_pcm_hw_params_get_period_time_min(hwparams, &period_time_min, 0);
	snd_pcm_hw_params_get_period_time_max(hwparams, &period_time_max, 0);
491 492 493
	FormatDebug(alsa_output_domain, "period: size=%u..%u time=%u..%u",
		    (unsigned)period_size_min, (unsigned)period_size_max,
		    period_time_min, period_time_max);
494

495 496 497
	if (ad->buffer_time > 0) {
		buffer_time = ad->buffer_time;
		cmd = "snd_pcm_hw_params_set_buffer_time_near";
Max Kellermann's avatar
Max Kellermann committed
498
		err = snd_pcm_hw_params_set_buffer_time_near(ad->pcm, hwparams,
499
							     &buffer_time, nullptr);
500 501
		if (err < 0)
			goto error;
502 503
	} else {
		err = snd_pcm_hw_params_get_buffer_time(hwparams, &buffer_time,
504
							nullptr);
505 506
		if (err < 0)
			buffer_time = 0;
507
	}
508

509 510 511
	if (period_time_ro == 0 && buffer_time >= 10000) {
		period_time_ro = period_time = buffer_time / 4;

512 513 514
		FormatDebug(alsa_output_domain,
			    "default period_time = buffer_time/4 = %u/4 = %u",
			    buffer_time, period_time);
515 516
	}

517 518 519
	if (period_time_ro > 0) {
		period_time = period_time_ro;
		cmd = "snd_pcm_hw_params_set_period_time_near";
Max Kellermann's avatar
Max Kellermann committed
520
		err = snd_pcm_hw_params_set_period_time_near(ad->pcm, hwparams,
521
							     &period_time, nullptr);
522 523 524
		if (err < 0)
			goto error;
	}
525

526
	cmd = "snd_pcm_hw_params";
Max Kellermann's avatar
Max Kellermann committed
527
	err = snd_pcm_hw_params(ad->pcm, hwparams);
528
	if (err == -EPIPE && --retry > 0 && period_time_ro > 0) {
529
		period_time_ro = period_time_ro >> 1;
530 531
		goto configure_hw;
	} else if (err < 0)
Avuton Olrich's avatar
Avuton Olrich committed
532
		goto error;
533
	if (retry != MPD_ALSA_RETRY_NR)
534 535
		FormatDebug(alsa_output_domain,
			    "ALSA period_time set to %d", period_time);
536

537
	snd_pcm_uframes_t alsa_buffer_size;
538
	cmd = "snd_pcm_hw_params_get_buffer_size";
539
	err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size);
Avuton Olrich's avatar
Avuton Olrich committed
540 541
	if (err < 0)
		goto error;
542

543
	snd_pcm_uframes_t alsa_period_size;
544
	cmd = "snd_pcm_hw_params_get_period_size";
545
	err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size,
546
						nullptr);
Avuton Olrich's avatar
Avuton Olrich committed
547 548
	if (err < 0)
		goto error;
549

550
	/* configure SW params */
551
	snd_pcm_sw_params_t *swparams;
552 553
	snd_pcm_sw_params_alloca(&swparams);

554
	cmd = "snd_pcm_sw_params_current";
Max Kellermann's avatar
Max Kellermann committed
555
	err = snd_pcm_sw_params_current(ad->pcm, swparams);
Avuton Olrich's avatar
Avuton Olrich committed
556 557
	if (err < 0)
		goto error;
558 559

	cmd = "snd_pcm_sw_params_set_start_threshold";
Max Kellermann's avatar
Max Kellermann committed
560
	err = snd_pcm_sw_params_set_start_threshold(ad->pcm, swparams,
Avuton Olrich's avatar
Avuton Olrich committed
561 562 563 564
						    alsa_buffer_size -
						    alsa_period_size);
	if (err < 0)
		goto error;
565

566
	cmd = "snd_pcm_sw_params_set_avail_min";
Max Kellermann's avatar
Max Kellermann committed
567
	err = snd_pcm_sw_params_set_avail_min(ad->pcm, swparams,
Avuton Olrich's avatar
Avuton Olrich committed
568 569 570
					      alsa_period_size);
	if (err < 0)
		goto error;
571 572

	cmd = "snd_pcm_sw_params";
Max Kellermann's avatar
Max Kellermann committed
573
	err = snd_pcm_sw_params(ad->pcm, swparams);
Avuton Olrich's avatar
Avuton Olrich committed
574 575 576
	if (err < 0)
		goto error;

577 578
	FormatDebug(alsa_output_domain, "buffer_size=%u period_size=%u",
		    (unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
579

580 581 582 583 584 585 586 587
	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;

588 589 590
	ad->period_frames = alsa_period_size;
	ad->period_position = 0;

591 592
	ad->silence = new uint8_t[snd_pcm_frames_to_bytes(ad->pcm,
							  alsa_period_size)];
593 594 595
	snd_pcm_format_set_silence(format, ad->silence,
				   alsa_period_size * channels);

596
	return true;
597

598
error:
599 600 601
	error.Format(alsa_output_domain, err,
		     "Error opening ALSA device \"%s\" (%s): %s",
		     alsa_device(ad), cmd, snd_strerror(-err));
602 603 604
	return false;
}

605
static bool
606
alsa_setup_dsd(AlsaOutput *ad, const AudioFormat audio_format,
607
	       bool *shift8_r, bool *packed_r, bool *reverse_endian_r,
608
	       Error &error)
609 610
{
	assert(ad->dsd_usb);
611
	assert(audio_format.format == SampleFormat::DSD);
612 613 614

	/* pass 24 bit to alsa_setup() */

615 616
	AudioFormat usb_format = audio_format;
	usb_format.format = SampleFormat::S24_P32;
617
	usb_format.sample_rate /= 2;
618

619
	const AudioFormat check = usb_format;
620

621
	if (!alsa_setup(ad, usb_format, packed_r, reverse_endian_r, error))
622 623
		return false;

624 625 626 627 628
	/* if the device allows only 32 bit, shift all DSD-over-USB
	   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) */
629 630 631
	*shift8_r = usb_format.format == SampleFormat::S32;
	if (usb_format.format == SampleFormat::S32)
		usb_format.format = SampleFormat::S24_P32;
632

633
	if (usb_format != check) {
634 635
		/* no bit-perfect playback, which is required
		   for DSD over USB */
636 637 638
		error.Format(alsa_output_domain,
			     "Failed to configure DSD-over-USB on ALSA device \"%s\"",
			     alsa_device(ad));
639
		delete[] ad->silence;
640 641 642 643 644 645 646
		return false;
	}

	return true;
}

static bool
647
alsa_setup_or_dsd(AlsaOutput *ad, AudioFormat &audio_format,
648
		  Error &error)
649
{
650
	bool shift8 = false, packed, reverse_endian;
651

652
	const bool dsd_usb = ad->dsd_usb &&
653
		audio_format.format == SampleFormat::DSD;
654
	const bool success = dsd_usb
655 656
		? alsa_setup_dsd(ad, audio_format,
				 &shift8, &packed, &reverse_endian,
657
				 error)
658
		: alsa_setup(ad, audio_format, &packed, &reverse_endian,
659
			     error);
660 661 662
	if (!success)
		return false;

663 664
	ad->pcm_export->Open(audio_format.format,
			     audio_format.channels,
665
			     dsd_usb, shift8, packed, reverse_endian);
666
	return true;
667 668
}

669
static bool
670
alsa_open(AudioOutput *ao, AudioFormat &audio_format, Error &error)
671
{
672
	AlsaOutput *ad = (AlsaOutput *)ao;
673

674 675
	ad->pi_workaround = 0;

676 677
	int err = snd_pcm_open(&ad->pcm, alsa_device(ad),
			       SND_PCM_STREAM_PLAYBACK, ad->mode);
678
	if (err < 0) {
679
		error.Format(alsa_output_domain, err,
680 681
			    "Failed to open ALSA device \"%s\": %s",
			    alsa_device(ad), snd_strerror(err));
682
		return false;
683
	}
684

685 686 687
	FormatDebug(alsa_output_domain, "opened %s type=%s",
		    snd_pcm_name(ad->pcm),
		    snd_pcm_type_name(snd_pcm_type(ad->pcm)));
688

689
	if (!alsa_setup_or_dsd(ad, audio_format, error)) {
Max Kellermann's avatar
Max Kellermann committed
690
		snd_pcm_close(ad->pcm);
691 692 693
		return false;
	}

694 695
	ad->in_frame_size = audio_format.GetFrameSize();
	ad->out_frame_size = ad->pcm_export->GetFrameSize(audio_format);
696 697

	return true;
698 699
}

700 701 702 703 704 705
/**
 * Write silence to the ALSA device.
 */
static void
alsa_write_silence(AlsaOutput *ad, snd_pcm_uframes_t nframes)
{
706
	ad->writei(ad->pcm, ad->silence, nframes);
707 708
}

Max Kellermann's avatar
Max Kellermann committed
709
static int
710
alsa_recover(AlsaOutput *ad, int err)
Avuton Olrich's avatar
Avuton Olrich committed
711 712
{
	if (err == -EPIPE) {
713 714
		FormatDebug(alsa_output_domain,
			    "Underrun on ALSA device \"%s\"", alsa_device(ad));
Avuton Olrich's avatar
Avuton Olrich committed
715
	} else if (err == -ESTRPIPE) {
716 717 718
		FormatDebug(alsa_output_domain,
			    "ALSA device \"%s\" was suspended",
			    alsa_device(ad));
719 720
	}

Max Kellermann's avatar
Max Kellermann committed
721
	switch (snd_pcm_state(ad->pcm)) {
Warren Dukes's avatar
Warren Dukes committed
722
	case SND_PCM_STATE_PAUSED:
Max Kellermann's avatar
Max Kellermann committed
723
		err = snd_pcm_pause(ad->pcm, /* disable */ 0);
Warren Dukes's avatar
Warren Dukes committed
724 725
		break;
	case SND_PCM_STATE_SUSPENDED:
Max Kellermann's avatar
Max Kellermann committed
726
		err = snd_pcm_resume(ad->pcm);
727 728 729
		if (err == -EAGAIN)
			return 0;
		/* fall-through to snd_pcm_prepare: */
730 731
	case SND_PCM_STATE_SETUP:
	case SND_PCM_STATE_XRUN:
732
		ad->period_position = 0;
Max Kellermann's avatar
Max Kellermann committed
733
		err = snd_pcm_prepare(ad->pcm);
734

735
		if (err == 0 && ad->pi_workaround == 0) {
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
			/* this works around a driver bug observed on
			   the Raspberry Pi: after snd_pcm_drop(), the
			   whole ring buffer must be invalidated, but
			   the snd_pcm_prepare() call above makes the
			   driver play random data that just happens
			   to be still in the buffer; by adding and
			   cancelling some silence, this bug does not
			   occur */
			alsa_write_silence(ad, ad->period_frames);

			/* cancel the silence data right away to avoid
			   increasing latency; even though this
			   function call invalidates the portion of
			   silence, the driver seems to avoid the
			   bug */
			snd_pcm_reset(ad->pcm);
752 753 754

			/* disable the workaround for some time */
			ad->pi_workaround = 8;
755 756
		}

Warren Dukes's avatar
Warren Dukes committed
757
		break;
758 759
	case SND_PCM_STATE_DISCONNECTED:
		break;
Max Kellermann's avatar
Max Kellermann committed
760 761 762 763
	/* this is no error, so just keep running */
	case SND_PCM_STATE_RUNNING:
		err = 0;
		break;
764 765 766
	default:
		/* unknown state, do nothing */
		break;
767 768 769 770 771
	}

	return err;
}

772
static void
773
alsa_drain(AudioOutput *ao)
774
{
775
	AlsaOutput *ad = (AlsaOutput *)ao;
776

777 778 779 780 781 782 783 784
	if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING)
		return;

	if (ad->period_position > 0) {
		/* generate some silence to finish the partial
		   period */
		snd_pcm_uframes_t nframes =
			ad->period_frames - ad->period_position;
785
		alsa_write_silence(ad, nframes);
786 787 788 789 790
	}

	snd_pcm_drain(ad->pcm);

	ad->period_position = 0;
791 792
}

Max Kellermann's avatar
Max Kellermann committed
793
static void
794
alsa_cancel(AudioOutput *ao)
Avuton Olrich's avatar
Avuton Olrich committed
795
{
796
	AlsaOutput *ad = (AlsaOutput *)ao;
797

798 799
	ad->period_position = 0;

800
	snd_pcm_drop(ad->pcm);
801 802
}

Max Kellermann's avatar
Max Kellermann committed
803
static void
804
alsa_close(AudioOutput *ao)
Avuton Olrich's avatar
Avuton Olrich committed
805
{
806
	AlsaOutput *ad = (AlsaOutput *)ao;
807

808
	snd_pcm_close(ad->pcm);
809
	delete[] ad->silence;
810 811
}

812
static size_t
813
alsa_play(AudioOutput *ao, const void *chunk, size_t size,
814
	  Error &error)
815
{
816
	AlsaOutput *ad = (AlsaOutput *)ao;
817

818 819
	assert(size % ad->in_frame_size == 0);

820
	chunk = ad->pcm_export->Export(chunk, size, size);
821

822 823 824
	assert(size % ad->out_frame_size == 0);

	size /= ad->out_frame_size;
825

826
	while (true) {
827
		snd_pcm_sframes_t ret = ad->writei(ad->pcm, chunk, size);
828 829 830
		if (ret > 0) {
			ad->period_position = (ad->period_position + ret)
				% ad->period_frames;
831

832 833 834
			if (ad->pi_workaround > 0)
				--ad->pi_workaround;

835
			size_t bytes_written = ret * ad->out_frame_size;
836
			return ad->pcm_export->CalcSourceSize(bytes_written);
837
		}
838 839 840

		if (ret < 0 && ret != -EAGAIN && ret != -EINTR &&
		    alsa_recover(ad, ret) < 0) {
841
			error.Set(alsa_output_domain, ret, snd_strerror(-ret));
842
			return 0;
843 844 845 846
		}
	}
}

847
const struct AudioOutputPlugin alsa_output_plugin = {
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
	"alsa",
	alsa_test_default_device,
	alsa_init,
	alsa_finish,
	alsa_output_enable,
	alsa_output_disable,
	alsa_open,
	alsa_close,
	nullptr,
	nullptr,
	alsa_play,
	alsa_drain,
	alsa_cancel,
	nullptr,

	&alsa_mixer_plugin,
864
};