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

302 lines
8.0 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/solaris.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 <stdlib.h>
  28. #include <stdio.h>
  29. #include <memory.h>
  30. #include <unistd.h>
  31. #include <errno.h>
  32. #include <poll.h>
  33. #include <math.h>
  34. #include <thread>
  35. #include <functional>
  36. #include "alMain.h"
  37. #include "alu.h"
  38. #include "alconfig.h"
  39. #include "threads.h"
  40. #include "vector.h"
  41. #include "compat.h"
  42. #include <sys/audioio.h>
  43. namespace {
  44. constexpr ALCchar solaris_device[] = "Solaris Default";
  45. const char *solaris_driver = "/dev/audio";
  46. struct SolarisBackend final : public BackendBase {
  47. SolarisBackend(ALCdevice *device) noexcept : BackendBase{device} { }
  48. ~SolarisBackend() override;
  49. int mixerProc();
  50. ALCenum open(const ALCchar *name) override;
  51. ALCboolean reset() override;
  52. ALCboolean start() override;
  53. void stop() override;
  54. int mFd{-1};
  55. al::vector<ALubyte> mBuffer;
  56. std::atomic<bool> mKillNow{true};
  57. std::thread mThread;
  58. static constexpr inline const char *CurrentPrefix() noexcept { return "SolarisBackend::"; }
  59. DEF_NEWDEL(SolarisBackend)
  60. };
  61. SolarisBackend::~SolarisBackend()
  62. {
  63. if(mFd != -1)
  64. close(mFd);
  65. mFd = -1;
  66. }
  67. int SolarisBackend::mixerProc()
  68. {
  69. SetRTPriority();
  70. althrd_setname(MIXER_THREAD_NAME);
  71. const int frame_size{mDevice->frameSizeFromFmt()};
  72. lock();
  73. while(!mKillNow.load(std::memory_order_acquire) &&
  74. mDevice->Connected.load(std::memory_order_acquire))
  75. {
  76. pollfd pollitem{};
  77. pollitem.fd = mFd;
  78. pollitem.events = POLLOUT;
  79. unlock();
  80. int pret{poll(&pollitem, 1, 1000)};
  81. lock();
  82. if(pret < 0)
  83. {
  84. if(errno == EINTR || errno == EAGAIN)
  85. continue;
  86. ERR("poll failed: %s\n", strerror(errno));
  87. aluHandleDisconnect(mDevice, "Failed to wait for playback buffer: %s",
  88. strerror(errno));
  89. break;
  90. }
  91. else if(pret == 0)
  92. {
  93. WARN("poll timeout\n");
  94. continue;
  95. }
  96. ALubyte *write_ptr{mBuffer.data()};
  97. size_t to_write{mBuffer.size()};
  98. aluMixData(mDevice, write_ptr, to_write/frame_size);
  99. while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
  100. {
  101. ssize_t wrote{write(mFd, write_ptr, to_write)};
  102. if(wrote < 0)
  103. {
  104. if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
  105. continue;
  106. ERR("write failed: %s\n", strerror(errno));
  107. aluHandleDisconnect(mDevice, "Failed to write playback samples: %s",
  108. strerror(errno));
  109. break;
  110. }
  111. to_write -= wrote;
  112. write_ptr += wrote;
  113. }
  114. }
  115. unlock();
  116. return 0;
  117. }
  118. ALCenum SolarisBackend::open(const ALCchar *name)
  119. {
  120. if(!name)
  121. name = solaris_device;
  122. else if(strcmp(name, solaris_device) != 0)
  123. return ALC_INVALID_VALUE;
  124. mFd = ::open(solaris_driver, O_WRONLY);
  125. if(mFd == -1)
  126. {
  127. ERR("Could not open %s: %s\n", solaris_driver, strerror(errno));
  128. return ALC_INVALID_VALUE;
  129. }
  130. mDevice->DeviceName = name;
  131. return ALC_NO_ERROR;
  132. }
  133. ALCboolean SolarisBackend::reset()
  134. {
  135. audio_info_t info;
  136. AUDIO_INITINFO(&info);
  137. info.play.sample_rate = mDevice->Frequency;
  138. if(mDevice->FmtChans != DevFmtMono)
  139. mDevice->FmtChans = DevFmtStereo;
  140. ALsizei numChannels{mDevice->channelsFromFmt()};
  141. info.play.channels = numChannels;
  142. switch(mDevice->FmtType)
  143. {
  144. case DevFmtByte:
  145. info.play.precision = 8;
  146. info.play.encoding = AUDIO_ENCODING_LINEAR;
  147. break;
  148. case DevFmtUByte:
  149. info.play.precision = 8;
  150. info.play.encoding = AUDIO_ENCODING_LINEAR8;
  151. break;
  152. case DevFmtUShort:
  153. case DevFmtInt:
  154. case DevFmtUInt:
  155. case DevFmtFloat:
  156. mDevice->FmtType = DevFmtShort;
  157. /* fall-through */
  158. case DevFmtShort:
  159. info.play.precision = 16;
  160. info.play.encoding = AUDIO_ENCODING_LINEAR;
  161. break;
  162. }
  163. ALsizei frameSize{numChannels * mDevice->bytesFromFmt()};
  164. info.play.buffer_size = mDevice->BufferSize * frameSize;
  165. if(ioctl(mFd, AUDIO_SETINFO, &info) < 0)
  166. {
  167. ERR("ioctl failed: %s\n", strerror(errno));
  168. return ALC_FALSE;
  169. }
  170. if(mDevice->channelsFromFmt() != (ALsizei)info.play.channels)
  171. {
  172. ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
  173. info.play.channels);
  174. return ALC_FALSE;
  175. }
  176. if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && mDevice->FmtType == DevFmtUByte) ||
  177. (info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtByte) ||
  178. (info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtShort) ||
  179. (info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtInt)))
  180. {
  181. ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(mDevice->FmtType),
  182. info.play.precision, info.play.encoding);
  183. return ALC_FALSE;
  184. }
  185. mDevice->Frequency = info.play.sample_rate;
  186. mDevice->BufferSize = info.play.buffer_size / frameSize;
  187. mDevice->UpdateSize = mDevice->BufferSize / 2;
  188. SetDefaultChannelOrder(mDevice);
  189. mBuffer.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
  190. std::fill(mBuffer.begin(), mBuffer.end(), 0);
  191. return ALC_TRUE;
  192. }
  193. ALCboolean SolarisBackend::start()
  194. {
  195. try {
  196. mKillNow.store(false, std::memory_order_release);
  197. mThread = std::thread{std::mem_fn(&SolarisBackend::mixerProc), this};
  198. return ALC_TRUE;
  199. }
  200. catch(std::exception& e) {
  201. ERR("Could not create playback thread: %s\n", e.what());
  202. }
  203. catch(...) {
  204. }
  205. return ALC_FALSE;
  206. }
  207. void SolarisBackend::stop()
  208. {
  209. if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
  210. return;
  211. mThread.join();
  212. if(ioctl(mFd, AUDIO_DRAIN) < 0)
  213. ERR("Error draining device: %s\n", strerror(errno));
  214. }
  215. } // namespace
  216. BackendFactory &SolarisBackendFactory::getFactory()
  217. {
  218. static SolarisBackendFactory factory{};
  219. return factory;
  220. }
  221. bool SolarisBackendFactory::init()
  222. {
  223. ConfigValueStr(nullptr, "solaris", "device", &solaris_driver);
  224. return true;
  225. }
  226. bool SolarisBackendFactory::querySupport(BackendType type)
  227. { return type == BackendType::Playback; }
  228. void SolarisBackendFactory::probe(DevProbe type, std::string *outnames)
  229. {
  230. switch(type)
  231. {
  232. case DevProbe::Playback:
  233. {
  234. #ifdef HAVE_STAT
  235. struct stat buf;
  236. if(stat(solaris_driver, &buf) == 0)
  237. #endif
  238. outnames->append(solaris_device, sizeof(solaris_device));
  239. }
  240. break;
  241. case DevProbe::Capture:
  242. break;
  243. }
  244. }
  245. BackendPtr SolarisBackendFactory::createBackend(ALCdevice *device, BackendType type)
  246. {
  247. if(type == BackendType::Playback)
  248. return BackendPtr{new SolarisBackend{device}};
  249. return nullptr;
  250. }