OssOutputPlugin.cxx 16 KB
Newer Older
1
/*
2
 * Copyright 2003-2016 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 "util/ConstBuffer.hxx"
27 28
#include "util/Error.hxx"
#include "util/Domain.hxx"
29
#include "util/Macros.hxx"
30
#include "system/ByteOrder.hxx"
31
#include "Log.hxx"
32

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

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

47 48 49 50 51 52 53 54 55
/* 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

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

61 62 63
class OssOutput {
	friend struct AudioOutputWrapper<OssOutput>;

64
	AudioOutput base;
65

66
#ifdef AFMT_S24_PACKED
67
	Manual<PcmExport> pcm_export;
68 69
#endif

70
	int fd;
71
	const char *device;
72

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

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

85
public:
86
	OssOutput(const char *_device=nullptr)
87
		:base(oss_output_plugin),
88
		 fd(-1), device(_device) {}
89

90 91
	bool Initialize(const ConfigBlock &block, Error &error_r) {
		return base.Configure(block, error_r);
92
	}
93

94
	static OssOutput *Create(const ConfigBlock &block, Error &error);
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

#ifdef AFMT_S24_PACKED
	bool Enable(gcc_unused Error &error) {
		pcm_export.Construct();
		return true;
	}

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

	bool Open(AudioFormat &audio_format, Error &error);

	void Close() {
		DoClose();
	}

	size_t Play(const void *chunk, size_t size, Error &error);
	void Cancel();

private:
	/**
	 * Sets up the OSS device which was opened before.
	 */
	bool Setup(AudioFormat &audio_format, Error &error);

	/**
	 * Reopen the device with the saved audio_format, without any probing.
	 */
	bool Reopen(Error &error);

	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(Error &error)
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
		if (ret[i] == OSS_STAT_NO_ERROR) {
199
			OssOutput *od = new OssOutput(default_devices[i]);
200
			if (!od->Initialize(empty, error)) {
201
				delete od;
202
				return nullptr;
203 204
			}

205
			return od;
206
		}
207 208
	}

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

233 234
	error.Set(oss_output_domain,
		  "error trying to open default OSS device");
235
	return nullptr;
236 237
}

238
inline OssOutput *
239
OssOutput::Create(const ConfigBlock &block, Error &error)
Avuton Olrich's avatar
Avuton Olrich committed
240
{
241
	const char *device = block.GetBlockValue("device");
242
	if (device != nullptr) {
243
		OssOutput *od = new OssOutput();
244
		if (!od->Initialize(block, error)) {
245
			delete od;
246
			return nullptr;
247 248
		}

Max Kellermann's avatar
Max Kellermann committed
249
		od->device = device;
250
		return od;
251
	}
Max Kellermann's avatar
Max Kellermann committed
252

253
	return oss_open_default(error);
254 255
}

256 257
void
OssOutput::DoClose()
258
{
259 260 261
	if (fd >= 0)
		close(fd);
	fd = -1;
262 263
}

264
/**
265
 * A tri-state type for oss_try_ioctl().
266
 */
267 268 269 270 271 272 273 274 275
enum oss_setup_result {
	SUCCESS,
	ERROR,
	UNSUPPORTED,
};

/**
 * Invoke an ioctl on the OSS file descriptor.  On success, SUCCESS is
 * returned.  If the parameter is not supported, UNSUPPORTED is
276
 * returned.  Any other failure returns ERROR and allocates an #Error.
277 278 279
 */
static enum oss_setup_result
oss_try_ioctl_r(int fd, unsigned long request, int *value_r,
280
		const char *msg, Error &error)
Avuton Olrich's avatar
Avuton Olrich committed
281
{
282
	assert(fd >= 0);
283 284
	assert(value_r != nullptr);
	assert(msg != nullptr);
285
	assert(!error.IsDefined());
286

287 288 289 290 291 292 293
	int ret = ioctl(fd, request, value_r);
	if (ret >= 0)
		return SUCCESS;

	if (errno == EINVAL)
		return UNSUPPORTED;

294
	error.SetErrno(msg);
295 296 297 298 299 300
	return ERROR;
}

/**
 * Invoke an ioctl on the OSS file descriptor.  On success, SUCCESS is
 * returned.  If the parameter is not supported, UNSUPPORTED is
301
 * returned.  Any other failure returns ERROR and allocates an #Error.
302 303 304
 */
static enum oss_setup_result
oss_try_ioctl(int fd, unsigned long request, int value,
305
	      const char *msg, Error &error_r)
306 307 308 309 310 311 312 313 314
{
	return oss_try_ioctl_r(fd, request, &value, msg, error_r);
}

/**
 * Set up the channel number, and attempts to find alternatives if the
 * specified number is not supported.
 */
static bool
315
oss_setup_channels(int fd, AudioFormat &audio_format, Error &error)
316 317
{
	const char *const msg = "Failed to set channel count";
318
	int channels = audio_format.channels;
319
	enum oss_setup_result result =
320
		oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS, &channels, msg, error);
321 322 323 324 325
	switch (result) {
	case SUCCESS:
		if (!audio_valid_channel_count(channels))
		    break;

326
		audio_format.channels = channels;
327 328 329
		return true;

	case ERROR:
330
		return false;
331 332 333

	case UNSUPPORTED:
		break;
334
	}
335

336
	for (unsigned i = 1; i < 2; ++i) {
337
		if (i == audio_format.channels)
338 339 340 341 342
			/* don't try that again */
			continue;

		channels = i;
		result = oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS, &channels,
343
					 msg, error);
344 345 346 347 348
		switch (result) {
		case SUCCESS:
			if (!audio_valid_channel_count(channels))
			    break;

349
			audio_format.channels = channels;
350 351 352 353 354 355 356 357 358 359
			return true;

		case ERROR:
			return false;

		case UNSUPPORTED:
			break;
		}
	}

360
	error.Set(oss_output_domain, msg);
361 362 363 364 365 366 367 368
	return false;
}

/**
 * Set up the sample rate, and attempts to find alternatives if the
 * specified sample rate is not supported.
 */
static bool
369
oss_setup_sample_rate(int fd, AudioFormat &audio_format,
370
		      Error &error)
371 372
{
	const char *const msg = "Failed to set sample rate";
373
	int sample_rate = audio_format.sample_rate;
374 375
	enum oss_setup_result result =
		oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &sample_rate,
376
				msg, error);
377 378 379 380 381
	switch (result) {
	case SUCCESS:
		if (!audio_valid_sample_rate(sample_rate))
			break;

382
		audio_format.sample_rate = sample_rate;
383 384 385
		return true;

	case ERROR:
386
		return false;
387 388 389 390 391

	case UNSUPPORTED:
		break;
	}

392
	static constexpr int sample_rates[] = { 48000, 44100, 0 };
393 394
	for (unsigned i = 0; sample_rates[i] != 0; ++i) {
		sample_rate = sample_rates[i];
395
		if (sample_rate == (int)audio_format.sample_rate)
396 397 398
			continue;

		result = oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &sample_rate,
399
					 msg, error);
400 401 402 403
		switch (result) {
		case SUCCESS:
			if (!audio_valid_sample_rate(sample_rate))
				break;
404

405
			audio_format.sample_rate = sample_rate;
406 407 408 409 410 411 412 413
			return true;

		case ERROR:
			return false;

		case UNSUPPORTED:
			break;
		}
414
	}
415

416
	error.Set(oss_output_domain, msg);
417 418 419 420 421 422 423
	return false;
}

/**
 * Convert a MPD sample format to its OSS counterpart.  Returns
 * AFMT_QUERY if there is no direct counterpart.
 */
424
gcc_const
425
static int
426
sample_format_to_oss(SampleFormat format)
427 428
{
	switch (format) {
429 430 431
	case SampleFormat::UNDEFINED:
	case SampleFormat::FLOAT:
	case SampleFormat::DSD:
432 433
		return AFMT_QUERY;

434
	case SampleFormat::S8:
435
		return AFMT_S8;
436

437
	case SampleFormat::S16:
438 439
		return AFMT_S16_NE;

440
	case SampleFormat::S24_P32:
441 442 443 444 445 446
#ifdef AFMT_S24_NE
		return AFMT_S24_NE;
#else
		return AFMT_QUERY;
#endif

447
	case SampleFormat::S32:
448 449 450
#ifdef AFMT_S32_NE
		return AFMT_S32_NE;
#else
451
		return AFMT_QUERY;
452
#endif
453 454
	}

455 456
	assert(false);
	gcc_unreachable();
457 458 459 460
}

/**
 * Convert an OSS sample format to its MPD counterpart.  Returns
461
 * SampleFormat::UNDEFINED if there is no direct counterpart.
462
 */
463
gcc_const
464
static SampleFormat
465 466 467 468
sample_format_from_oss(int format)
{
	switch (format) {
	case AFMT_S8:
469
		return SampleFormat::S8;
470 471

	case AFMT_S16_NE:
472
		return SampleFormat::S16;
473

474 475
#ifdef AFMT_S24_PACKED
	case AFMT_S24_PACKED:
476
		return SampleFormat::S24_P32;
477 478 479 480
#endif

#ifdef AFMT_S24_NE
	case AFMT_S24_NE:
481
		return SampleFormat::S24_P32;
482 483 484 485
#endif

#ifdef AFMT_S32_NE
	case AFMT_S32_NE:
486
		return SampleFormat::S32;
487 488
#endif

489
	default:
490
		return SampleFormat::UNDEFINED;
491
	}
492
}
493

494 495 496
/**
 * Probe one sample format.
 *
497
 * @return the selected sample format or SampleFormat::UNDEFINED on
498 499 500
 * error
 */
static enum oss_setup_result
501 502
oss_probe_sample_format(int fd, SampleFormat sample_format,
			SampleFormat *sample_format_r,
503
			int *oss_format_r,
504
#ifdef AFMT_S24_PACKED
505
			PcmExport &pcm_export,
506
#endif
507
			Error &error)
508 509 510 511 512 513 514 515
{
	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,
516
				"Failed to set sample format", error);
517 518

#ifdef AFMT_S24_PACKED
519
	if (result == UNSUPPORTED && sample_format == SampleFormat::S24_P32) {
520 521 522 523 524
		/* 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,
525
					 "Failed to set sample format", error);
526 527 528
	}
#endif

529 530 531 532
	if (result != SUCCESS)
		return result;

	sample_format = sample_format_from_oss(oss_format);
533
	if (sample_format == SampleFormat::UNDEFINED)
534 535 536
		return UNSUPPORTED;

	*sample_format_r = sample_format;
537
	*oss_format_r = oss_format;
538 539

#ifdef AFMT_S24_PACKED
540 541 542 543 544 545 546
	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);
547 548 549 550 551
#endif

	return SUCCESS;
}

552 553 554 555 556
/**
 * Set up the sample format, and attempts to find alternatives if the
 * specified format is not supported.
 */
static bool
557
oss_setup_sample_format(int fd, AudioFormat &audio_format,
558
			int *oss_format_r,
559
#ifdef AFMT_S24_PACKED
560
			PcmExport &pcm_export,
561
#endif
562
			Error &error)
563
{
564
	SampleFormat mpd_format;
565
	enum oss_setup_result result =
566
		oss_probe_sample_format(fd, audio_format.format,
567
					&mpd_format, oss_format_r,
568
#ifdef AFMT_S24_PACKED
569
					pcm_export,
570
#endif
571
					error);
572 573
	switch (result) {
	case SUCCESS:
574
		audio_format.format = mpd_format;
575 576 577
		return true;

	case ERROR:
578
		return false;
579 580 581

	case UNSUPPORTED:
		break;
582
	}
583

584 585 586
	if (result != UNSUPPORTED)
		return result == SUCCESS;

587 588 589
	/* the requested sample format is not available - probe for
	   other formats supported by MPD */

590
	static constexpr SampleFormat sample_formats[] = {
591 592 593 594 595
		SampleFormat::S24_P32,
		SampleFormat::S32,
		SampleFormat::S16,
		SampleFormat::S8,
		SampleFormat::UNDEFINED /* sentinel */
596 597
	};

598
	for (unsigned i = 0; sample_formats[i] != SampleFormat::UNDEFINED; ++i) {
599
		mpd_format = sample_formats[i];
600
		if (mpd_format == audio_format.format)
601 602 603
			/* don't try that again */
			continue;

604
		result = oss_probe_sample_format(fd, mpd_format,
605
						 &mpd_format, oss_format_r,
606
#ifdef AFMT_S24_PACKED
607
						 pcm_export,
608
#endif
609
						 error);
610 611
		switch (result) {
		case SUCCESS:
612
			audio_format.format = mpd_format;
613 614 615 616 617 618 619 620 621 622
			return true;

		case ERROR:
			return false;

		case UNSUPPORTED:
			break;
		}
	}

623
	error.Set(oss_output_domain, "Failed to set sample format");
624
	return false;
625
}
626

627 628
inline bool
OssOutput::Setup(AudioFormat &_audio_format, Error &error)
629
{
630 631 632
	return oss_setup_channels(fd, _audio_format, error) &&
		oss_setup_sample_rate(fd, _audio_format, error) &&
		oss_setup_sample_format(fd, _audio_format, &oss_format,
633
#ifdef AFMT_S24_PACKED
634
					pcm_export,
635
#endif
636
					error);
637 638 639 640 641
}

/**
 * Reopen the device with the saved audio_format, without any probing.
 */
642 643
inline bool
OssOutput::Reopen(Error &error)
644
{
645
	assert(fd < 0);
646

647 648
	fd = open_cloexec(device, O_WRONLY, 0);
	if (fd < 0) {
649
		error.FormatErrno("Error opening OSS device \"%s\"",
650
				  device);
651 652 653
		return false;
	}

654 655 656
	enum oss_setup_result result;

	const char *const msg1 = "Failed to set channel count";
657 658
	result = oss_try_ioctl(fd, SNDCTL_DSP_CHANNELS,
			       audio_format.channels, msg1, error);
659
	if (result != SUCCESS) {
660
		DoClose();
661
		if (result == UNSUPPORTED)
662
			error.Set(oss_output_domain, msg1);
663 664 665 666
		return false;
	}

	const char *const msg2 = "Failed to set sample rate";
667 668
	result = oss_try_ioctl(fd, SNDCTL_DSP_SPEED,
			       audio_format.sample_rate, msg2, error);
669
	if (result != SUCCESS) {
670
		DoClose();
671
		if (result == UNSUPPORTED)
672
			error.Set(oss_output_domain, msg2);
673 674 675 676
		return false;
	}

	const char *const msg3 = "Failed to set sample format";
677 678
	result = oss_try_ioctl(fd, SNDCTL_DSP_SAMPLESIZE,
			       oss_format,
679
			       msg3, error);
680
	if (result != SUCCESS) {
681
		DoClose();
682
		if (result == UNSUPPORTED)
683
			error.Set(oss_output_domain, msg3);
684 685 686 687
		return false;
	}

	return true;
688 689
}

690 691
inline bool
OssOutput::Open(AudioFormat &_audio_format, Error &error)
692
{
693 694
	fd = open_cloexec(device, O_WRONLY, 0);
	if (fd < 0) {
695
		error.FormatErrno("Error opening OSS device \"%s\"",
696
				  device);
697
		return false;
698
	}
699

700 701
	if (!Setup(_audio_format, error)) {
		DoClose();
702 703
		return false;
	}
704

705
	audio_format = _audio_format;
706
	return true;
707 708
}

709 710
inline void
OssOutput::Cancel()
Avuton Olrich's avatar
Avuton Olrich committed
711
{
712 713 714
	if (fd >= 0) {
		ioctl(fd, SNDCTL_DSP_RESET, 0);
		DoClose();
715 716 717
	}
}

718 719
inline size_t
OssOutput::Play(const void *chunk, size_t size, Error &error)
720
{
721
	ssize_t ret;
722

723 724
	assert(size > 0);

725
	/* reopen the device since it was closed by dropBufferedAudio */
726
	if (fd < 0 && !Reopen(error))
727
		return 0;
728

729
#ifdef AFMT_S24_PACKED
730
	const auto e = pcm_export->Export({chunk, size});
731 732
	chunk = e.data;
	size = e.size;
733 734
#endif

735 736
	assert(size > 0);

737
	while (true) {
738
		ret = write(fd, chunk, size);
739 740
		if (ret > 0) {
#ifdef AFMT_S24_PACKED
741
			ret = pcm_export->CalcSourceSize(ret);
742 743 744
#endif
			return ret;
		}
745 746

		if (ret < 0 && errno != EINTR) {
747
			error.FormatErrno("Write error on %s", device);
748
			return 0;
749 750 751 752
		}
	}
}

753 754
typedef AudioOutputWrapper<OssOutput> Wrapper;

755
const struct AudioOutputPlugin oss_output_plugin = {
756 757
	"oss",
	oss_output_test_default_device,
758 759
	&Wrapper::Init,
	&Wrapper::Finish,
760
#ifdef AFMT_S24_PACKED
761 762
	&Wrapper::Enable,
	&Wrapper::Disable,
763 764 765
#else
	nullptr,
	nullptr,
766
#endif
767 768
	&Wrapper::Open,
	&Wrapper::Close,
769 770
	nullptr,
	nullptr,
771
	&Wrapper::Play,
772
	nullptr,
773
	&Wrapper::Cancel,
774 775 776
	nullptr,

	&oss_mixer_plugin,
777
};