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

32 33
#include <stdexcept>

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

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

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

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

62
class OssOutput final : AudioOutput {
63
#ifdef AFMT_S24_PACKED
64
	Manual<PcmExport> pcm_export;
65 66
#endif

67
	FileDescriptor fd = FileDescriptor::Undefined();
68
	const char *device;
69

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

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

82 83 84 85 86 87
#ifdef AFMT_S24_PACKED
	static constexpr unsigned oss_flags = FLAG_ENABLE_DISABLE;
#else
	static constexpr unsigned oss_flags = 0;
#endif

88
public:
89
	explicit OssOutput(const char *_device=nullptr)
90
		:AudioOutput(oss_flags),
91
		 device(_device) {}
92

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

#ifdef AFMT_S24_PACKED
97
	void Enable() override {
98 99 100
		pcm_export.Construct();
	}

101
	void Disable() noexcept override {
102 103 104 105
		pcm_export.Destruct();
	}
#endif

106
	void Open(AudioFormat &audio_format) override;
107

108
	void Close() noexcept override {
109 110 111
		DoClose();
	}

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

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

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

	void DoClose();
129 130
};

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

133 134 135 136 137 138 139
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,
};
140

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

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

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

164
	return OSS_STAT_NO_ERROR;
165 166
}

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

Max Kellermann's avatar
Max Kellermann committed
169 170
static bool
oss_output_test_default_device(void)
Avuton Olrich's avatar
Avuton Olrich committed
171
{
172 173 174
	for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) {
		UniqueFileDescriptor fd;
		if (fd.Open(default_devices[i], O_WRONLY, 0))
175
			return true;
176 177 178 179

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

182
	return false;
183
}
184

185
static OssOutput *
186
oss_open_default()
187
{
188 189
	int err[ARRAY_SIZE(default_devices)];
	enum oss_stat ret[ARRAY_SIZE(default_devices)];
190

191
	for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) {
Max Kellermann's avatar
Max Kellermann committed
192
		ret[i] = oss_stat_device(default_devices[i], &err[i]);
193
		if (ret[i] == OSS_STAT_NO_ERROR)
194
			return new OssOutput(default_devices[i]);
195 196
	}

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

221
	throw std::runtime_error("error trying to open default OSS device");
222 223
}

224
AudioOutput *
225
OssOutput::Create(EventLoop &, const ConfigBlock &block)
Avuton Olrich's avatar
Avuton Olrich committed
226
{
227
	const char *device = block.GetBlockValue("device");
228
	if (device != nullptr)
229
		return new OssOutput(device);
230

231
	return oss_open_default();
232 233
}

234 235
void
OssOutput::DoClose()
236
{
237 238
	if (fd.IsDefined())
		fd.Close();
239 240
}

241
/**
242
 * A tri-state type for oss_try_ioctl().
243
 */
244 245 246 247 248 249 250 251
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
252
 * returned.  Any other failure throws std::runtime_error.
253 254
 */
static enum oss_setup_result
255
oss_try_ioctl_r(FileDescriptor fd, unsigned long request, int *value_r,
256
		const char *msg)
Avuton Olrich's avatar
Avuton Olrich committed
257
{
258
	assert(fd.IsDefined());
259 260
	assert(value_r != nullptr);
	assert(msg != nullptr);
261

262
	int ret = ioctl(fd.Get(), request, value_r);
263 264 265 266 267 268
	if (ret >= 0)
		return SUCCESS;

	if (errno == EINVAL)
		return UNSUPPORTED;

269
	throw MakeErrno(msg);
270 271 272 273 274
}

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

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

302
		audio_format.channels = channels;
303
		return;
304 305 306

	case UNSUPPORTED:
		break;
307
	}
308

309
	for (unsigned i = 1; i < 2; ++i) {
310
		if (i == audio_format.channels)
311 312 313 314 315
			/* don't try that again */
			continue;

		channels = i;
		result = oss_try_ioctl_r(fd, SNDCTL_DSP_CHANNELS, &channels,
316
					 msg);
317 318 319 320 321
		switch (result) {
		case SUCCESS:
			if (!audio_valid_channel_count(channels))
			    break;

322
			audio_format.channels = channels;
323
			return;
324 325 326 327 328 329

		case UNSUPPORTED:
			break;
		}
	}

330
	throw std::runtime_error(msg);
331 332 333 334 335
}

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

352
		audio_format.sample_rate = sample_rate;
353
		return;
354 355 356 357 358

	case UNSUPPORTED:
		break;
	}

359
	static constexpr int sample_rates[] = { 48000, 44100, 0 };
360 361
	for (unsigned i = 0; sample_rates[i] != 0; ++i) {
		sample_rate = sample_rates[i];
362
		if (sample_rate == (int)audio_format.sample_rate)
363 364 365
			continue;

		result = oss_try_ioctl_r(fd, SNDCTL_DSP_SPEED, &sample_rate,
366
					 msg);
367 368 369 370
		switch (result) {
		case SUCCESS:
			if (!audio_valid_sample_rate(sample_rate))
				break;
371

372
			audio_format.sample_rate = sample_rate;
373
			return;
374 375 376 377

		case UNSUPPORTED:
			break;
		}
378
	}
379

380
	throw std::runtime_error(msg);
381 382 383 384 385 386
}

/**
 * Convert a MPD sample format to its OSS counterpart.  Returns
 * AFMT_QUERY if there is no direct counterpart.
 */
387
gcc_const
388
static int
389
sample_format_to_oss(SampleFormat format) noexcept
390 391
{
	switch (format) {
392 393 394
	case SampleFormat::UNDEFINED:
	case SampleFormat::FLOAT:
	case SampleFormat::DSD:
395 396
		return AFMT_QUERY;

397
	case SampleFormat::S8:
398
		return AFMT_S8;
399

400
	case SampleFormat::S16:
401 402
		return AFMT_S16_NE;

403
	case SampleFormat::S24_P32:
404 405 406 407 408 409
#ifdef AFMT_S24_NE
		return AFMT_S24_NE;
#else
		return AFMT_QUERY;
#endif

410
	case SampleFormat::S32:
411 412 413
#ifdef AFMT_S32_NE
		return AFMT_S32_NE;
#else
414
		return AFMT_QUERY;
415
#endif
416 417
	}

418 419
	assert(false);
	gcc_unreachable();
420 421 422 423
}

/**
 * Convert an OSS sample format to its MPD counterpart.  Returns
424
 * SampleFormat::UNDEFINED if there is no direct counterpart.
425
 */
426
gcc_const
427
static SampleFormat
428
sample_format_from_oss(int format) noexcept
429 430 431
{
	switch (format) {
	case AFMT_S8:
432
		return SampleFormat::S8;
433 434

	case AFMT_S16_NE:
435
		return SampleFormat::S16;
436

437 438
#ifdef AFMT_S24_PACKED
	case AFMT_S24_PACKED:
439
		return SampleFormat::S24_P32;
440 441 442 443
#endif

#ifdef AFMT_S24_NE
	case AFMT_S24_NE:
444
		return SampleFormat::S24_P32;
445 446 447 448
#endif

#ifdef AFMT_S32_NE
	case AFMT_S32_NE:
449
		return SampleFormat::S32;
450 451
#endif

452
	default:
453
		return SampleFormat::UNDEFINED;
454
	}
455
}
456

457 458 459
/**
 * Probe one sample format.
 *
460
 * @return the selected sample format or SampleFormat::UNDEFINED on
461 462 463
 * error
 */
static enum oss_setup_result
464
oss_probe_sample_format(FileDescriptor fd, SampleFormat sample_format,
465
			SampleFormat *sample_format_r,
466
			int *oss_format_r
467
#ifdef AFMT_S24_PACKED
468
			, PcmExport &pcm_export
469
#endif
470
			)
471 472 473 474 475 476 477 478
{
	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,
479
				"Failed to set sample format");
480 481

#ifdef AFMT_S24_PACKED
482
	if (result == UNSUPPORTED && sample_format == SampleFormat::S24_P32) {
483 484 485 486 487
		/* 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,
488
					 "Failed to set sample format");
489 490 491
	}
#endif

492 493 494 495
	if (result != SUCCESS)
		return result;

	sample_format = sample_format_from_oss(oss_format);
496
	if (sample_format == SampleFormat::UNDEFINED)
497 498 499
		return UNSUPPORTED;

	*sample_format_r = sample_format;
500
	*oss_format_r = oss_format;
501 502

#ifdef AFMT_S24_PACKED
503 504 505 506 507 508 509
	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);
510 511 512 513 514
#endif

	return SUCCESS;
}

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

	case UNSUPPORTED:
		break;
542
	}
543

544 545 546
	/* the requested sample format is not available - probe for
	   other formats supported by MPD */

547
	static constexpr SampleFormat sample_formats[] = {
548 549 550 551 552
		SampleFormat::S24_P32,
		SampleFormat::S32,
		SampleFormat::S16,
		SampleFormat::S8,
		SampleFormat::UNDEFINED /* sentinel */
553 554
	};

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

561
		result = oss_probe_sample_format(fd, mpd_format,
562
						 &mpd_format, oss_format_r
563
#ifdef AFMT_S24_PACKED
564
						 , pcm_export
565
#endif
566
						 );
567 568
		switch (result) {
		case SUCCESS:
569
			audio_format.format = mpd_format;
570
			return;
571 572 573 574 575 576

		case UNSUPPORTED:
			break;
		}
	}

577
	throw std::runtime_error("Failed to set sample format");
578
}
579

580 581
inline void
OssOutput::Setup(AudioFormat &_audio_format)
582
{
583 584 585
	oss_setup_channels(fd, _audio_format);
	oss_setup_sample_rate(fd, _audio_format);
	oss_setup_sample_format(fd, _audio_format, &oss_format
586
#ifdef AFMT_S24_PACKED
587
				, pcm_export
588
#endif
589
				);
590 591 592 593 594
}

/**
 * Reopen the device with the saved audio_format, without any probing.
 */
595 596 597
inline void
OssOutput::Reopen()
try {
598
	assert(!fd.IsDefined());
599

600
	if (!fd.Open(device, O_WRONLY))
601
		throw FormatErrno("Error opening OSS device \"%s\"", device);
602

603 604 605
	enum oss_setup_result result;

	const char *const msg1 = "Failed to set channel count";
606
	result = oss_try_ioctl(fd, SNDCTL_DSP_CHANNELS,
607
			       audio_format.channels, msg1);
608
	if (result != SUCCESS) {
609
		DoClose();
610
		throw std::runtime_error(msg1);
611 612 613
	}

	const char *const msg2 = "Failed to set sample rate";
614
	result = oss_try_ioctl(fd, SNDCTL_DSP_SPEED,
615
			       audio_format.sample_rate, msg2);
616
	if (result != SUCCESS) {
617
		DoClose();
618
		throw std::runtime_error(msg2);
619 620 621
	}

	const char *const msg3 = "Failed to set sample format";
622 623
	result = oss_try_ioctl(fd, SNDCTL_DSP_SAMPLESIZE,
			       oss_format,
624
			       msg3);
625
	if (result != SUCCESS) {
626
		DoClose();
627
		throw std::runtime_error(msg3);
628
	}
629 630 631
} catch (...) {
	DoClose();
	throw;
632 633
}

634
void
635
OssOutput::Open(AudioFormat &_audio_format)
636
try {
637
	if (!fd.Open(device, O_WRONLY))
638
		throw FormatErrno("Error opening OSS device \"%s\"", device);
639

640
	Setup(_audio_format);
641

642
	audio_format = _audio_format;
643 644 645
} catch (...) {
	DoClose();
	throw;
646 647
}

648 649
void
OssOutput::Cancel() noexcept
Avuton Olrich's avatar
Avuton Olrich committed
650
{
651 652
	if (fd.IsDefined()) {
		ioctl(fd.Get(), SNDCTL_DSP_RESET, 0);
653
		DoClose();
654
	}
655 656 657 658

#ifdef AFMT_S24_PACKED
	pcm_export->Reset();
#endif
659 660
}

661
size_t
662
OssOutput::Play(const void *chunk, size_t size)
663
{
664
	ssize_t ret;
665

666 667
	assert(size > 0);

668
	/* reopen the device since it was closed by dropBufferedAudio */
669
	if (!fd.IsDefined())
670
		Reopen();
671

672
#ifdef AFMT_S24_PACKED
673
	const auto e = pcm_export->Export({chunk, size});
674 675
	chunk = e.data;
	size = e.size;
676 677
#endif

678 679
	assert(size > 0);

680
	while (true) {
681
		ret = fd.Write(chunk, size);
682 683
		if (ret > 0) {
#ifdef AFMT_S24_PACKED
684
			ret = pcm_export->CalcSourceSize(ret);
685 686 687
#endif
			return ret;
		}
688

689 690
		if (ret < 0 && errno != EINTR)
			throw FormatErrno("Write error on %s", device);
691 692 693
	}
}

694
const struct AudioOutputPlugin oss_output_plugin = {
695 696
	"oss",
	oss_output_test_default_device,
697
	OssOutput::Create,
698
	&oss_mixer_plugin,
699
};