OssOutputPlugin.cxx 15.6 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2021 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 "OssOutputPlugin.hxx"
21
#include "../OutputAPI.hxx"
Max Kellermann's avatar
Max Kellermann committed
22
#include "mixer/MixerList.hxx"
23
#include "pcm/Export.hxx"
24
#include "io/UniqueFileDescriptor.hxx"
25
#include "system/Error.hxx"
26
#include "util/ConstBuffer.hxx"
27
#include "util/Domain.hxx"
28
#include "util/ByteOrder.hxx"
29
#include "util/Manual.hxx"
30
#include "Log.hxx"
31

32
#include <cassert>
Rosen Penev's avatar
Rosen Penev committed
33
#include <cerrno>
34
#include <iterator>
35 36
#include <stdexcept>

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

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 59 60 61
#if defined(ENABLE_DSD) && defined(AFMT_S32_NE)
#define ENABLE_OSS_DSD
#endif

62
class OssOutput final : AudioOutput {
63
	Manual<PcmExport> pcm_export;
64

65 66 67 68 69 70 71 72 73 74 75
#ifdef ENABLE_OSS_DSD
	/**
	 * Enable DSD over PCM according to the DoP standard?
	 *
	 * @see http://dsd-guide.com/dop-open-standard
	 *
	 * this is default in oss as no other dsd-method is known to man
	 */
	const bool dop_setting;
#endif

76
	FileDescriptor fd = FileDescriptor::Undefined();
77
	const char *device;
78

79
	/**
80 81
	 * The effective audio format settings of the OSS device.
	 * This is needed by Reopen() after Cancel().
82
	 */
83
	int effective_channels, effective_speed, effective_samplesize;
84

85 86
	static constexpr unsigned oss_flags = FLAG_ENABLE_DISABLE;

87
public:
88 89 90 91 92
	explicit OssOutput(const char *_device=nullptr
#ifdef ENABLE_OSS_DSD
			   , bool dop = false
#endif
			   )
93
		:AudioOutput(oss_flags),
94 95 96 97 98 99
#ifdef ENABLE_OSS_DSD
		 dop_setting(dop),
#endif
		 device(_device)
	{
	}
100

101 102
	static AudioOutput *Create(EventLoop &event_loop,
				   const ConfigBlock &block);
103

104
	void Enable() override {
105 106 107
		pcm_export.Construct();
	}

108
	void Disable() noexcept override {
109 110 111
		pcm_export.Destruct();
	}

112
	void Open(AudioFormat &audio_format) override;
113

114
	void Close() noexcept override {
115 116 117
		DoClose();
	}

118 119
	size_t Play(const void *chunk, size_t size) override;
	void Cancel() noexcept override;
120 121 122 123 124

private:
	/**
	 * Sets up the OSS device which was opened before.
	 */
125
	void Setup(AudioFormat &audio_format);
126

127 128 129 130 131 132
#ifdef ENABLE_OSS_DSD
	void SetupDop(const AudioFormat &audio_format);
#endif

	void SetupOrDop(AudioFormat &audio_format);

133 134
	/**
	 * Reopen the device with the saved audio_format, without any probing.
135
	 *
136
	 * Throws on error.
137
	 */
138
	void Reopen();
139

140
	void DoClose() noexcept;
141 142
};

143
static constexpr Domain oss_output_domain("oss_output");
144

145 146 147 148 149 150 151
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,
};
152

153
static enum oss_stat
154
oss_stat_device(const char *device, int *errno_r) noexcept
Avuton Olrich's avatar
Avuton Olrich committed
155
{
156
	struct stat st;
Avuton Olrich's avatar
Avuton Olrich committed
157 158 159

	if (0 == stat(device, &st)) {
		if (!S_ISCHR(st.st_mode)) {
160 161
			return OSS_STAT_NOT_CHAR_DEV;
		}
Avuton Olrich's avatar
Avuton Olrich committed
162
	} else {
Max Kellermann's avatar
Max Kellermann committed
163
		*errno_r = errno;
164

Avuton Olrich's avatar
Avuton Olrich committed
165
		switch (errno) {
166 167 168 169 170 171 172 173 174 175
		case ENOENT:
		case ENOTDIR:
			return OSS_STAT_DOESN_T_EXIST;
		case EACCES:
			return OSS_STAT_NO_PERMS;
		default:
			return OSS_STAT_OTHER;
		}
	}

176
	return OSS_STAT_NO_ERROR;
177 178
}

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

Max Kellermann's avatar
Max Kellermann committed
181
static bool
182
oss_output_test_default_device() noexcept
Avuton Olrich's avatar
Avuton Olrich committed
183
{
184
	for (int i = std::size(default_devices); --i >= 0; ) {
185 186
		UniqueFileDescriptor fd;
		if (fd.Open(default_devices[i], O_WRONLY, 0))
187
			return true;
188

189 190 191
		FmtError(oss_output_domain,
			 "Error opening OSS device \"{}\": {}",
			 default_devices[i], strerror(errno));
192 193
	}

194
	return false;
195
}
196

197
static OssOutput *
198 199 200 201 202
oss_open_default(
#ifdef ENABLE_OSS_DSD
		 bool dop
#endif
		 )
203
{
204 205
	int err[std::size(default_devices)];
	enum oss_stat ret[std::size(default_devices)];
206

207
	for (int i = std::size(default_devices); --i >= 0; ) {
Max Kellermann's avatar
Max Kellermann committed
208
		ret[i] = oss_stat_device(default_devices[i], &err[i]);
209
		if (ret[i] == OSS_STAT_NO_ERROR)
210 211 212 213 214
			return new OssOutput(default_devices[i]
#ifdef ENABLE_OSS_DSD
					     , dop
#endif
					     );
215 216
	}

217
	for (int i = std::size(default_devices); --i >= 0; ) {
218 219
		const char *dev = default_devices[i];
		switch(ret[i]) {
220 221 222
		case OSS_STAT_NO_ERROR:
			/* never reached */
			break;
223
		case OSS_STAT_DOESN_T_EXIST:
224 225
			FmtWarning(oss_output_domain,
				   "{} not found", dev);
226 227
			break;
		case OSS_STAT_NOT_CHAR_DEV:
228 229
			FmtWarning(oss_output_domain,
				   "{} is not a character device", dev);
230 231
			break;
		case OSS_STAT_NO_PERMS:
232 233
			FmtWarning(oss_output_domain,
				   "{}: permission denied", dev);
234
			break;
235
		case OSS_STAT_OTHER:
236 237
			FmtError(oss_output_domain, "Error accessing {}: {}",
				 dev, strerror(err[i]));
238 239
		}
	}
240

241
	throw std::runtime_error("error trying to open default OSS device");
242 243
}

244
AudioOutput *
245
OssOutput::Create(EventLoop &, const ConfigBlock &block)
Avuton Olrich's avatar
Avuton Olrich committed
246
{
247 248 249 250
#ifdef ENABLE_OSS_DSD
	bool dop = block.GetBlockValue("dop", false);
#endif

251
	const char *device = block.GetBlockValue("device");
252
	if (device != nullptr)
253 254 255 256 257
		return new OssOutput(device
#ifdef ENABLE_OSS_DSD
				     , dop
#endif
				     );
258

259 260 261 262 263
	return oss_open_default(
#ifdef ENABLE_OSS_DSD
				dop
#endif
				);
264 265
}

266
void
267
OssOutput::DoClose() noexcept
268
{
269 270
	if (fd.IsDefined())
		fd.Close();
271 272
}

273
/**
274 275 276 277 278
 * Invoke an ioctl on the OSS file descriptor.
 *
 * Throws on error.
 *
 * @return true success, false if the parameter is not supported
279
 */
280
static bool
281
oss_try_ioctl_r(FileDescriptor fd, unsigned long request, int *value_r,
282
		const char *msg)
Avuton Olrich's avatar
Avuton Olrich committed
283
{
284
	assert(fd.IsDefined());
285 286
	assert(value_r != nullptr);
	assert(msg != nullptr);
287

288
	int ret = ioctl(fd.Get(), request, value_r);
289
	if (ret >= 0)
290
		return true;
291 292

	if (errno == EINVAL)
293
		return false;
294

295
	throw MakeErrno(msg);
296 297 298
}

/**
299 300
 * Invoke an ioctl on the OSS file descriptor, and expect an
 * unmodified effective value.
301 302
 *
 * Throws on error.
303
 */
304
static void
305
OssIoctlExact(FileDescriptor fd, unsigned long request, int requested_value,
306
	      const char *msg)
307
{
308 309 310
	assert(fd.IsDefined());
	assert(msg != nullptr);

311 312
	int effective_value = requested_value;
	if (ioctl(fd.Get(), request, &effective_value) < 0)
313
		throw MakeErrno(msg);
314 315 316

	if (effective_value != requested_value)
		throw std::runtime_error(msg);
317 318 319 320 321
}

/**
 * Set up the channel number, and attempts to find alternatives if the
 * specified number is not supported.
322
 *
323
 * Throws on error.
324
 */
325
static void
326 327
oss_setup_channels(FileDescriptor fd, AudioFormat &audio_format,
		   int &effective_channels)
328 329 330
{
	const char *const msg = "Failed to set channel count";

331 332 333 334 335 336
	effective_channels = audio_format.channels;

	if (oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS,
			    &effective_channels, msg) &&
	    audio_valid_channel_count(effective_channels)) {
		audio_format.channels = effective_channels;
337
		return;
338
	}
339

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

345 346 347 348 349
		effective_channels = i;
		if (oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS,
				    &effective_channels, msg) &&
		    audio_valid_channel_count(effective_channels)) {
			audio_format.channels = effective_channels;
350
			return;
351 352 353
		}
	}

354
	throw std::runtime_error(msg);
355 356 357 358 359
}

/**
 * Set up the sample rate, and attempts to find alternatives if the
 * specified sample rate is not supported.
360
 *
361
 * Throws on error.
362
 */
363
static void
364 365
oss_setup_sample_rate(FileDescriptor fd, AudioFormat &audio_format,
		      int &effective_speed)
366 367 368
{
	const char *const msg = "Failed to set sample rate";

369 370 371 372
	effective_speed = audio_format.sample_rate;
	if (oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &effective_speed, msg) &&
	    audio_valid_sample_rate(effective_speed)) {
		audio_format.sample_rate = effective_speed;
373
		return;
374 375
	}

376
	static constexpr int sample_rates[] = { 48000, 44100, 0 };
377
	for (unsigned i = 0; sample_rates[i] != 0; ++i) {
378 379
		effective_speed = sample_rates[i];
		if (effective_speed == (int)audio_format.sample_rate)
380 381
			continue;

382 383 384
		if (oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &effective_speed, msg) &&
		    audio_valid_sample_rate(effective_speed)) {
			audio_format.sample_rate = effective_speed;
385
			return;
386
		}
387
	}
388

389
	throw std::runtime_error(msg);
390 391 392 393 394 395
}

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

406
	case SampleFormat::S8:
407
		return AFMT_S8;
408

409
	case SampleFormat::S16:
410 411
		return AFMT_S16_NE;

412
	case SampleFormat::S24_P32:
413 414 415 416 417 418
#ifdef AFMT_S24_NE
		return AFMT_S24_NE;
#else
		return AFMT_QUERY;
#endif

419
	case SampleFormat::S32:
420 421 422
#ifdef AFMT_S32_NE
		return AFMT_S32_NE;
#else
423
		return AFMT_QUERY;
424
#endif
425 426
	}

427 428
	assert(false);
	gcc_unreachable();
429 430 431 432
}

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

	case AFMT_S16_NE:
444
		return SampleFormat::S16;
445

446 447
#ifdef AFMT_S24_PACKED
	case AFMT_S24_PACKED:
448
		return SampleFormat::S24_P32;
449 450 451 452
#endif

#ifdef AFMT_S24_NE
	case AFMT_S24_NE:
453
		return SampleFormat::S24_P32;
454 455 456 457
#endif

#ifdef AFMT_S32_NE
	case AFMT_S32_NE:
458
		return SampleFormat::S32;
459 460
#endif

461
	default:
462
		return SampleFormat::UNDEFINED;
463
	}
464
}
465

466 467 468
/**
 * Probe one sample format.
 *
469 470 471
 * Throws on error.
 *
 * @return true success, false if the parameter is not supported
472
 */
473
static bool
474
oss_probe_sample_format(FileDescriptor fd, SampleFormat sample_format,
475
			SampleFormat *sample_format_r,
476 477
			int *oss_format_r,
			PcmExport &pcm_export)
478 479 480
{
	int oss_format = sample_format_to_oss(sample_format);
	if (oss_format == AFMT_QUERY)
481
		return false;
482

483
	bool success =
484 485
		oss_try_ioctl_r(fd, SNDCTL_DSP_SAMPLESIZE,
				&oss_format,
486
				"Failed to set sample format");
487 488

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

499 500
	if (!success)
		return false;
501 502

	sample_format = sample_format_from_oss(oss_format);
503

504
	if (sample_format == SampleFormat::UNDEFINED)
505
		return false;
506 507

	*sample_format_r = sample_format;
508
	*oss_format_r = oss_format;
509

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

	pcm_export.Open(sample_format, 0, params);
519

520
	return true;
521 522
}

523 524 525 526
/**
 * Set up the sample format, and attempts to find alternatives if the
 * specified format is not supported.
 */
527
static void
528
oss_setup_sample_format(FileDescriptor fd, AudioFormat &audio_format,
529 530
			int *oss_format_r,
			PcmExport &pcm_export)
531
{
532
	SampleFormat mpd_format;
533
	if (oss_probe_sample_format(fd, audio_format.format,
534 535
				    &mpd_format, oss_format_r,
				    pcm_export)) {
536
		audio_format.format = mpd_format;
537
		return;
538
	}
539

540 541 542
	/* the requested sample format is not available - probe for
	   other formats supported by MPD */

543
	static constexpr SampleFormat sample_formats[] = {
544 545 546 547 548
		SampleFormat::S24_P32,
		SampleFormat::S32,
		SampleFormat::S16,
		SampleFormat::S8,
		SampleFormat::UNDEFINED /* sentinel */
549 550
	};

551
	for (unsigned i = 0; sample_formats[i] != SampleFormat::UNDEFINED; ++i) {
552
		mpd_format = sample_formats[i];
553
		if (mpd_format == audio_format.format)
554 555 556
			/* don't try that again */
			continue;

557
		if (oss_probe_sample_format(fd, mpd_format,
558 559
					    &mpd_format, oss_format_r,
					    pcm_export)) {
560
			audio_format.format = mpd_format;
561
			return;
562 563 564
		}
	}

565
	throw std::runtime_error("Failed to set sample format");
566
}
567

568 569
inline void
OssOutput::Setup(AudioFormat &_audio_format)
570
{
571 572 573 574
	oss_setup_channels(fd, _audio_format, effective_channels);
	oss_setup_sample_rate(fd, _audio_format, effective_speed);
	oss_setup_sample_format(fd, _audio_format, &effective_samplesize,
				pcm_export);
575 576
}

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
#ifdef ENABLE_OSS_DSD

void
OssOutput::SetupDop(const AudioFormat &audio_format)
{
	assert(audio_format.format == SampleFormat::DSD);

	effective_channels = audio_format.channels;

	/* DoP packs two 8-bit "samples" in one 24-bit "sample" */
	effective_speed = audio_format.sample_rate / 2;

	effective_samplesize = AFMT_S32_NE;

	OssIoctlExact(fd, SNDCTL_DSP_CHANNELS, effective_channels,
		      "Failed to set channel count");
	OssIoctlExact(fd, SNDCTL_DSP_SPEED, effective_speed,
		      "Failed to set sample rate");
	OssIoctlExact(fd, SNDCTL_DSP_SAMPLESIZE, effective_samplesize,
		      "Failed to set sample format");

	PcmExport::Params params;
	params.alsa_channel_order = true;
	params.dsd_mode = PcmExport::DsdMode::DOP;
	params.shift8 = true;

	pcm_export->Open(audio_format.format, audio_format.channels, params);
}

#endif

void
OssOutput::SetupOrDop(AudioFormat &audio_format)
{
#ifdef ENABLE_OSS_DSD
	std::exception_ptr dop_error;
	if (dop_setting && audio_format.format == SampleFormat::DSD) {
		try {
			SetupDop(audio_format);
			return;
		} catch (...) {
			dop_error = std::current_exception();
		}
	}

	try {
#endif
		Setup(audio_format);
#ifdef ENABLE_OSS_DSD
	} 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;
	}
#endif
}

638 639 640
/**
 * Reopen the device with the saved audio_format, without any probing.
 */
641 642 643
inline void
OssOutput::Reopen()
try {
644
	assert(!fd.IsDefined());
645

646
	if (!fd.Open(device, O_WRONLY))
647
		throw FormatErrno("Error opening OSS device \"%s\"", device);
648

649
	OssIoctlExact(fd, SNDCTL_DSP_CHANNELS, effective_channels,
650
		      "Failed to set channel count");
651
	OssIoctlExact(fd, SNDCTL_DSP_SPEED, effective_speed,
652
		      "Failed to set sample rate");
653
	OssIoctlExact(fd, SNDCTL_DSP_SAMPLESIZE, effective_samplesize,
654
		      "Failed to set sample format");
655 656 657
} catch (...) {
	DoClose();
	throw;
658 659
}

660
void
661
OssOutput::Open(AudioFormat &_audio_format)
662
try {
663
	if (!fd.Open(device, O_WRONLY))
664
		throw FormatErrno("Error opening OSS device \"%s\"", device);
665

666
	SetupOrDop(_audio_format);
667 668 669
} catch (...) {
	DoClose();
	throw;
670 671
}

672 673
void
OssOutput::Cancel() noexcept
Avuton Olrich's avatar
Avuton Olrich committed
674
{
675 676
	if (fd.IsDefined()) {
		ioctl(fd.Get(), SNDCTL_DSP_RESET, 0);
677
		DoClose();
678
	}
679 680

	pcm_export->Reset();
681 682
}

683
size_t
684
OssOutput::Play(const void *chunk, size_t size)
685
{
686
	ssize_t ret;
687

688 689
	assert(size > 0);

690
	/* reopen the device since it was closed by dropBufferedAudio */
691
	if (!fd.IsDefined())
692
		Reopen();
693

694
	const auto e = pcm_export->Export({chunk, size});
695 696 697
	if (e.empty())
		return size;

698 699
	chunk = e.data;
	size = e.size;
700

701
	while (true) {
702
		ret = fd.Write(chunk, size);
703 704
		if (ret > 0)
			return pcm_export->CalcInputSize(ret);
705

706 707
		if (ret < 0 && errno != EINTR)
			throw FormatErrno("Write error on %s", device);
708 709 710
	}
}

Rosen Penev's avatar
Rosen Penev committed
711
constexpr struct AudioOutputPlugin oss_output_plugin = {
712 713
	"oss",
	oss_output_test_default_device,
714
	OssOutput::Create,
715
	&oss_mixer_plugin,
716
};