Add "fast" variants for the bsinc resamplers
[openal-soft.git] / alc / backends / oss.cpp
blobc825ce3eb118f7f8de8d76cac5181bb1de127e13
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include "backends/oss.h"
25 #include <fcntl.h>
26 #include <poll.h>
27 #include <sys/ioctl.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
31 #include <algorithm>
32 #include <atomic>
33 #include <cerrno>
34 #include <cstdio>
35 #include <cstring>
36 #include <exception>
37 #include <functional>
38 #include <memory>
39 #include <new>
40 #include <string>
41 #include <thread>
42 #include <utility>
44 #include "AL/al.h"
46 #include "alcmain.h"
47 #include "alconfig.h"
48 #include "almalloc.h"
49 #include "alnumeric.h"
50 #include "aloptional.h"
51 #include "alu.h"
52 #include "logging.h"
53 #include "ringbuffer.h"
54 #include "threads.h"
55 #include "vector.h"
57 #include <sys/soundcard.h>
60 * The OSS documentation talks about SOUND_MIXER_READ, but the header
61 * only contains MIXER_READ. Play safe. Same for WRITE.
63 #ifndef SOUND_MIXER_READ
64 #define SOUND_MIXER_READ MIXER_READ
65 #endif
66 #ifndef SOUND_MIXER_WRITE
67 #define SOUND_MIXER_WRITE MIXER_WRITE
68 #endif
70 #if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
71 #define ALC_OSS_COMPAT
72 #endif
73 #ifndef SNDCTL_AUDIOINFO
74 #define ALC_OSS_COMPAT
75 #endif
78 * FreeBSD strongly discourages the use of specific devices,
79 * such as those returned in oss_audioinfo.devnode
81 #ifdef __FreeBSD__
82 #define ALC_OSS_DEVNODE_TRUC
83 #endif
85 namespace {
87 constexpr char DefaultName[] = "OSS Default";
88 std::string DefaultPlayback{"/dev/dsp"};
89 std::string DefaultCapture{"/dev/dsp"};
91 struct DevMap {
92 std::string name;
93 std::string device_name;
96 bool checkName(const al::vector<DevMap> &list, const std::string &name)
98 return std::find_if(list.cbegin(), list.cend(),
99 [&name](const DevMap &entry) -> bool
100 { return entry.name == name; }
101 ) != list.cend();
104 al::vector<DevMap> PlaybackDevices;
105 al::vector<DevMap> CaptureDevices;
108 #ifdef ALC_OSS_COMPAT
110 #define DSP_CAP_OUTPUT 0x00020000
111 #define DSP_CAP_INPUT 0x00010000
112 void ALCossListPopulate(al::vector<DevMap> *devlist, int type)
114 devlist->emplace_back(DevMap{DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback});
117 #else
119 void ALCossListAppend(al::vector<DevMap> *list, const char *handle, size_t hlen, const char *path, size_t plen)
121 #ifdef ALC_OSS_DEVNODE_TRUC
122 for(size_t i{0};i < plen;i++)
124 if(path[i] == '.')
126 if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
127 hlen = hlen + i - plen;
128 plen = i;
131 #endif
132 if(handle[0] == '\0')
134 handle = path;
135 hlen = plen;
138 std::string basename{handle, hlen};
139 basename.erase(std::find(basename.begin(), basename.end(), '\0'), basename.end());
140 std::string devname{path, plen};
141 devname.erase(std::find(devname.begin(), devname.end(), '\0'), devname.end());
143 auto iter = std::find_if(list->cbegin(), list->cend(),
144 [&devname](const DevMap &entry) -> bool
145 { return entry.device_name == devname; }
147 if(iter != list->cend())
148 return;
150 int count{1};
151 std::string newname{basename};
152 while(checkName(PlaybackDevices, newname))
154 newname = basename;
155 newname += " #";
156 newname += std::to_string(++count);
159 list->emplace_back(DevMap{std::move(newname), std::move(devname)});
160 const DevMap &entry = list->back();
162 TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
165 void ALCossListPopulate(al::vector<DevMap> *devlist, int type_flag)
167 int fd{open("/dev/mixer", O_RDONLY)};
168 if(fd < 0)
170 TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
171 goto done;
174 oss_sysinfo si;
175 if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
177 TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
178 goto done;
181 for(int i{0};i < si.numaudios;i++)
183 oss_audioinfo ai;
184 ai.dev = i;
185 if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
187 ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
188 continue;
190 if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
191 continue;
193 const char *handle;
194 size_t len;
195 if(ai.handle[0] != '\0')
197 len = strnlen(ai.handle, sizeof(ai.handle));
198 handle = ai.handle;
200 else
202 len = strnlen(ai.name, sizeof(ai.name));
203 handle = ai.name;
206 ALCossListAppend(devlist, handle, len, ai.devnode,
207 strnlen(ai.devnode, sizeof(ai.devnode)));
210 done:
211 if(fd >= 0)
212 close(fd);
213 fd = -1;
215 const char *defdev{((type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback).c_str()};
216 auto iter = std::find_if(devlist->cbegin(), devlist->cend(),
217 [defdev](const DevMap &entry) -> bool
218 { return entry.device_name == defdev; }
220 if(iter == devlist->cend())
221 devlist->insert(devlist->begin(), DevMap{DefaultName, defdev});
222 else
224 DevMap entry{std::move(*iter)};
225 devlist->erase(iter);
226 devlist->insert(devlist->begin(), std::move(entry));
228 devlist->shrink_to_fit();
231 #endif
233 ALCuint log2i(ALCuint x)
235 ALCuint y{0};
236 while(x > 1)
238 x >>= 1;
239 y++;
241 return y;
245 struct OSSPlayback final : public BackendBase {
246 OSSPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
247 ~OSSPlayback() override;
249 int mixerProc();
251 ALCenum open(const ALCchar *name) override;
252 bool reset() override;
253 bool start() override;
254 void stop() override;
256 int mFd{-1};
258 al::vector<ALubyte> mMixData;
260 std::atomic<bool> mKillNow{true};
261 std::thread mThread;
263 DEF_NEWDEL(OSSPlayback)
266 OSSPlayback::~OSSPlayback()
268 if(mFd != -1)
269 close(mFd);
270 mFd = -1;
274 int OSSPlayback::mixerProc()
276 SetRTPriority();
277 althrd_setname(MIXER_THREAD_NAME);
279 const ALuint frame_size{mDevice->frameSizeFromFmt()};
281 lock();
282 while(!mKillNow.load(std::memory_order_acquire) &&
283 mDevice->Connected.load(std::memory_order_acquire))
285 pollfd pollitem{};
286 pollitem.fd = mFd;
287 pollitem.events = POLLOUT;
289 unlock();
290 int pret{poll(&pollitem, 1, 1000)};
291 lock();
292 if(pret < 0)
294 if(errno == EINTR || errno == EAGAIN)
295 continue;
296 ERR("poll failed: %s\n", strerror(errno));
297 aluHandleDisconnect(mDevice, "Failed waiting for playback buffer: %s", strerror(errno));
298 break;
300 else if(pret == 0)
302 WARN("poll timeout\n");
303 continue;
306 ALubyte *write_ptr{mMixData.data()};
307 size_t to_write{mMixData.size()};
308 aluMixData(mDevice, write_ptr, static_cast<ALuint>(to_write/frame_size));
309 while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
311 ssize_t wrote{write(mFd, write_ptr, to_write)};
312 if(wrote < 0)
314 if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
315 continue;
316 ERR("write failed: %s\n", strerror(errno));
317 aluHandleDisconnect(mDevice, "Failed writing playback samples: %s",
318 strerror(errno));
319 break;
322 to_write -= static_cast<size_t>(wrote);
323 write_ptr += wrote;
326 unlock();
328 return 0;
332 ALCenum OSSPlayback::open(const ALCchar *name)
334 const char *devname{DefaultPlayback.c_str()};
335 if(!name)
336 name = DefaultName;
337 else
339 if(PlaybackDevices.empty())
340 ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
342 auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
343 [&name](const DevMap &entry) -> bool
344 { return entry.name == name; }
346 if(iter == PlaybackDevices.cend())
347 return ALC_INVALID_VALUE;
348 devname = iter->device_name.c_str();
351 mFd = ::open(devname, O_WRONLY);
352 if(mFd == -1)
354 ERR("Could not open %s: %s\n", devname, strerror(errno));
355 return ALC_INVALID_VALUE;
358 mDevice->DeviceName = name;
359 return ALC_NO_ERROR;
362 bool OSSPlayback::reset()
364 int ossFormat{};
365 switch(mDevice->FmtType)
367 case DevFmtByte:
368 ossFormat = AFMT_S8;
369 break;
370 case DevFmtUByte:
371 ossFormat = AFMT_U8;
372 break;
373 case DevFmtUShort:
374 case DevFmtInt:
375 case DevFmtUInt:
376 case DevFmtFloat:
377 mDevice->FmtType = DevFmtShort;
378 /* fall-through */
379 case DevFmtShort:
380 ossFormat = AFMT_S16_NE;
381 break;
384 ALuint periods{mDevice->BufferSize / mDevice->UpdateSize};
385 ALuint numChannels{mDevice->channelsFromFmt()};
386 ALuint ossSpeed{mDevice->Frequency};
387 ALuint frameSize{numChannels * mDevice->bytesFromFmt()};
388 /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
389 ALuint log2FragmentSize{maxu(log2i(mDevice->UpdateSize*frameSize), 4)};
390 ALuint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
392 audio_buf_info info{};
393 const char *err;
394 #define CHECKERR(func) if((func) < 0) { \
395 err = #func; \
396 goto err; \
398 /* Don't fail if SETFRAGMENT fails. We can handle just about anything
399 * that's reported back via GETOSPACE */
400 ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
401 CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
402 CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
403 CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
404 CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info));
405 if(0)
407 err:
408 ERR("%s failed: %s\n", err, strerror(errno));
409 return false;
411 #undef CHECKERR
413 if(mDevice->channelsFromFmt() != numChannels)
415 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
416 numChannels);
417 return false;
420 if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) ||
421 (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
422 (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
424 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType),
425 ossFormat);
426 return false;
429 mDevice->Frequency = ossSpeed;
430 mDevice->UpdateSize = static_cast<ALuint>(info.fragsize) / frameSize;
431 mDevice->BufferSize = static_cast<ALuint>(info.fragments) * mDevice->UpdateSize;
433 SetDefaultChannelOrder(mDevice);
435 mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
437 return true;
440 bool OSSPlayback::start()
442 try {
443 mKillNow.store(false, std::memory_order_release);
444 mThread = std::thread{std::mem_fn(&OSSPlayback::mixerProc), this};
445 return true;
447 catch(std::exception& e) {
448 ERR("Could not create playback thread: %s\n", e.what());
450 catch(...) {
452 return false;
455 void OSSPlayback::stop()
457 if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
458 return;
459 mThread.join();
461 if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
462 ERR("Error resetting device: %s\n", strerror(errno));
466 struct OSScapture final : public BackendBase {
467 OSScapture(ALCdevice *device) noexcept : BackendBase{device} { }
468 ~OSScapture() override;
470 int recordProc();
472 ALCenum open(const ALCchar *name) override;
473 bool start() override;
474 void stop() override;
475 ALCenum captureSamples(al::byte *buffer, ALCuint samples) override;
476 ALCuint availableSamples() override;
478 int mFd{-1};
480 RingBufferPtr mRing{nullptr};
482 std::atomic<bool> mKillNow{true};
483 std::thread mThread;
485 DEF_NEWDEL(OSScapture)
488 OSScapture::~OSScapture()
490 if(mFd != -1)
491 close(mFd);
492 mFd = -1;
496 int OSScapture::recordProc()
498 SetRTPriority();
499 althrd_setname(RECORD_THREAD_NAME);
501 const ALuint frame_size{mDevice->frameSizeFromFmt()};
502 while(!mKillNow.load(std::memory_order_acquire))
504 pollfd pollitem{};
505 pollitem.fd = mFd;
506 pollitem.events = POLLIN;
508 int sret{poll(&pollitem, 1, 1000)};
509 if(sret < 0)
511 if(errno == EINTR || errno == EAGAIN)
512 continue;
513 ERR("poll failed: %s\n", strerror(errno));
514 aluHandleDisconnect(mDevice, "Failed to check capture samples: %s", strerror(errno));
515 break;
517 else if(sret == 0)
519 WARN("poll timeout\n");
520 continue;
523 auto vec = mRing->getWriteVector();
524 if(vec.first.len > 0)
526 ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)};
527 if(amt < 0)
529 ERR("read failed: %s\n", strerror(errno));
530 aluHandleDisconnect(mDevice, "Failed reading capture samples: %s",
531 strerror(errno));
532 break;
534 mRing->writeAdvance(static_cast<ALuint>(amt)/frame_size);
538 return 0;
542 ALCenum OSScapture::open(const ALCchar *name)
544 const char *devname{DefaultCapture.c_str()};
545 if(!name)
546 name = DefaultName;
547 else
549 if(CaptureDevices.empty())
550 ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
552 auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
553 [&name](const DevMap &entry) -> bool
554 { return entry.name == name; }
556 if(iter == CaptureDevices.cend())
557 return ALC_INVALID_VALUE;
558 devname = iter->device_name.c_str();
561 mFd = ::open(devname, O_RDONLY);
562 if(mFd == -1)
564 ERR("Could not open %s: %s\n", devname, strerror(errno));
565 return ALC_INVALID_VALUE;
568 int ossFormat{};
569 switch(mDevice->FmtType)
571 case DevFmtByte:
572 ossFormat = AFMT_S8;
573 break;
574 case DevFmtUByte:
575 ossFormat = AFMT_U8;
576 break;
577 case DevFmtShort:
578 ossFormat = AFMT_S16_NE;
579 break;
580 case DevFmtUShort:
581 case DevFmtInt:
582 case DevFmtUInt:
583 case DevFmtFloat:
584 ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
585 return ALC_INVALID_VALUE;
588 ALuint periods{4};
589 ALuint numChannels{mDevice->channelsFromFmt()};
590 ALuint frameSize{numChannels * mDevice->bytesFromFmt()};
591 ALuint ossSpeed{mDevice->Frequency};
592 /* according to the OSS spec, 16 bytes are the minimum */
593 ALuint log2FragmentSize{maxu(log2i(mDevice->BufferSize * frameSize / periods), 4)};
594 ALuint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
596 audio_buf_info info{};
597 const char *err;
598 #define CHECKERR(func) if((func) < 0) { \
599 err = #func; \
600 goto err; \
602 CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
603 CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
604 CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
605 CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
606 CHECKERR(ioctl(mFd, SNDCTL_DSP_GETISPACE, &info));
607 if(0)
609 err:
610 ERR("%s failed: %s\n", err, strerror(errno));
611 close(mFd);
612 mFd = -1;
613 return ALC_INVALID_VALUE;
615 #undef CHECKERR
617 if(mDevice->channelsFromFmt() != numChannels)
619 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
620 numChannels);
621 close(mFd);
622 mFd = -1;
623 return ALC_INVALID_VALUE;
626 if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) ||
627 (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
628 (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
630 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType), ossFormat);
631 close(mFd);
632 mFd = -1;
633 return ALC_INVALID_VALUE;
636 mRing = CreateRingBuffer(mDevice->BufferSize, frameSize, false);
637 if(!mRing)
639 ERR("Ring buffer create failed\n");
640 close(mFd);
641 mFd = -1;
642 return ALC_OUT_OF_MEMORY;
645 mDevice->DeviceName = name;
646 return ALC_NO_ERROR;
649 bool OSScapture::start()
651 try {
652 mKillNow.store(false, std::memory_order_release);
653 mThread = std::thread{std::mem_fn(&OSScapture::recordProc), this};
654 return true;
656 catch(std::exception& e) {
657 ERR("Could not create record thread: %s\n", e.what());
659 catch(...) {
661 return false;
664 void OSScapture::stop()
666 if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
667 return;
668 mThread.join();
670 if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
671 ERR("Error resetting device: %s\n", strerror(errno));
674 ALCenum OSScapture::captureSamples(al::byte *buffer, ALCuint samples)
676 mRing->read(buffer, samples);
677 return ALC_NO_ERROR;
680 ALCuint OSScapture::availableSamples()
681 { return static_cast<ALCuint>(mRing->readSpace()); }
683 } // namespace
686 BackendFactory &OSSBackendFactory::getFactory()
688 static OSSBackendFactory factory{};
689 return factory;
692 bool OSSBackendFactory::init()
694 if(auto devopt = ConfigValueStr(nullptr, "oss", "device"))
695 DefaultPlayback = std::move(*devopt);
696 if(auto capopt = ConfigValueStr(nullptr, "oss", "capture"))
697 DefaultCapture = std::move(*capopt);
699 return true;
702 bool OSSBackendFactory::querySupport(BackendType type)
703 { return (type == BackendType::Playback || type == BackendType::Capture); }
705 void OSSBackendFactory::probe(DevProbe type, std::string *outnames)
707 auto add_device = [outnames](const DevMap &entry) -> void
709 #ifdef HAVE_STAT
710 struct stat buf;
711 if(stat(entry.device_name.c_str(), &buf) == 0)
712 #endif
714 /* Includes null char. */
715 outnames->append(entry.name.c_str(), entry.name.length()+1);
719 switch(type)
721 case DevProbe::Playback:
722 PlaybackDevices.clear();
723 ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
724 std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
725 break;
727 case DevProbe::Capture:
728 CaptureDevices.clear();
729 ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
730 std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
731 break;
735 BackendPtr OSSBackendFactory::createBackend(ALCdevice *device, BackendType type)
737 if(type == BackendType::Playback)
738 return BackendPtr{new OSSPlayback{device}};
739 if(type == BackendType::Capture)
740 return BackendPtr{new OSScapture{device}};
741 return nullptr;