OssOutputPlugin.cxx 15 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 "OssOutputPlugin.hxx"
22
#include "../OutputAPI.hxx"
23
#include "../Wrapper.hxx"
Max Kellermann's avatar
Max Kellermann committed
24
#include "mixer/MixerList.hxx"
25
#include "system/fd_util.h"
26
#include "system/Error.hxx"
27
#include "util/ConstBuffer.hxx"
28
#include "util/Domain.hxx"
29
#include "util/Macros.hxx"
30
#include "system/ByteOrder.hxx"
31
#include "Log.hxx"
32

33 34
#include <stdexcept>

35 36 37
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
38 39 40
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
41
#include <assert.h>
42

43 44
#if defined(__OpenBSD__) || defined(__NetBSD__)
# include <soundcard.h>
Avuton Olrich's avatar
Avuton Olrich committed
45
#else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
46
# include <sys/soundcard.h>
Avuton Olrich's avatar
Avuton Olrich committed
47
#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
48

49 50 51 52 53 54 55 56 57
/* We got bug reports from FreeBSD users who said that the two 24 bit
   formats generate white noise on FreeBSD, but 32 bit works.  This is
   a workaround until we know what exactly is expected by the kernel
   audio drivers. */
#ifndef __linux__
#undef AFMT_S24_PACKED
#undef AFMT_S24_NE
#endif

58
#ifdef AFMT_S24_PACKED
59 60
#include "pcm/PcmExport.hxx"
#include "util/Manual.hxx"
61 62
#endif

63 64 65
class OssOutput {
	friend struct AudioOutputWrapper<OssOutput>;

66
	AudioOutput base;
67

68
#ifdef AFMT_S24_PACKED
69
	Manual<PcmExport> pcm_export;
70 71
#endif

72
	int fd;
73
	const char *device;
74

75 76 77 78
	/**
	 * The current input audio format.  This is needed to reopen
	 * the device after cancel().
	 */
79
	AudioFormat audio_format;
80 81 82 83 84 85

	/**
	 * The current OSS audio format.  This is needed to reopen the
	 * device after cancel().
	 */
	int oss_format;
86

87
public:
88 89
	OssOutput(const ConfigBlock &block, const char *_device=nullptr)
		:base(oss_output_plugin, block),
90
		 fd(-1), device(_device) {}
91

92 93
	static OssOutput *Create(EventLoop &event_loop,
				 const ConfigBlock &block);
94 95

#ifdef AFMT_S24_PACKED
96
	void Enable() {
97 98 99 100 101 102 103 104
		pcm_export.Construct();
	}

	void Disable() {
		pcm_export.Destruct();
	}
#endif

105
	void Open(AudioFormat &audio_format);
106 107 108 109 110

	void Close() {
		DoClose();
	}

111
	size_t Play(const void *chunk, size_t size);
112 113 114 115 116 117
	void Cancel();

private:
	/**
	 * Sets up the OSS device which was opened before.
	 */
118
	void Setup(AudioFormat &audio_format);
119 120 121

	/**
	 * Reopen the device with the saved audio_format, without any probing.
122 123
	 *
	 * Throws #std::runtime_error on error.
124
	 */
125
	void Reopen();
126 127

	void DoClose();
128 129
};

130
static constexpr Domain oss_output_domain("oss_output");
131

132 133 134 135 136 137 138
enum oss_stat {
	OSS_STAT_NO_ERROR = 0,
	OSS_STAT_NOT_CHAR_DEV = -1,
	OSS_STAT_NO_PERMS = -2,
	OSS_STAT_DOESN_T_EXIST = -3,
	OSS_STAT_OTHER = -4,
};
139

140
static enum oss_stat
Max Kellermann's avatar
Max Kellermann committed
141
oss_stat_device(const char *device, int *errno_r)
Avuton Olrich's avatar
Avuton Olrich committed
142
{
143
	struct stat st;
Avuton Olrich's avatar
Avuton Olrich committed
144 145 146

	if (0 == stat(device, &st)) {
		if (!S_ISCHR(st.st_mode)) {
147 148
			return OSS_STAT_NOT_CHAR_DEV;
		}
Avuton Olrich's avatar
Avuton Olrich committed
149
	} else {
Max Kellermann's avatar
Max Kellermann committed
150
		*errno_r = errno;
151

Avuton Olrich's avatar
Avuton Olrich committed
152
		switch (errno) {
153 154 155 156 157 158 159 160 161 162
		case ENOENT:
		case ENOTDIR:
			return OSS_STAT_DOESN_T_EXIST;
		case EACCES:
			return OSS_STAT_NO_PERMS;
		default:
			return OSS_STAT_OTHER;
		}
	}

163
	return OSS_STAT_NO_ERROR;
164 165
}

166
static const char *const default_devices[] = { "/dev/sound/dsp", "/dev/dsp" };
167

Max Kellermann's avatar
Max Kellermann committed
168 169
static bool
oss_output_test_default_device(void)
Avuton Olrich's avatar
Avuton Olrich committed
170
{
171
	int fd, i;
172

173
	for (i = ARRAY_SIZE(default_devices); --i >= 0; ) {
174
		fd = open_cloexec(default_devices[i], O_WRONLY, 0);
175 176

		if (fd >= 0) {
177
			close(fd);
178
			return true;
179
		}
180 181 182 183

		FormatErrno(oss_output_domain,
			    "Error opening OSS device \"%s\"",
			    default_devices[i]);
184 185
	}

186
	return false;
187
}
188

189
static OssOutput *
190
oss_open_default()
191
{
192 193
	int err[ARRAY_SIZE(default_devices)];
	enum oss_stat ret[ARRAY_SIZE(default_devices)];
194

195
	const ConfigBlock empty;
196
	for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) {
Max Kellermann's avatar
Max Kellermann committed
197
		ret[i] = oss_stat_device(default_devices[i], &err[i]);
198 199
		if (ret[i] == OSS_STAT_NO_ERROR)
			return new OssOutput(empty, default_devices[i]);
200 201
	}

202
	for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) {
203 204
		const char *dev = default_devices[i];
		switch(ret[i]) {
205 206 207
		case OSS_STAT_NO_ERROR:
			/* never reached */
			break;
208
		case OSS_STAT_DOESN_T_EXIST:
209 210
			FormatWarning(oss_output_domain,
				      "%s not found", dev);
211 212
			break;
		case OSS_STAT_NOT_CHAR_DEV:
213 214
			FormatWarning(oss_output_domain,
				      "%s is not a character device", dev);
215 216
			break;
		case OSS_STAT_NO_PERMS:
217 218
			FormatWarning(oss_output_domain,
				      "%s: permission denied", dev);
219
			break;
220
		case OSS_STAT_OTHER:
221 222
			FormatErrno(oss_output_domain, err[i],
				    "Error accessing %s", dev);
223 224
		}
	}
225

226
	throw std::runtime_error("error trying to open default OSS device");
227 228
}

229
inline OssOutput *
230
OssOutput::Create(EventLoop &, const ConfigBlock &block)
Avuton Olrich's avatar
Avuton Olrich committed
231
{
232
	const char *device = block.GetBlockValue("device");
233 234
	if (device != nullptr)
		return new OssOutput(block, device);
235

236
	return oss_open_default();
237 238
}

239 240
void
OssOutput::DoClose()
241
{
242 243 244
	if (fd >= 0)
		close(fd);
	fd = -1;
245 246
}

247
/**
248
 * A tri-state type for oss_try_ioctl().
249
 */
250 251 252 253 254 255 256 257
enum oss_setup_result {
	SUCCESS,
	UNSUPPORTED,
};

/**
 * Invoke an ioctl on the OSS file descriptor.  On success, SUCCESS is
 * returned.  If the parameter is not supported, UNSUPPORTED is
258
 * returned.  Any other failure throws std::runtime_error.
259 260 261
 */
static enum oss_setup_result
oss_try_ioctl_r(int fd, unsigned long request, int *value_r,
262
		const char *msg)
Avuton Olrich's avatar
Avuton Olrich committed
263
{
264
	assert(fd >= 0);
265 266
	assert(value_r != nullptr);
	assert(msg != nullptr);
267

268 269 270 271 272 273 274
	int ret = ioctl(fd, request, value_r);
	if (ret >= 0)
		return SUCCESS;

	if (errno == EINVAL)
		return UNSUPPORTED;

275
	throw MakeErrno(msg);
276 277 278 279 280
}

/**
 * Invoke an ioctl on the OSS file descriptor.  On success, SUCCESS is
 * returned.  If the parameter is not supported, UNSUPPORTED is
281
 * returned.  Any other failure throws std::runtime_error.
282 283 284
 */
static enum oss_setup_result
oss_try_ioctl(int fd, unsigned long request, int value,
285
	      const char *msg)
286
{
287
	return oss_try_ioctl_r(fd, request, &value, msg);
288 289 290 291 292
}

/**
 * Set up the channel number, and attempts to find alternatives if the
 * specified number is not supported.
293 294
 *
 * Throws #std::runtime_error on error.
295
 */
296 297
static void
oss_setup_channels(int fd, AudioFormat &audio_format)
298 299
{
	const char *const msg = "Failed to set channel count";
300
	int channels = audio_format.channels;
301
	enum oss_setup_result result =
302
		oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS, &channels, msg);
303 304 305 306 307
	switch (result) {
	case SUCCESS:
		if (!audio_valid_channel_count(channels))
		    break;

308
		audio_format.channels = channels;
309
		return;
310 311 312

	case UNSUPPORTED:
		break;
313
	}
314

315
	for (unsigned i = 1; i < 2; ++i) {
316
		if (i == audio_format.channels)
317 318 319 320 321
			/* don't try that again */
			continue;

		channels = i;
		result = oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS, &channels,
322
					 msg);
323 324 325 326 327
		switch (result) {
		case SUCCESS:
			if (!audio_valid_channel_count(channels))
			    break;

328
			audio_format.channels = channels;
329
			return;
330 331 332 333 334 335

		case UNSUPPORTED:
			break;
		}
	}

336
	throw std::runtime_error(msg);
337 338 339 340 341
}

/**
 * Set up the sample rate, and attempts to find alternatives if the
 * specified sample rate is not supported.
342 343
 *
 * Throws #std::runtime_error on error.
344
 */
345 346
static void
oss_setup_sample_rate(int fd, AudioFormat &audio_format)
347 348
{
	const char *const msg = "Failed to set sample rate";
349
	int sample_rate = audio_format.sample_rate;
350 351
	enum oss_setup_result result =
		oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &sample_rate,
352
				msg);
353 354 355 356 357
	switch (result) {
	case SUCCESS:
		if (!audio_valid_sample_rate(sample_rate))
			break;

358
		audio_format.sample_rate = sample_rate;
359
		return;
360 361 362 363 364

	case UNSUPPORTED:
		break;
	}

365
	static constexpr int sample_rates[] = { 48000, 44100, 0 };
366 367
	for (unsigned i = 0; sample_rates[i] != 0; ++i) {
		sample_rate = sample_rates[i];
368
		if (sample_rate == (int)audio_format.sample_rate)
369 370 371
			continue;

		result = oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &sample_rate,
372
					 msg);
373 374 375 376
		switch (result) {
		case SUCCESS:
			if (!audio_valid_sample_rate(sample_rate))
				break;
377

378
			audio_format.sample_rate = sample_rate;
379
			return;
380 381 382 383

		case UNSUPPORTED:
			break;
		}
384
	}
385

386
	throw std::runtime_error(msg);
387 388 389 390 391 392
}

/**
 * Convert a MPD sample format to its OSS counterpart.  Returns
 * AFMT_QUERY if there is no direct counterpart.
 */
393
gcc_const
394
static int
395
sample_format_to_oss(SampleFormat format)
396 397
{
	switch (format) {
398 399 400
	case SampleFormat::UNDEFINED:
	case SampleFormat::FLOAT:
	case SampleFormat::DSD:
401 402
		return AFMT_QUERY;

403
	case SampleFormat::S8:
404
		return AFMT_S8;
405

406
	case SampleFormat::S16:
407 408
		return AFMT_S16_NE;

409
	case SampleFormat::S24_P32:
410 411 412 413 414 415
#ifdef AFMT_S24_NE
		return AFMT_S24_NE;
#else
		return AFMT_QUERY;
#endif

416
	case SampleFormat::S32:
417 418 419
#ifdef AFMT_S32_NE
		return AFMT_S32_NE;
#else
420
		return AFMT_QUERY;
421
#endif
422 423
	}

424 425
	assert(false);
	gcc_unreachable();
426 427 428 429
}

/**
 * Convert an OSS sample format to its MPD counterpart.  Returns
430
 * SampleFormat::UNDEFINED if there is no direct counterpart.
431
 */
432
gcc_const
433
static SampleFormat
434 435 436 437
sample_format_from_oss(int format)
{
	switch (format) {
	case AFMT_S8:
438
		return SampleFormat::S8;
439 440

	case AFMT_S16_NE:
441
		return SampleFormat::S16;
442

443 444
#ifdef AFMT_S24_PACKED
	case AFMT_S24_PACKED:
445
		return SampleFormat::S24_P32;
446 447 448 449
#endif

#ifdef AFMT_S24_NE
	case AFMT_S24_NE:
450
		return SampleFormat::S24_P32;
451 452 453 454
#endif

#ifdef AFMT_S32_NE
	case AFMT_S32_NE:
455
		return SampleFormat::S32;
456 457
#endif

458
	default:
459
		return SampleFormat::UNDEFINED;
460
	}
461
}
462

463 464 465
/**
 * Probe one sample format.
 *
466
 * @return the selected sample format or SampleFormat::UNDEFINED on
467 468 469
 * error
 */
static enum oss_setup_result
470 471
oss_probe_sample_format(int fd, SampleFormat sample_format,
			SampleFormat *sample_format_r,
472
			int *oss_format_r
473
#ifdef AFMT_S24_PACKED
474
			, PcmExport &pcm_export
475
#endif
476
			)
477 478 479 480 481 482 483 484
{
	int oss_format = sample_format_to_oss(sample_format);
	if (oss_format == AFMT_QUERY)
		return UNSUPPORTED;

	enum oss_setup_result result =
		oss_try_ioctl_r(fd, SNDCTL_DSP_SAMPLESIZE,
				&oss_format,
485
				"Failed to set sample format");
486 487

#ifdef AFMT_S24_PACKED
488
	if (result == UNSUPPORTED && sample_format == SampleFormat::S24_P32) {
489 490 491 492 493
		/* if the driver doesn't support padded 24 bit, try
		   packed 24 bit */
		oss_format = AFMT_S24_PACKED;
		result = oss_try_ioctl_r(fd, SNDCTL_DSP_SAMPLESIZE,
					 &oss_format,
494
					 "Failed to set sample format");
495 496 497
	}
#endif

498 499 500 501
	if (result != SUCCESS)
		return result;

	sample_format = sample_format_from_oss(oss_format);
502
	if (sample_format == SampleFormat::UNDEFINED)
503 504 505
		return UNSUPPORTED;

	*sample_format_r = sample_format;
506
	*oss_format_r = oss_format;
507 508

#ifdef AFMT_S24_PACKED
509 510 511 512 513 514 515
	PcmExport::Params params;
	params.alsa_channel_order = true;
	params.pack24 = oss_format == AFMT_S24_PACKED;
	params.reverse_endian = oss_format == AFMT_S24_PACKED &&
		!IsLittleEndian();

	pcm_export.Open(sample_format, 0, params);
516 517 518 519 520
#endif

	return SUCCESS;
}

521 522 523 524
/**
 * Set up the sample format, and attempts to find alternatives if the
 * specified format is not supported.
 */
525
static void
526
oss_setup_sample_format(int fd, AudioFormat &audio_format,
527
			int *oss_format_r
528
#ifdef AFMT_S24_PACKED
529
			, PcmExport &pcm_export
530
#endif
531
			)
532
{
533
	SampleFormat mpd_format;
534
	enum oss_setup_result result =
535
		oss_probe_sample_format(fd, audio_format.format,
536
					&mpd_format, oss_format_r
537
#ifdef AFMT_S24_PACKED
538
					, pcm_export
539
#endif
540
					);
541 542
	switch (result) {
	case SUCCESS:
543
		audio_format.format = mpd_format;
544
		return;
545 546 547

	case UNSUPPORTED:
		break;
548
	}
549

550 551 552
	/* the requested sample format is not available - probe for
	   other formats supported by MPD */

553
	static constexpr SampleFormat sample_formats[] = {
554 555 556 557 558
		SampleFormat::S24_P32,
		SampleFormat::S32,
		SampleFormat::S16,
		SampleFormat::S8,
		SampleFormat::UNDEFINED /* sentinel */
559 560
	};

561
	for (unsigned i = 0; sample_formats[i] != SampleFormat::UNDEFINED; ++i) {
562
		mpd_format = sample_formats[i];
563
		if (mpd_format == audio_format.format)
564 565 566
			/* don't try that again */
			continue;

567
		result = oss_probe_sample_format(fd, mpd_format,
568
						 &mpd_format, oss_format_r
569
#ifdef AFMT_S24_PACKED
570
						 , pcm_export
571
#endif
572
						 );
573 574
		switch (result) {
		case SUCCESS:
575
			audio_format.format = mpd_format;
576
			return;
577 578 579 580 581 582

		case UNSUPPORTED:
			break;
		}
	}

583
	throw std::runtime_error("Failed to set sample format");
584
}
585

586 587
inline void
OssOutput::Setup(AudioFormat &_audio_format)
588
{
589 590 591
	oss_setup_channels(fd, _audio_format);
	oss_setup_sample_rate(fd, _audio_format);
	oss_setup_sample_format(fd, _audio_format, &oss_format
592
#ifdef AFMT_S24_PACKED
593
				, pcm_export
594
#endif
595
				);
596 597 598 599 600
}

/**
 * Reopen the device with the saved audio_format, without any probing.
 */
601 602 603
inline void
OssOutput::Reopen()
try {
604
	assert(fd < 0);
605

606
	fd = open_cloexec(device, O_WRONLY, 0);
607 608
	if (fd < 0)
		throw FormatErrno("Error opening OSS device \"%s\"", device);
609

610 611 612
	enum oss_setup_result result;

	const char *const msg1 = "Failed to set channel count";
613
	result = oss_try_ioctl(fd, SNDCTL_DSP_CHANNELS,
614
			       audio_format.channels, msg1);
615
	if (result != SUCCESS) {
616
		DoClose();
617
		throw std::runtime_error(msg1);
618 619 620
	}

	const char *const msg2 = "Failed to set sample rate";
621
	result = oss_try_ioctl(fd, SNDCTL_DSP_SPEED,
622
			       audio_format.sample_rate, msg2);
623
	if (result != SUCCESS) {
624
		DoClose();
625
		throw std::runtime_error(msg2);
626 627 628
	}

	const char *const msg3 = "Failed to set sample format";
629 630
	result = oss_try_ioctl(fd, SNDCTL_DSP_SAMPLESIZE,
			       oss_format,
631
			       msg3);
632
	if (result != SUCCESS) {
633
		DoClose();
634
		throw std::runtime_error(msg3);
635
	}
636 637 638
} catch (...) {
	DoClose();
	throw;
639 640
}

641 642
inline void
OssOutput::Open(AudioFormat &_audio_format)
643
try {
644
	fd = open_cloexec(device, O_WRONLY, 0);
645 646
	if (fd < 0)
		throw FormatErrno("Error opening OSS device \"%s\"", device);
647

648
	Setup(_audio_format);
649

650
	audio_format = _audio_format;
651 652 653
} catch (...) {
	DoClose();
	throw;
654 655
}

656 657
inline void
OssOutput::Cancel()
Avuton Olrich's avatar
Avuton Olrich committed
658
{
659 660 661
	if (fd >= 0) {
		ioctl(fd, SNDCTL_DSP_RESET, 0);
		DoClose();
662
	}
663 664 665 666

#ifdef AFMT_S24_PACKED
	pcm_export->Reset();
#endif
667 668
}

669
inline size_t
670
OssOutput::Play(const void *chunk, size_t size)
671
{
672
	ssize_t ret;
673

674 675
	assert(size > 0);

676
	/* reopen the device since it was closed by dropBufferedAudio */
677 678
	if (fd < 0)
		Reopen();
679

680
#ifdef AFMT_S24_PACKED
681
	const auto e = pcm_export->Export({chunk, size});
682 683
	chunk = e.data;
	size = e.size;
684 685
#endif

686 687
	assert(size > 0);

688
	while (true) {
689
		ret = write(fd, chunk, size);
690 691
		if (ret > 0) {
#ifdef AFMT_S24_PACKED
692
			ret = pcm_export->CalcSourceSize(ret);
693 694 695
#endif
			return ret;
		}
696

697 698
		if (ret < 0 && errno != EINTR)
			throw FormatErrno("Write error on %s", device);
699 700 701
	}
}

702 703
typedef AudioOutputWrapper<OssOutput> Wrapper;

704
const struct AudioOutputPlugin oss_output_plugin = {
705 706
	"oss",
	oss_output_test_default_device,
707 708
	&Wrapper::Init,
	&Wrapper::Finish,
709
#ifdef AFMT_S24_PACKED
710 711
	&Wrapper::Enable,
	&Wrapper::Disable,
712 713 714
#else
	nullptr,
	nullptr,
715
#endif
716 717
	&Wrapper::Open,
	&Wrapper::Close,
718 719
	nullptr,
	nullptr,
720
	&Wrapper::Play,
721
	nullptr,
722
	&Wrapper::Cancel,
723 724 725
	nullptr,

	&oss_mixer_plugin,
726
};