🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

751 lines
20 KiB

  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.
  8. *
  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.
  13. *
  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
  19. */
  20. #include "config.h"
  21. #include "backends/oss.h"
  22. #include <sys/ioctl.h>
  23. #include <sys/types.h>
  24. #include <sys/time.h>
  25. #include <sys/stat.h>
  26. #include <fcntl.h>
  27. #include <cstdlib>
  28. #include <cstdio>
  29. #include <cstring>
  30. #include <memory.h>
  31. #include <unistd.h>
  32. #include <cerrno>
  33. #include <poll.h>
  34. #include <cmath>
  35. #include <atomic>
  36. #include <thread>
  37. #include <vector>
  38. #include <string>
  39. #include <algorithm>
  40. #include <functional>
  41. #include "alMain.h"
  42. #include "alu.h"
  43. #include "alconfig.h"
  44. #include "ringbuffer.h"
  45. #include "compat.h"
  46. #include <sys/soundcard.h>
  47. /*
  48. * The OSS documentation talks about SOUND_MIXER_READ, but the header
  49. * only contains MIXER_READ. Play safe. Same for WRITE.
  50. */
  51. #ifndef SOUND_MIXER_READ
  52. #define SOUND_MIXER_READ MIXER_READ
  53. #endif
  54. #ifndef SOUND_MIXER_WRITE
  55. #define SOUND_MIXER_WRITE MIXER_WRITE
  56. #endif
  57. #if defined(SOUND_VERSION) && (SOUND_VERSION < 0x040000)
  58. #define ALC_OSS_COMPAT
  59. #endif
  60. #ifndef SNDCTL_AUDIOINFO
  61. #define ALC_OSS_COMPAT
  62. #endif
  63. /*
  64. * FreeBSD strongly discourages the use of specific devices,
  65. * such as those returned in oss_audioinfo.devnode
  66. */
  67. #ifdef __FreeBSD__
  68. #define ALC_OSS_DEVNODE_TRUC
  69. #endif
  70. namespace {
  71. constexpr char DefaultName[] = "OSS Default";
  72. const char *DefaultPlayback{"/dev/dsp"};
  73. const char *DefaultCapture{"/dev/dsp"};
  74. struct DevMap {
  75. std::string name;
  76. std::string device_name;
  77. template<typename StrT0, typename StrT1>
  78. DevMap(StrT0&& name_, StrT1&& devname_)
  79. : name{std::forward<StrT0>(name_)}, device_name{std::forward<StrT1>(devname_)}
  80. { }
  81. };
  82. bool checkName(const al::vector<DevMap> &list, const std::string &name)
  83. {
  84. return std::find_if(list.cbegin(), list.cend(),
  85. [&name](const DevMap &entry) -> bool
  86. { return entry.name == name; }
  87. ) != list.cend();
  88. }
  89. al::vector<DevMap> PlaybackDevices;
  90. al::vector<DevMap> CaptureDevices;
  91. #ifdef ALC_OSS_COMPAT
  92. #define DSP_CAP_OUTPUT 0x00020000
  93. #define DSP_CAP_INPUT 0x00010000
  94. void ALCossListPopulate(al::vector<DevMap> *devlist, int type)
  95. {
  96. devlist->emplace_back(DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback);
  97. }
  98. #else
  99. void ALCossListAppend(al::vector<DevMap> *list, const char *handle, size_t hlen, const char *path, size_t plen)
  100. {
  101. #ifdef ALC_OSS_DEVNODE_TRUC
  102. for(size_t i{0};i < plen;i++)
  103. {
  104. if(path[i] == '.')
  105. {
  106. if(strncmp(path + i, handle + hlen + i - plen, plen - i) == 0)
  107. hlen = hlen + i - plen;
  108. plen = i;
  109. }
  110. }
  111. #endif
  112. if(handle[0] == '\0')
  113. {
  114. handle = path;
  115. hlen = plen;
  116. }
  117. std::string basename{handle, hlen};
  118. basename.erase(std::find(basename.begin(), basename.end(), '\0'), basename.end());
  119. std::string devname{path, plen};
  120. devname.erase(std::find(devname.begin(), devname.end(), '\0'), devname.end());
  121. auto iter = std::find_if(list->cbegin(), list->cend(),
  122. [&devname](const DevMap &entry) -> bool
  123. { return entry.device_name == devname; }
  124. );
  125. if(iter != list->cend())
  126. return;
  127. int count{1};
  128. std::string newname{basename};
  129. while(checkName(PlaybackDevices, newname))
  130. {
  131. newname = basename;
  132. newname += " #";
  133. newname += std::to_string(++count);
  134. }
  135. list->emplace_back(std::move(newname), std::move(devname));
  136. const DevMap &entry = list->back();
  137. TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
  138. }
  139. void ALCossListPopulate(al::vector<DevMap> *devlist, int type_flag)
  140. {
  141. int fd{open("/dev/mixer", O_RDONLY)};
  142. if(fd < 0)
  143. {
  144. TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
  145. goto done;
  146. }
  147. oss_sysinfo si;
  148. if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
  149. {
  150. TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
  151. goto done;
  152. }
  153. for(int i{0};i < si.numaudios;i++)
  154. {
  155. oss_audioinfo ai;
  156. ai.dev = i;
  157. if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
  158. {
  159. ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
  160. continue;
  161. }
  162. if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
  163. continue;
  164. const char *handle;
  165. size_t len;
  166. if(ai.handle[0] != '\0')
  167. {
  168. len = strnlen(ai.handle, sizeof(ai.handle));
  169. handle = ai.handle;
  170. }
  171. else
  172. {
  173. len = strnlen(ai.name, sizeof(ai.name));
  174. handle = ai.name;
  175. }
  176. ALCossListAppend(devlist, handle, len, ai.devnode,
  177. strnlen(ai.devnode, sizeof(ai.devnode)));
  178. }
  179. done:
  180. if(fd >= 0)
  181. close(fd);
  182. fd = -1;
  183. const char *defdev{(type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback};
  184. auto iter = std::find_if(devlist->cbegin(), devlist->cend(),
  185. [defdev](const DevMap &entry) -> bool
  186. { return entry.device_name == defdev; }
  187. );
  188. if(iter == devlist->cend())
  189. devlist->insert(devlist->begin(), DevMap{DefaultName, defdev});
  190. else
  191. {
  192. DevMap entry{std::move(*iter)};
  193. devlist->erase(iter);
  194. devlist->insert(devlist->begin(), std::move(entry));
  195. }
  196. devlist->shrink_to_fit();
  197. }
  198. #endif
  199. int log2i(ALCuint x)
  200. {
  201. int y = 0;
  202. while (x > 1)
  203. {
  204. x >>= 1;
  205. y++;
  206. }
  207. return y;
  208. }
  209. struct OSSPlayback final : public BackendBase {
  210. OSSPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
  211. ~OSSPlayback() override;
  212. int mixerProc();
  213. ALCenum open(const ALCchar *name) override;
  214. ALCboolean reset() override;
  215. ALCboolean start() override;
  216. void stop() override;
  217. int mFd{-1};
  218. al::vector<ALubyte> mMixData;
  219. std::atomic<bool> mKillNow{true};
  220. std::thread mThread;
  221. static constexpr inline const char *CurrentPrefix() noexcept { return "OSSPlayback::"; }
  222. DEF_NEWDEL(OSSPlayback)
  223. };
  224. OSSPlayback::~OSSPlayback()
  225. {
  226. if(mFd != -1)
  227. close(mFd);
  228. mFd = -1;
  229. }
  230. int OSSPlayback::mixerProc()
  231. {
  232. SetRTPriority();
  233. althrd_setname(MIXER_THREAD_NAME);
  234. const int frame_size{mDevice->frameSizeFromFmt()};
  235. lock();
  236. while(!mKillNow.load(std::memory_order_acquire) &&
  237. mDevice->Connected.load(std::memory_order_acquire))
  238. {
  239. pollfd pollitem{};
  240. pollitem.fd = mFd;
  241. pollitem.events = POLLOUT;
  242. unlock();
  243. int pret{poll(&pollitem, 1, 1000)};
  244. lock();
  245. if(pret < 0)
  246. {
  247. if(errno == EINTR || errno == EAGAIN)
  248. continue;
  249. ERR("poll failed: %s\n", strerror(errno));
  250. aluHandleDisconnect(mDevice, "Failed waiting for playback buffer: %s", strerror(errno));
  251. break;
  252. }
  253. else if(pret == 0)
  254. {
  255. WARN("poll timeout\n");
  256. continue;
  257. }
  258. ALubyte *write_ptr{mMixData.data()};
  259. size_t to_write{mMixData.size()};
  260. aluMixData(mDevice, write_ptr, to_write/frame_size);
  261. while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
  262. {
  263. ssize_t wrote{write(mFd, write_ptr, to_write)};
  264. if(wrote < 0)
  265. {
  266. if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
  267. continue;
  268. ERR("write failed: %s\n", strerror(errno));
  269. aluHandleDisconnect(mDevice, "Failed writing playback samples: %s",
  270. strerror(errno));
  271. break;
  272. }
  273. to_write -= wrote;
  274. write_ptr += wrote;
  275. }
  276. }
  277. unlock();
  278. return 0;
  279. }
  280. ALCenum OSSPlayback::open(const ALCchar *name)
  281. {
  282. const char *devname{DefaultPlayback};
  283. if(!name)
  284. name = DefaultName;
  285. else
  286. {
  287. if(PlaybackDevices.empty())
  288. ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
  289. auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
  290. [&name](const DevMap &entry) -> bool
  291. { return entry.name == name; }
  292. );
  293. if(iter == PlaybackDevices.cend())
  294. return ALC_INVALID_VALUE;
  295. devname = iter->device_name.c_str();
  296. }
  297. mFd = ::open(devname, O_WRONLY);
  298. if(mFd == -1)
  299. {
  300. ERR("Could not open %s: %s\n", devname, strerror(errno));
  301. return ALC_INVALID_VALUE;
  302. }
  303. mDevice->DeviceName = name;
  304. return ALC_NO_ERROR;
  305. }
  306. ALCboolean OSSPlayback::reset()
  307. {
  308. int numFragmentsLogSize;
  309. int log2FragmentSize;
  310. unsigned int periods;
  311. audio_buf_info info;
  312. ALuint frameSize;
  313. int numChannels;
  314. int ossFormat;
  315. int ossSpeed;
  316. const char *err;
  317. switch(mDevice->FmtType)
  318. {
  319. case DevFmtByte:
  320. ossFormat = AFMT_S8;
  321. break;
  322. case DevFmtUByte:
  323. ossFormat = AFMT_U8;
  324. break;
  325. case DevFmtUShort:
  326. case DevFmtInt:
  327. case DevFmtUInt:
  328. case DevFmtFloat:
  329. mDevice->FmtType = DevFmtShort;
  330. /* fall-through */
  331. case DevFmtShort:
  332. ossFormat = AFMT_S16_NE;
  333. break;
  334. }
  335. periods = mDevice->BufferSize / mDevice->UpdateSize;
  336. numChannels = mDevice->channelsFromFmt();
  337. ossSpeed = mDevice->Frequency;
  338. frameSize = numChannels * mDevice->bytesFromFmt();
  339. /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
  340. log2FragmentSize = maxi(log2i(mDevice->UpdateSize*frameSize), 4);
  341. numFragmentsLogSize = (periods << 16) | log2FragmentSize;
  342. #define CHECKERR(func) if((func) < 0) { \
  343. err = #func; \
  344. goto err; \
  345. }
  346. /* Don't fail if SETFRAGMENT fails. We can handle just about anything
  347. * that's reported back via GETOSPACE */
  348. ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
  349. CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
  350. CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
  351. CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
  352. CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info));
  353. if(0)
  354. {
  355. err:
  356. ERR("%s failed: %s\n", err, strerror(errno));
  357. return ALC_FALSE;
  358. }
  359. #undef CHECKERR
  360. if(mDevice->channelsFromFmt() != numChannels)
  361. {
  362. ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
  363. numChannels);
  364. return ALC_FALSE;
  365. }
  366. if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) ||
  367. (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
  368. (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
  369. {
  370. ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType),
  371. ossFormat);
  372. return ALC_FALSE;
  373. }
  374. mDevice->Frequency = ossSpeed;
  375. mDevice->UpdateSize = info.fragsize / frameSize;
  376. mDevice->BufferSize = info.fragments * mDevice->UpdateSize;
  377. SetDefaultChannelOrder(mDevice);
  378. mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
  379. return ALC_TRUE;
  380. }
  381. ALCboolean OSSPlayback::start()
  382. {
  383. try {
  384. mKillNow.store(false, std::memory_order_release);
  385. mThread = std::thread{std::mem_fn(&OSSPlayback::mixerProc), this};
  386. return ALC_TRUE;
  387. }
  388. catch(std::exception& e) {
  389. ERR("Could not create playback thread: %s\n", e.what());
  390. }
  391. catch(...) {
  392. }
  393. return ALC_FALSE;
  394. }
  395. void OSSPlayback::stop()
  396. {
  397. if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
  398. return;
  399. mThread.join();
  400. if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
  401. ERR("Error resetting device: %s\n", strerror(errno));
  402. }
  403. struct OSScapture final : public BackendBase {
  404. OSScapture(ALCdevice *device) noexcept : BackendBase{device} { }
  405. ~OSScapture() override;
  406. int recordProc();
  407. ALCenum open(const ALCchar *name) override;
  408. ALCboolean start() override;
  409. void stop() override;
  410. ALCenum captureSamples(ALCvoid *buffer, ALCuint samples) override;
  411. ALCuint availableSamples() override;
  412. int mFd{-1};
  413. RingBufferPtr mRing{nullptr};
  414. std::atomic<bool> mKillNow{true};
  415. std::thread mThread;
  416. static constexpr inline const char *CurrentPrefix() noexcept { return "OSScapture::"; }
  417. DEF_NEWDEL(OSScapture)
  418. };
  419. OSScapture::~OSScapture()
  420. {
  421. if(mFd != -1)
  422. close(mFd);
  423. mFd = -1;
  424. }
  425. int OSScapture::recordProc()
  426. {
  427. SetRTPriority();
  428. althrd_setname(RECORD_THREAD_NAME);
  429. const int frame_size{mDevice->frameSizeFromFmt()};
  430. while(!mKillNow.load(std::memory_order_acquire))
  431. {
  432. pollfd pollitem{};
  433. pollitem.fd = mFd;
  434. pollitem.events = POLLIN;
  435. int sret{poll(&pollitem, 1, 1000)};
  436. if(sret < 0)
  437. {
  438. if(errno == EINTR || errno == EAGAIN)
  439. continue;
  440. ERR("poll failed: %s\n", strerror(errno));
  441. aluHandleDisconnect(mDevice, "Failed to check capture samples: %s", strerror(errno));
  442. break;
  443. }
  444. else if(sret == 0)
  445. {
  446. WARN("poll timeout\n");
  447. continue;
  448. }
  449. auto vec = mRing->getWriteVector();
  450. if(vec.first.len > 0)
  451. {
  452. ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)};
  453. if(amt < 0)
  454. {
  455. ERR("read failed: %s\n", strerror(errno));
  456. aluHandleDisconnect(mDevice, "Failed reading capture samples: %s",
  457. strerror(errno));
  458. break;
  459. }
  460. mRing->writeAdvance(amt/frame_size);
  461. }
  462. }
  463. return 0;
  464. }
  465. ALCenum OSScapture::open(const ALCchar *name)
  466. {
  467. const char *devname{DefaultCapture};
  468. if(!name)
  469. name = DefaultName;
  470. else
  471. {
  472. if(CaptureDevices.empty())
  473. ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
  474. auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
  475. [&name](const DevMap &entry) -> bool
  476. { return entry.name == name; }
  477. );
  478. if(iter == CaptureDevices.cend())
  479. return ALC_INVALID_VALUE;
  480. devname = iter->device_name.c_str();
  481. }
  482. mFd = ::open(devname, O_RDONLY);
  483. if(mFd == -1)
  484. {
  485. ERR("Could not open %s: %s\n", devname, strerror(errno));
  486. return ALC_INVALID_VALUE;
  487. }
  488. int ossFormat{};
  489. switch(mDevice->FmtType)
  490. {
  491. case DevFmtByte:
  492. ossFormat = AFMT_S8;
  493. break;
  494. case DevFmtUByte:
  495. ossFormat = AFMT_U8;
  496. break;
  497. case DevFmtShort:
  498. ossFormat = AFMT_S16_NE;
  499. break;
  500. case DevFmtUShort:
  501. case DevFmtInt:
  502. case DevFmtUInt:
  503. case DevFmtFloat:
  504. ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
  505. return ALC_INVALID_VALUE;
  506. }
  507. int periods{4};
  508. int numChannels{mDevice->channelsFromFmt()};
  509. int frameSize{numChannels * mDevice->bytesFromFmt()};
  510. int ossSpeed{static_cast<int>(mDevice->Frequency)};
  511. int log2FragmentSize{log2i(mDevice->BufferSize * frameSize / periods)};
  512. /* according to the OSS spec, 16 bytes are the minimum */
  513. log2FragmentSize = std::max(log2FragmentSize, 4);
  514. int numFragmentsLogSize{(periods << 16) | log2FragmentSize};
  515. audio_buf_info info;
  516. const char *err;
  517. #define CHECKERR(func) if((func) < 0) { \
  518. err = #func; \
  519. goto err; \
  520. }
  521. CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
  522. CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
  523. CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
  524. CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
  525. CHECKERR(ioctl(mFd, SNDCTL_DSP_GETISPACE, &info));
  526. if(0)
  527. {
  528. err:
  529. ERR("%s failed: %s\n", err, strerror(errno));
  530. close(mFd);
  531. mFd = -1;
  532. return ALC_INVALID_VALUE;
  533. }
  534. #undef CHECKERR
  535. if(mDevice->channelsFromFmt() != numChannels)
  536. {
  537. ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
  538. numChannels);
  539. close(mFd);
  540. mFd = -1;
  541. return ALC_INVALID_VALUE;
  542. }
  543. if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) ||
  544. (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) ||
  545. (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort)))
  546. {
  547. ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType), ossFormat);
  548. close(mFd);
  549. mFd = -1;
  550. return ALC_INVALID_VALUE;
  551. }
  552. mRing = CreateRingBuffer(mDevice->BufferSize, frameSize, false);
  553. if(!mRing)
  554. {
  555. ERR("Ring buffer create failed\n");
  556. close(mFd);
  557. mFd = -1;
  558. return ALC_OUT_OF_MEMORY;
  559. }
  560. mDevice->DeviceName = name;
  561. return ALC_NO_ERROR;
  562. }
  563. ALCboolean OSScapture::start()
  564. {
  565. try {
  566. mKillNow.store(false, std::memory_order_release);
  567. mThread = std::thread{std::mem_fn(&OSScapture::recordProc), this};
  568. return ALC_TRUE;
  569. }
  570. catch(std::exception& e) {
  571. ERR("Could not create record thread: %s\n", e.what());
  572. }
  573. catch(...) {
  574. }
  575. return ALC_FALSE;
  576. }
  577. void OSScapture::stop()
  578. {
  579. if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
  580. return;
  581. mThread.join();
  582. if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
  583. ERR("Error resetting device: %s\n", strerror(errno));
  584. }
  585. ALCenum OSScapture::captureSamples(ALCvoid *buffer, ALCuint samples)
  586. {
  587. mRing->read(buffer, samples);
  588. return ALC_NO_ERROR;
  589. }
  590. ALCuint OSScapture::availableSamples()
  591. { return mRing->readSpace(); }
  592. } // namespace
  593. BackendFactory &OSSBackendFactory::getFactory()
  594. {
  595. static OSSBackendFactory factory{};
  596. return factory;
  597. }
  598. bool OSSBackendFactory::init()
  599. {
  600. ConfigValueStr(nullptr, "oss", "device", &DefaultPlayback);
  601. ConfigValueStr(nullptr, "oss", "capture", &DefaultCapture);
  602. return true;
  603. }
  604. bool OSSBackendFactory::querySupport(BackendType type)
  605. { return (type == BackendType::Playback || type == BackendType::Capture); }
  606. void OSSBackendFactory::probe(DevProbe type, std::string *outnames)
  607. {
  608. auto add_device = [outnames](const DevMap &entry) -> void
  609. {
  610. #ifdef HAVE_STAT
  611. struct stat buf;
  612. if(stat(entry.device_name.c_str(), &buf) == 0)
  613. #endif
  614. {
  615. /* Includes null char. */
  616. outnames->append(entry.name.c_str(), entry.name.length()+1);
  617. }
  618. };
  619. switch(type)
  620. {
  621. case DevProbe::Playback:
  622. PlaybackDevices.clear();
  623. ALCossListPopulate(&PlaybackDevices, DSP_CAP_OUTPUT);
  624. std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
  625. break;
  626. case DevProbe::Capture:
  627. CaptureDevices.clear();
  628. ALCossListPopulate(&CaptureDevices, DSP_CAP_INPUT);
  629. std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
  630. break;
  631. }
  632. }
  633. BackendPtr OSSBackendFactory::createBackend(ALCdevice *device, BackendType type)
  634. {
  635. if(type == BackendType::Playback)
  636. return BackendPtr{new OSSPlayback{device}};
  637. if(type == BackendType::Capture)
  638. return BackendPtr{new OSScapture{device}};
  639. return nullptr;
  640. }