sndio.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  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/sndio.h"
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <thread>
  26. #include <functional>
  27. #include "alcmain.h"
  28. #include "alexcpt.h"
  29. #include "alu.h"
  30. #include "threads.h"
  31. #include "vector.h"
  32. #include "ringbuffer.h"
  33. #include <sndio.h>
  34. namespace {
  35. static const ALCchar sndio_device[] = "SndIO Default";
  36. struct SndioPlayback final : public BackendBase {
  37. SndioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
  38. ~SndioPlayback() override;
  39. int mixerProc();
  40. void open(const ALCchar *name) override;
  41. bool reset() override;
  42. bool start() override;
  43. void stop() override;
  44. sio_hdl *mSndHandle{nullptr};
  45. al::vector<ALubyte> mBuffer;
  46. std::atomic<bool> mKillNow{true};
  47. std::thread mThread;
  48. DEF_NEWDEL(SndioPlayback)
  49. };
  50. SndioPlayback::~SndioPlayback()
  51. {
  52. if(mSndHandle)
  53. sio_close(mSndHandle);
  54. mSndHandle = nullptr;
  55. }
  56. int SndioPlayback::mixerProc()
  57. {
  58. SetRTPriority();
  59. althrd_setname(MIXER_THREAD_NAME);
  60. const ALuint frameSize{mDevice->frameSizeFromFmt()};
  61. while(!mKillNow.load(std::memory_order_acquire) &&
  62. mDevice->Connected.load(std::memory_order_acquire))
  63. {
  64. ALubyte *WritePtr{mBuffer.data()};
  65. size_t len{mBuffer.size()};
  66. {
  67. std::lock_guard<SndioPlayback> _{*this};
  68. aluMixData(mDevice, WritePtr, static_cast<ALuint>(len/frameSize));
  69. }
  70. while(len > 0 && !mKillNow.load(std::memory_order_acquire))
  71. {
  72. size_t wrote{sio_write(mSndHandle, WritePtr, len)};
  73. if(wrote == 0)
  74. {
  75. ERR("sio_write failed\n");
  76. aluHandleDisconnect(mDevice, "Failed to write playback samples");
  77. break;
  78. }
  79. len -= wrote;
  80. WritePtr += wrote;
  81. }
  82. }
  83. return 0;
  84. }
  85. void SndioPlayback::open(const ALCchar *name)
  86. {
  87. if(!name)
  88. name = sndio_device;
  89. else if(strcmp(name, sndio_device) != 0)
  90. throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
  91. mSndHandle = sio_open(nullptr, SIO_PLAY, 0);
  92. if(mSndHandle == nullptr)
  93. throw al::backend_exception{ALC_INVALID_VALUE, "Could not open backend device"};
  94. mDevice->DeviceName = name;
  95. }
  96. bool SndioPlayback::reset()
  97. {
  98. sio_par par;
  99. sio_initpar(&par);
  100. par.rate = mDevice->Frequency;
  101. par.pchan = ((mDevice->FmtChans != DevFmtMono) ? 2 : 1);
  102. switch(mDevice->FmtType)
  103. {
  104. case DevFmtByte:
  105. par.bits = 8;
  106. par.sig = 1;
  107. break;
  108. case DevFmtUByte:
  109. par.bits = 8;
  110. par.sig = 0;
  111. break;
  112. case DevFmtFloat:
  113. case DevFmtShort:
  114. par.bits = 16;
  115. par.sig = 1;
  116. break;
  117. case DevFmtUShort:
  118. par.bits = 16;
  119. par.sig = 0;
  120. break;
  121. case DevFmtInt:
  122. par.bits = 32;
  123. par.sig = 1;
  124. break;
  125. case DevFmtUInt:
  126. par.bits = 32;
  127. par.sig = 0;
  128. break;
  129. }
  130. par.le = SIO_LE_NATIVE;
  131. par.round = mDevice->UpdateSize;
  132. par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
  133. if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
  134. if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
  135. {
  136. ERR("Failed to set device parameters\n");
  137. return false;
  138. }
  139. if(par.bits != par.bps*8)
  140. {
  141. ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
  142. return true;
  143. }
  144. mDevice->Frequency = par.rate;
  145. mDevice->FmtChans = ((par.pchan==1) ? DevFmtMono : DevFmtStereo);
  146. if(par.bits == 8 && par.sig == 1)
  147. mDevice->FmtType = DevFmtByte;
  148. else if(par.bits == 8 && par.sig == 0)
  149. mDevice->FmtType = DevFmtUByte;
  150. else if(par.bits == 16 && par.sig == 1)
  151. mDevice->FmtType = DevFmtShort;
  152. else if(par.bits == 16 && par.sig == 0)
  153. mDevice->FmtType = DevFmtUShort;
  154. else if(par.bits == 32 && par.sig == 1)
  155. mDevice->FmtType = DevFmtInt;
  156. else if(par.bits == 32 && par.sig == 0)
  157. mDevice->FmtType = DevFmtUInt;
  158. else
  159. {
  160. ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
  161. return false;
  162. }
  163. SetDefaultChannelOrder(mDevice);
  164. mDevice->UpdateSize = par.round;
  165. mDevice->BufferSize = par.bufsz + par.round;
  166. mBuffer.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
  167. std::fill(mBuffer.begin(), mBuffer.end(), 0);
  168. return true;
  169. }
  170. bool SndioPlayback::start()
  171. {
  172. if(!sio_start(mSndHandle))
  173. {
  174. ERR("Error starting playback\n");
  175. return false;
  176. }
  177. try {
  178. mKillNow.store(false, std::memory_order_release);
  179. mThread = std::thread{std::mem_fn(&SndioPlayback::mixerProc), this};
  180. return true;
  181. }
  182. catch(std::exception& e) {
  183. ERR("Could not create playback thread: %s\n", e.what());
  184. }
  185. catch(...) {
  186. }
  187. sio_stop(mSndHandle);
  188. return false;
  189. }
  190. void SndioPlayback::stop()
  191. {
  192. if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
  193. return;
  194. mThread.join();
  195. if(!sio_stop(mSndHandle))
  196. ERR("Error stopping device\n");
  197. }
  198. struct SndioCapture final : public BackendBase {
  199. SndioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
  200. ~SndioCapture() override;
  201. int recordProc();
  202. void open(const ALCchar *name) override;
  203. bool start() override;
  204. void stop() override;
  205. ALCenum captureSamples(al::byte *buffer, ALCuint samples) override;
  206. ALCuint availableSamples() override;
  207. sio_hdl *mSndHandle{nullptr};
  208. RingBufferPtr mRing;
  209. std::atomic<bool> mKillNow{true};
  210. std::thread mThread;
  211. DEF_NEWDEL(SndioCapture)
  212. };
  213. SndioCapture::~SndioCapture()
  214. {
  215. if(mSndHandle)
  216. sio_close(mSndHandle);
  217. mSndHandle = nullptr;
  218. }
  219. int SndioCapture::recordProc()
  220. {
  221. SetRTPriority();
  222. althrd_setname(RECORD_THREAD_NAME);
  223. const ALuint frameSize{mDevice->frameSizeFromFmt()};
  224. while(!mKillNow.load(std::memory_order_acquire) &&
  225. mDevice->Connected.load(std::memory_order_acquire))
  226. {
  227. auto data = mRing->getWriteVector();
  228. size_t todo{data.first.len + data.second.len};
  229. if(todo == 0)
  230. {
  231. static char junk[4096];
  232. sio_read(mSndHandle, junk,
  233. minz(sizeof(junk)/frameSize, mDevice->UpdateSize)*frameSize);
  234. continue;
  235. }
  236. size_t total{0u};
  237. data.first.len *= frameSize;
  238. data.second.len *= frameSize;
  239. todo = minz(todo, mDevice->UpdateSize) * frameSize;
  240. while(total < todo)
  241. {
  242. if(!data.first.len)
  243. data.first = data.second;
  244. size_t got{sio_read(mSndHandle, data.first.buf, minz(todo-total, data.first.len))};
  245. if(!got)
  246. {
  247. aluHandleDisconnect(mDevice, "Failed to read capture samples");
  248. break;
  249. }
  250. data.first.buf += got;
  251. data.first.len -= got;
  252. total += got;
  253. }
  254. mRing->writeAdvance(total / frameSize);
  255. }
  256. return 0;
  257. }
  258. void SndioCapture::open(const ALCchar *name)
  259. {
  260. if(!name)
  261. name = sndio_device;
  262. else if(strcmp(name, sndio_device) != 0)
  263. throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
  264. mSndHandle = sio_open(nullptr, SIO_REC, 0);
  265. if(mSndHandle == nullptr)
  266. throw al::backend_exception{ALC_INVALID_VALUE, "Could not open backend device"};
  267. sio_par par;
  268. sio_initpar(&par);
  269. switch(mDevice->FmtType)
  270. {
  271. case DevFmtByte:
  272. par.bps = 1;
  273. par.sig = 1;
  274. break;
  275. case DevFmtUByte:
  276. par.bps = 1;
  277. par.sig = 0;
  278. break;
  279. case DevFmtShort:
  280. par.bps = 2;
  281. par.sig = 1;
  282. break;
  283. case DevFmtUShort:
  284. par.bps = 2;
  285. par.sig = 0;
  286. break;
  287. case DevFmtInt:
  288. par.bps = 4;
  289. par.sig = 1;
  290. break;
  291. case DevFmtUInt:
  292. par.bps = 4;
  293. par.sig = 0;
  294. break;
  295. case DevFmtFloat:
  296. throw al::backend_exception{ALC_INVALID_VALUE, "%s capture samples not supported",
  297. DevFmtTypeString(mDevice->FmtType)};
  298. }
  299. par.bits = par.bps * 8;
  300. par.le = SIO_LE_NATIVE;
  301. par.msb = SIO_LE_NATIVE ? 0 : 1;
  302. par.rchan = mDevice->channelsFromFmt();
  303. par.rate = mDevice->Frequency;
  304. par.appbufsz = maxu(mDevice->BufferSize, mDevice->Frequency/10);
  305. par.round = minu(par.appbufsz, mDevice->Frequency/40);
  306. mDevice->UpdateSize = par.round;
  307. mDevice->BufferSize = par.appbufsz;
  308. if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
  309. throw al::backend_exception{ALC_INVALID_VALUE, "Failed to set device praameters"};
  310. if(par.bits != par.bps*8)
  311. throw al::backend_exception{ALC_INVALID_VALUE,
  312. "Padded samples not supported (got %u of %u bits)", par.bits, par.bps*8};
  313. if(!((mDevice->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0)
  314. || (mDevice->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0)
  315. || (mDevice->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0)
  316. || (mDevice->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0)
  317. || (mDevice->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0)
  318. || (mDevice->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0))
  319. || mDevice->channelsFromFmt() != par.rchan || mDevice->Frequency != par.rate)
  320. throw al::backend_exception{ALC_INVALID_VALUE,
  321. "Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead",
  322. DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans),
  323. mDevice->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate};
  324. mRing = CreateRingBuffer(mDevice->BufferSize, par.bps*par.rchan, false);
  325. SetDefaultChannelOrder(mDevice);
  326. mDevice->DeviceName = name;
  327. }
  328. bool SndioCapture::start()
  329. {
  330. if(!sio_start(mSndHandle))
  331. {
  332. ERR("Error starting playback\n");
  333. return false;
  334. }
  335. try {
  336. mKillNow.store(false, std::memory_order_release);
  337. mThread = std::thread{std::mem_fn(&SndioCapture::recordProc), this};
  338. return true;
  339. }
  340. catch(std::exception& e) {
  341. ERR("Could not create record thread: %s\n", e.what());
  342. }
  343. catch(...) {
  344. }
  345. sio_stop(mSndHandle);
  346. return false;
  347. }
  348. void SndioCapture::stop()
  349. {
  350. if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
  351. return;
  352. mThread.join();
  353. if(!sio_stop(mSndHandle))
  354. ERR("Error stopping device\n");
  355. }
  356. ALCenum SndioCapture::captureSamples(al::byte *buffer, ALCuint samples)
  357. {
  358. mRing->read(buffer, samples);
  359. return ALC_NO_ERROR;
  360. }
  361. ALCuint SndioCapture::availableSamples()
  362. { return static_cast<ALCuint>(mRing->readSpace()); }
  363. } // namespace
  364. BackendFactory &SndIOBackendFactory::getFactory()
  365. {
  366. static SndIOBackendFactory factory{};
  367. return factory;
  368. }
  369. bool SndIOBackendFactory::init()
  370. { return true; }
  371. bool SndIOBackendFactory::querySupport(BackendType type)
  372. { return (type == BackendType::Playback || type == BackendType::Capture); }
  373. void SndIOBackendFactory::probe(DevProbe type, std::string *outnames)
  374. {
  375. switch(type)
  376. {
  377. case DevProbe::Playback:
  378. case DevProbe::Capture:
  379. /* Includes null char. */
  380. outnames->append(sndio_device, sizeof(sndio_device));
  381. break;
  382. }
  383. }
  384. BackendPtr SndIOBackendFactory::createBackend(ALCdevice *device, BackendType type)
  385. {
  386. if(type == BackendType::Playback)
  387. return BackendPtr{new SndioPlayback{device}};
  388. if(type == BackendType::Capture)
  389. return BackendPtr{new SndioCapture{device}};
  390. return nullptr;
  391. }