🛠️🐜 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.

533 lines
15 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 "sndio.h"
  22. #include <poll.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <thread>
  27. #include <functional>
  28. #include "alnumeric.h"
  29. #include "core/device.h"
  30. #include "core/helpers.h"
  31. #include "core/logging.h"
  32. #include "ringbuffer.h"
  33. #include "threads.h"
  34. #include "vector.h"
  35. #include <sndio.h>
  36. namespace {
  37. static const char sndio_device[] = "SndIO Default";
  38. struct SioPar : public sio_par {
  39. SioPar() { sio_initpar(this); }
  40. void clear() { sio_initpar(this); }
  41. };
  42. struct SndioPlayback final : public BackendBase {
  43. SndioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
  44. ~SndioPlayback() override;
  45. int mixerProc();
  46. void open(const char *name) override;
  47. bool reset() override;
  48. void start() override;
  49. void stop() override;
  50. sio_hdl *mSndHandle{nullptr};
  51. uint mFrameStep{};
  52. al::vector<al::byte> mBuffer;
  53. std::atomic<bool> mKillNow{true};
  54. std::thread mThread;
  55. DEF_NEWDEL(SndioPlayback)
  56. };
  57. SndioPlayback::~SndioPlayback()
  58. {
  59. if(mSndHandle)
  60. sio_close(mSndHandle);
  61. mSndHandle = nullptr;
  62. }
  63. int SndioPlayback::mixerProc()
  64. {
  65. const size_t frameStep{mFrameStep};
  66. const size_t frameSize{frameStep * mDevice->bytesFromFmt()};
  67. SetRTPriority();
  68. althrd_setname(MIXER_THREAD_NAME);
  69. while(!mKillNow.load(std::memory_order_acquire)
  70. && mDevice->Connected.load(std::memory_order_acquire))
  71. {
  72. al::span<al::byte> buffer{mBuffer};
  73. mDevice->renderSamples(buffer.data(), static_cast<uint>(buffer.size() / frameSize),
  74. frameStep);
  75. while(!buffer.empty() && !mKillNow.load(std::memory_order_acquire))
  76. {
  77. size_t wrote{sio_write(mSndHandle, buffer.data(), buffer.size())};
  78. if(wrote == 0)
  79. {
  80. ERR("sio_write failed\n");
  81. mDevice->handleDisconnect("Failed to write playback samples");
  82. break;
  83. }
  84. buffer = buffer.subspan(wrote);
  85. }
  86. }
  87. return 0;
  88. }
  89. void SndioPlayback::open(const char *name)
  90. {
  91. if(!name)
  92. name = sndio_device;
  93. else if(strcmp(name, sndio_device) != 0)
  94. throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
  95. name};
  96. sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)};
  97. if(!sndHandle)
  98. throw al::backend_exception{al::backend_error::NoDevice, "Could not open backend device"};
  99. if(mSndHandle)
  100. sio_close(mSndHandle);
  101. mSndHandle = sndHandle;
  102. mDevice->DeviceName = name;
  103. }
  104. bool SndioPlayback::reset()
  105. {
  106. SioPar par;
  107. auto tryfmt = mDevice->FmtType;
  108. retry_params:
  109. switch(tryfmt)
  110. {
  111. case DevFmtByte:
  112. par.bits = 8;
  113. par.sig = 1;
  114. break;
  115. case DevFmtUByte:
  116. par.bits = 8;
  117. par.sig = 0;
  118. break;
  119. case DevFmtShort:
  120. par.bits = 16;
  121. par.sig = 1;
  122. break;
  123. case DevFmtUShort:
  124. par.bits = 16;
  125. par.sig = 0;
  126. break;
  127. case DevFmtFloat:
  128. case DevFmtInt:
  129. par.bits = 32;
  130. par.sig = 1;
  131. break;
  132. case DevFmtUInt:
  133. par.bits = 32;
  134. par.sig = 0;
  135. break;
  136. }
  137. par.bps = SIO_BPS(par.bits);
  138. par.le = SIO_LE_NATIVE;
  139. par.msb = 1;
  140. par.rate = mDevice->Frequency;
  141. par.pchan = mDevice->channelsFromFmt();
  142. par.round = mDevice->UpdateSize;
  143. par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
  144. if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
  145. try {
  146. if(!sio_setpar(mSndHandle, &par))
  147. throw al::backend_exception{al::backend_error::DeviceError,
  148. "Failed to set device parameters"};
  149. par.clear();
  150. if(!sio_getpar(mSndHandle, &par))
  151. throw al::backend_exception{al::backend_error::DeviceError,
  152. "Failed to get device parameters"};
  153. if(par.bps > 1 && par.le != SIO_LE_NATIVE)
  154. throw al::backend_exception{al::backend_error::DeviceError,
  155. "%s-endian samples not supported", par.le ? "Little" : "Big"};
  156. if(par.bits < par.bps*8 && !par.msb)
  157. throw al::backend_exception{al::backend_error::DeviceError,
  158. "MSB-padded samples not supported (%u of %u bits)", par.bits, par.bps*8};
  159. if(par.pchan < 1)
  160. throw al::backend_exception{al::backend_error::DeviceError,
  161. "No playback channels on device"};
  162. }
  163. catch(al::backend_exception &e) {
  164. if(tryfmt == DevFmtShort)
  165. throw;
  166. par.clear();
  167. tryfmt = DevFmtShort;
  168. goto retry_params;
  169. }
  170. if(par.bps == 1)
  171. mDevice->FmtType = (par.sig==1) ? DevFmtByte : DevFmtUByte;
  172. else if(par.bps == 2)
  173. mDevice->FmtType = (par.sig==1) ? DevFmtShort : DevFmtUShort;
  174. else if(par.bps == 4)
  175. mDevice->FmtType = (par.sig==1) ? DevFmtInt : DevFmtUInt;
  176. else
  177. throw al::backend_exception{al::backend_error::DeviceError,
  178. "Unhandled sample format: %s %u-bit", (par.sig?"signed":"unsigned"), par.bps*8};
  179. mFrameStep = par.pchan;
  180. if(par.pchan != mDevice->channelsFromFmt())
  181. {
  182. WARN("Got %u channel%s for %s\n", par.pchan, (par.pchan==1)?"":"s",
  183. DevFmtChannelsString(mDevice->FmtChans));
  184. if(par.pchan < 2) mDevice->FmtChans = DevFmtMono;
  185. else mDevice->FmtChans = DevFmtStereo;
  186. }
  187. mDevice->Frequency = par.rate;
  188. setDefaultChannelOrder();
  189. mDevice->UpdateSize = par.round;
  190. mDevice->BufferSize = par.bufsz + par.round;
  191. mBuffer.resize(mDevice->UpdateSize * par.pchan*par.bps);
  192. if(par.sig == 1)
  193. std::fill(mBuffer.begin(), mBuffer.end(), al::byte{});
  194. else if(par.bits == 8)
  195. std::fill_n(mBuffer.data(), mBuffer.size(), al::byte(0x80));
  196. else if(par.bits == 16)
  197. std::fill_n(reinterpret_cast<uint16_t*>(mBuffer.data()), mBuffer.size()/2, 0x8000);
  198. else if(par.bits == 32)
  199. std::fill_n(reinterpret_cast<uint32_t*>(mBuffer.data()), mBuffer.size()/4, 0x80000000u);
  200. return true;
  201. }
  202. void SndioPlayback::start()
  203. {
  204. if(!sio_start(mSndHandle))
  205. throw al::backend_exception{al::backend_error::DeviceError, "Error starting playback"};
  206. try {
  207. mKillNow.store(false, std::memory_order_release);
  208. mThread = std::thread{std::mem_fn(&SndioPlayback::mixerProc), this};
  209. }
  210. catch(std::exception& e) {
  211. sio_stop(mSndHandle);
  212. throw al::backend_exception{al::backend_error::DeviceError,
  213. "Failed to start mixing thread: %s", e.what()};
  214. }
  215. }
  216. void SndioPlayback::stop()
  217. {
  218. if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
  219. return;
  220. mThread.join();
  221. if(!sio_stop(mSndHandle))
  222. ERR("Error stopping device\n");
  223. }
  224. /* TODO: This could be improved by avoiding the ring buffer and record thread,
  225. * counting the available samples with the sio_onmove callback and reading
  226. * directly from the device. However, this depends on reasonable support for
  227. * capture buffer sizes apps may request.
  228. */
  229. struct SndioCapture final : public BackendBase {
  230. SndioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
  231. ~SndioCapture() override;
  232. int recordProc();
  233. void open(const char *name) override;
  234. void start() override;
  235. void stop() override;
  236. void captureSamples(al::byte *buffer, uint samples) override;
  237. uint availableSamples() override;
  238. sio_hdl *mSndHandle{nullptr};
  239. RingBufferPtr mRing;
  240. std::atomic<bool> mKillNow{true};
  241. std::thread mThread;
  242. DEF_NEWDEL(SndioCapture)
  243. };
  244. SndioCapture::~SndioCapture()
  245. {
  246. if(mSndHandle)
  247. sio_close(mSndHandle);
  248. mSndHandle = nullptr;
  249. }
  250. int SndioCapture::recordProc()
  251. {
  252. SetRTPriority();
  253. althrd_setname(RECORD_THREAD_NAME);
  254. const uint frameSize{mDevice->frameSizeFromFmt()};
  255. int nfds_pre{sio_nfds(mSndHandle)};
  256. if(nfds_pre <= 0)
  257. {
  258. mDevice->handleDisconnect("Incorrect return value from sio_nfds(): %d", nfds_pre);
  259. return 1;
  260. }
  261. auto fds = std::make_unique<pollfd[]>(static_cast<uint>(nfds_pre));
  262. while(!mKillNow.load(std::memory_order_acquire)
  263. && mDevice->Connected.load(std::memory_order_acquire))
  264. {
  265. /* Wait until there's some samples to read. */
  266. const int nfds{sio_pollfd(mSndHandle, fds.get(), POLLIN)};
  267. if(nfds <= 0)
  268. {
  269. mDevice->handleDisconnect("Failed to get polling fds: %d", nfds);
  270. break;
  271. }
  272. int pollres{::poll(fds.get(), static_cast<uint>(nfds), 2000)};
  273. if(pollres < 0)
  274. {
  275. if(errno == EINTR) continue;
  276. mDevice->handleDisconnect("Poll error: %s", strerror(errno));
  277. break;
  278. }
  279. if(pollres == 0)
  280. continue;
  281. const int revents{sio_revents(mSndHandle, fds.get())};
  282. if((revents&POLLHUP))
  283. {
  284. mDevice->handleDisconnect("Got POLLHUP from poll events");
  285. break;
  286. }
  287. if(!(revents&POLLIN))
  288. continue;
  289. auto data = mRing->getWriteVector();
  290. al::span<al::byte> buffer{data.first.buf, data.first.len*frameSize};
  291. while(!buffer.empty())
  292. {
  293. size_t got{sio_read(mSndHandle, buffer.data(), buffer.size())};
  294. if(got == 0) break;
  295. mRing->writeAdvance(got / frameSize);
  296. buffer = buffer.subspan(got);
  297. if(buffer.empty())
  298. {
  299. data = mRing->getWriteVector();
  300. buffer = {data.first.buf, data.first.len*frameSize};
  301. }
  302. }
  303. if(buffer.empty())
  304. {
  305. /* Got samples to read, but no place to store it. Drop it. */
  306. static char junk[4096];
  307. sio_read(mSndHandle, junk, sizeof(junk) - (sizeof(junk)%frameSize));
  308. }
  309. }
  310. return 0;
  311. }
  312. void SndioCapture::open(const char *name)
  313. {
  314. if(!name)
  315. name = sndio_device;
  316. else if(strcmp(name, sndio_device) != 0)
  317. throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
  318. name};
  319. mSndHandle = sio_open(nullptr, SIO_REC, true);
  320. if(mSndHandle == nullptr)
  321. throw al::backend_exception{al::backend_error::NoDevice, "Could not open backend device"};
  322. SioPar par;
  323. switch(mDevice->FmtType)
  324. {
  325. case DevFmtByte:
  326. par.bits = 8;
  327. par.sig = 1;
  328. break;
  329. case DevFmtUByte:
  330. par.bits = 8;
  331. par.sig = 0;
  332. break;
  333. case DevFmtShort:
  334. par.bits = 16;
  335. par.sig = 1;
  336. break;
  337. case DevFmtUShort:
  338. par.bits = 16;
  339. par.sig = 0;
  340. break;
  341. case DevFmtInt:
  342. par.bits = 32;
  343. par.sig = 1;
  344. break;
  345. case DevFmtUInt:
  346. par.bits = 32;
  347. par.sig = 0;
  348. break;
  349. case DevFmtFloat:
  350. throw al::backend_exception{al::backend_error::DeviceError,
  351. "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
  352. }
  353. par.bps = SIO_BPS(par.bits);
  354. par.le = SIO_LE_NATIVE;
  355. par.msb = 1;
  356. par.rchan = mDevice->channelsFromFmt();
  357. par.rate = mDevice->Frequency;
  358. par.appbufsz = maxu(mDevice->BufferSize, mDevice->Frequency/10);
  359. par.round = minu(par.appbufsz/2, mDevice->Frequency/40);
  360. if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
  361. throw al::backend_exception{al::backend_error::DeviceError,
  362. "Failed to set device praameters"};
  363. if(par.bps > 1 && par.le != SIO_LE_NATIVE)
  364. throw al::backend_exception{al::backend_error::DeviceError,
  365. "%s-endian samples not supported", par.le ? "Little" : "Big"};
  366. if(par.bits < par.bps*8 && !par.msb)
  367. throw al::backend_exception{al::backend_error::DeviceError,
  368. "Padded samples not supported (got %u of %u bits)", par.bits, par.bps*8};
  369. auto match_fmt = [](DevFmtType fmttype, const sio_par &p) -> bool
  370. {
  371. return (fmttype == DevFmtByte && p.bps == 1 && p.sig != 0)
  372. || (fmttype == DevFmtUByte && p.bps == 1 && p.sig == 0)
  373. || (fmttype == DevFmtShort && p.bps == 2 && p.sig != 0)
  374. || (fmttype == DevFmtUShort && p.bps == 2 && p.sig == 0)
  375. || (fmttype == DevFmtInt && p.bps == 4 && p.sig != 0)
  376. || (fmttype == DevFmtUInt && p.bps == 4 && p.sig == 0);
  377. };
  378. if(!match_fmt(mDevice->FmtType, par) || mDevice->channelsFromFmt() != par.rchan
  379. || mDevice->Frequency != par.rate)
  380. throw al::backend_exception{al::backend_error::DeviceError,
  381. "Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead",
  382. DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans),
  383. mDevice->Frequency, par.sig?'s':'u', par.bps*8, par.rchan, par.rate};
  384. mRing = RingBuffer::Create(mDevice->BufferSize, par.bps*par.rchan, false);
  385. mDevice->BufferSize = static_cast<uint>(mRing->writeSpace());
  386. mDevice->UpdateSize = par.round;
  387. setDefaultChannelOrder();
  388. mDevice->DeviceName = name;
  389. }
  390. void SndioCapture::start()
  391. {
  392. if(!sio_start(mSndHandle))
  393. throw al::backend_exception{al::backend_error::DeviceError, "Error starting capture"};
  394. try {
  395. mKillNow.store(false, std::memory_order_release);
  396. mThread = std::thread{std::mem_fn(&SndioCapture::recordProc), this};
  397. }
  398. catch(std::exception& e) {
  399. sio_stop(mSndHandle);
  400. throw al::backend_exception{al::backend_error::DeviceError,
  401. "Failed to start capture thread: %s", e.what()};
  402. }
  403. }
  404. void SndioCapture::stop()
  405. {
  406. if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
  407. return;
  408. mThread.join();
  409. if(!sio_stop(mSndHandle))
  410. ERR("Error stopping device\n");
  411. }
  412. void SndioCapture::captureSamples(al::byte *buffer, uint samples)
  413. { mRing->read(buffer, samples); }
  414. uint SndioCapture::availableSamples()
  415. { return static_cast<uint>(mRing->readSpace()); }
  416. } // namespace
  417. BackendFactory &SndIOBackendFactory::getFactory()
  418. {
  419. static SndIOBackendFactory factory{};
  420. return factory;
  421. }
  422. bool SndIOBackendFactory::init()
  423. { return true; }
  424. bool SndIOBackendFactory::querySupport(BackendType type)
  425. { return (type == BackendType::Playback || type == BackendType::Capture); }
  426. std::string SndIOBackendFactory::probe(BackendType type)
  427. {
  428. std::string outnames;
  429. switch(type)
  430. {
  431. case BackendType::Playback:
  432. case BackendType::Capture:
  433. /* Includes null char. */
  434. outnames.append(sndio_device, sizeof(sndio_device));
  435. break;
  436. }
  437. return outnames;
  438. }
  439. BackendPtr SndIOBackendFactory::createBackend(DeviceBase *device, BackendType type)
  440. {
  441. if(type == BackendType::Playback)
  442. return BackendPtr{new SndioPlayback{device}};
  443. if(type == BackendType::Capture)
  444. return BackendPtr{new SndioCapture{device}};
  445. return nullptr;
  446. }