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

447 lines
13 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 "portaudio.h"
  22. #include <cstdio>
  23. #include <cstdlib>
  24. #include <cstring>
  25. #include "alc/alconfig.h"
  26. #include "alnumeric.h"
  27. #include "core/device.h"
  28. #include "core/logging.h"
  29. #include "dynload.h"
  30. #include "ringbuffer.h"
  31. #include <portaudio.h>
  32. namespace {
  33. constexpr char pa_device[] = "PortAudio Default";
  34. #ifdef HAVE_DYNLOAD
  35. void *pa_handle;
  36. #define MAKE_FUNC(x) decltype(x) * p##x
  37. MAKE_FUNC(Pa_Initialize);
  38. MAKE_FUNC(Pa_Terminate);
  39. MAKE_FUNC(Pa_GetErrorText);
  40. MAKE_FUNC(Pa_StartStream);
  41. MAKE_FUNC(Pa_StopStream);
  42. MAKE_FUNC(Pa_OpenStream);
  43. MAKE_FUNC(Pa_CloseStream);
  44. MAKE_FUNC(Pa_GetDefaultOutputDevice);
  45. MAKE_FUNC(Pa_GetDefaultInputDevice);
  46. MAKE_FUNC(Pa_GetStreamInfo);
  47. #undef MAKE_FUNC
  48. #ifndef IN_IDE_PARSER
  49. #define Pa_Initialize pPa_Initialize
  50. #define Pa_Terminate pPa_Terminate
  51. #define Pa_GetErrorText pPa_GetErrorText
  52. #define Pa_StartStream pPa_StartStream
  53. #define Pa_StopStream pPa_StopStream
  54. #define Pa_OpenStream pPa_OpenStream
  55. #define Pa_CloseStream pPa_CloseStream
  56. #define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
  57. #define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
  58. #define Pa_GetStreamInfo pPa_GetStreamInfo
  59. #endif
  60. #endif
  61. struct PortPlayback final : public BackendBase {
  62. PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
  63. ~PortPlayback() override;
  64. int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
  65. const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept;
  66. static int writeCallbackC(const void *inputBuffer, void *outputBuffer,
  67. unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
  68. const PaStreamCallbackFlags statusFlags, void *userData) noexcept
  69. {
  70. return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
  71. framesPerBuffer, timeInfo, statusFlags);
  72. }
  73. void open(const char *name) override;
  74. bool reset() override;
  75. void start() override;
  76. void stop() override;
  77. PaStream *mStream{nullptr};
  78. PaStreamParameters mParams{};
  79. uint mUpdateSize{0u};
  80. DEF_NEWDEL(PortPlayback)
  81. };
  82. PortPlayback::~PortPlayback()
  83. {
  84. PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
  85. if(err != paNoError)
  86. ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
  87. mStream = nullptr;
  88. }
  89. int PortPlayback::writeCallback(const void*, void *outputBuffer, unsigned long framesPerBuffer,
  90. const PaStreamCallbackTimeInfo*, const PaStreamCallbackFlags) noexcept
  91. {
  92. mDevice->renderSamples(outputBuffer, static_cast<uint>(framesPerBuffer),
  93. static_cast<uint>(mParams.channelCount));
  94. return 0;
  95. }
  96. void PortPlayback::open(const char *name)
  97. {
  98. if(!name)
  99. name = pa_device;
  100. else if(strcmp(name, pa_device) != 0)
  101. throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
  102. name};
  103. PaStreamParameters params{};
  104. auto devidopt = ConfigValueInt(nullptr, "port", "device");
  105. if(devidopt && *devidopt >= 0) params.device = *devidopt;
  106. else params.device = Pa_GetDefaultOutputDevice();
  107. params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
  108. params.hostApiSpecificStreamInfo = nullptr;
  109. params.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
  110. switch(mDevice->FmtType)
  111. {
  112. case DevFmtByte:
  113. params.sampleFormat = paInt8;
  114. break;
  115. case DevFmtUByte:
  116. params.sampleFormat = paUInt8;
  117. break;
  118. case DevFmtUShort:
  119. /* fall-through */
  120. case DevFmtShort:
  121. params.sampleFormat = paInt16;
  122. break;
  123. case DevFmtUInt:
  124. /* fall-through */
  125. case DevFmtInt:
  126. params.sampleFormat = paInt32;
  127. break;
  128. case DevFmtFloat:
  129. params.sampleFormat = paFloat32;
  130. break;
  131. }
  132. retry_open:
  133. PaStream *stream{};
  134. PaError err{Pa_OpenStream(&stream, nullptr, &params, mDevice->Frequency, mDevice->UpdateSize,
  135. paNoFlag, &PortPlayback::writeCallbackC, this)};
  136. if(err != paNoError)
  137. {
  138. if(params.sampleFormat == paFloat32)
  139. {
  140. params.sampleFormat = paInt16;
  141. goto retry_open;
  142. }
  143. throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
  144. Pa_GetErrorText(err)};
  145. }
  146. Pa_CloseStream(mStream);
  147. mStream = stream;
  148. mParams = params;
  149. mUpdateSize = mDevice->UpdateSize;
  150. mDevice->DeviceName = name;
  151. }
  152. bool PortPlayback::reset()
  153. {
  154. const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
  155. mDevice->Frequency = static_cast<uint>(streamInfo->sampleRate);
  156. mDevice->UpdateSize = mUpdateSize;
  157. if(mParams.sampleFormat == paInt8)
  158. mDevice->FmtType = DevFmtByte;
  159. else if(mParams.sampleFormat == paUInt8)
  160. mDevice->FmtType = DevFmtUByte;
  161. else if(mParams.sampleFormat == paInt16)
  162. mDevice->FmtType = DevFmtShort;
  163. else if(mParams.sampleFormat == paInt32)
  164. mDevice->FmtType = DevFmtInt;
  165. else if(mParams.sampleFormat == paFloat32)
  166. mDevice->FmtType = DevFmtFloat;
  167. else
  168. {
  169. ERR("Unexpected sample format: 0x%lx\n", mParams.sampleFormat);
  170. return false;
  171. }
  172. if(mParams.channelCount >= 2)
  173. mDevice->FmtChans = DevFmtStereo;
  174. else if(mParams.channelCount == 1)
  175. mDevice->FmtChans = DevFmtMono;
  176. else
  177. {
  178. ERR("Unexpected channel count: %u\n", mParams.channelCount);
  179. return false;
  180. }
  181. setDefaultChannelOrder();
  182. return true;
  183. }
  184. void PortPlayback::start()
  185. {
  186. const PaError err{Pa_StartStream(mStream)};
  187. if(err == paNoError)
  188. throw al::backend_exception{al::backend_error::DeviceError, "Failed to start playback: %s",
  189. Pa_GetErrorText(err)};
  190. }
  191. void PortPlayback::stop()
  192. {
  193. PaError err{Pa_StopStream(mStream)};
  194. if(err != paNoError)
  195. ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
  196. }
  197. struct PortCapture final : public BackendBase {
  198. PortCapture(DeviceBase *device) noexcept : BackendBase{device} { }
  199. ~PortCapture() override;
  200. int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
  201. const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept;
  202. static int readCallbackC(const void *inputBuffer, void *outputBuffer,
  203. unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
  204. const PaStreamCallbackFlags statusFlags, void *userData) noexcept
  205. {
  206. return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
  207. framesPerBuffer, timeInfo, statusFlags);
  208. }
  209. void open(const char *name) override;
  210. void start() override;
  211. void stop() override;
  212. void captureSamples(al::byte *buffer, uint samples) override;
  213. uint availableSamples() override;
  214. PaStream *mStream{nullptr};
  215. PaStreamParameters mParams;
  216. RingBufferPtr mRing{nullptr};
  217. DEF_NEWDEL(PortCapture)
  218. };
  219. PortCapture::~PortCapture()
  220. {
  221. PaError err{mStream ? Pa_CloseStream(mStream) : paNoError};
  222. if(err != paNoError)
  223. ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
  224. mStream = nullptr;
  225. }
  226. int PortCapture::readCallback(const void *inputBuffer, void*, unsigned long framesPerBuffer,
  227. const PaStreamCallbackTimeInfo*, const PaStreamCallbackFlags) noexcept
  228. {
  229. mRing->write(inputBuffer, framesPerBuffer);
  230. return 0;
  231. }
  232. void PortCapture::open(const char *name)
  233. {
  234. if(!name)
  235. name = pa_device;
  236. else if(strcmp(name, pa_device) != 0)
  237. throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
  238. name};
  239. uint samples{mDevice->BufferSize};
  240. samples = maxu(samples, 100 * mDevice->Frequency / 1000);
  241. uint frame_size{mDevice->frameSizeFromFmt()};
  242. mRing = RingBuffer::Create(samples, frame_size, false);
  243. auto devidopt = ConfigValueInt(nullptr, "port", "capture");
  244. if(devidopt && *devidopt >= 0) mParams.device = *devidopt;
  245. else mParams.device = Pa_GetDefaultOutputDevice();
  246. mParams.suggestedLatency = 0.0f;
  247. mParams.hostApiSpecificStreamInfo = nullptr;
  248. switch(mDevice->FmtType)
  249. {
  250. case DevFmtByte:
  251. mParams.sampleFormat = paInt8;
  252. break;
  253. case DevFmtUByte:
  254. mParams.sampleFormat = paUInt8;
  255. break;
  256. case DevFmtShort:
  257. mParams.sampleFormat = paInt16;
  258. break;
  259. case DevFmtInt:
  260. mParams.sampleFormat = paInt32;
  261. break;
  262. case DevFmtFloat:
  263. mParams.sampleFormat = paFloat32;
  264. break;
  265. case DevFmtUInt:
  266. case DevFmtUShort:
  267. throw al::backend_exception{al::backend_error::DeviceError, "%s samples not supported",
  268. DevFmtTypeString(mDevice->FmtType)};
  269. }
  270. mParams.channelCount = static_cast<int>(mDevice->channelsFromFmt());
  271. PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency,
  272. paFramesPerBufferUnspecified, paNoFlag, &PortCapture::readCallbackC, this)};
  273. if(err != paNoError)
  274. throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
  275. Pa_GetErrorText(err)};
  276. mDevice->DeviceName = name;
  277. }
  278. void PortCapture::start()
  279. {
  280. const PaError err{Pa_StartStream(mStream)};
  281. if(err != paNoError)
  282. throw al::backend_exception{al::backend_error::DeviceError,
  283. "Failed to start recording: %s", Pa_GetErrorText(err)};
  284. }
  285. void PortCapture::stop()
  286. {
  287. PaError err{Pa_StopStream(mStream)};
  288. if(err != paNoError)
  289. ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
  290. }
  291. uint PortCapture::availableSamples()
  292. { return static_cast<uint>(mRing->readSpace()); }
  293. void PortCapture::captureSamples(al::byte *buffer, uint samples)
  294. { mRing->read(buffer, samples); }
  295. } // namespace
  296. bool PortBackendFactory::init()
  297. {
  298. PaError err;
  299. #ifdef HAVE_DYNLOAD
  300. if(!pa_handle)
  301. {
  302. #ifdef _WIN32
  303. # define PALIB "portaudio.dll"
  304. #elif defined(__APPLE__) && defined(__MACH__)
  305. # define PALIB "libportaudio.2.dylib"
  306. #elif defined(__OpenBSD__)
  307. # define PALIB "libportaudio.so"
  308. #else
  309. # define PALIB "libportaudio.so.2"
  310. #endif
  311. pa_handle = LoadLib(PALIB);
  312. if(!pa_handle)
  313. return false;
  314. #define LOAD_FUNC(f) do { \
  315. p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pa_handle, #f)); \
  316. if(p##f == nullptr) \
  317. { \
  318. CloseLib(pa_handle); \
  319. pa_handle = nullptr; \
  320. return false; \
  321. } \
  322. } while(0)
  323. LOAD_FUNC(Pa_Initialize);
  324. LOAD_FUNC(Pa_Terminate);
  325. LOAD_FUNC(Pa_GetErrorText);
  326. LOAD_FUNC(Pa_StartStream);
  327. LOAD_FUNC(Pa_StopStream);
  328. LOAD_FUNC(Pa_OpenStream);
  329. LOAD_FUNC(Pa_CloseStream);
  330. LOAD_FUNC(Pa_GetDefaultOutputDevice);
  331. LOAD_FUNC(Pa_GetDefaultInputDevice);
  332. LOAD_FUNC(Pa_GetStreamInfo);
  333. #undef LOAD_FUNC
  334. if((err=Pa_Initialize()) != paNoError)
  335. {
  336. ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
  337. CloseLib(pa_handle);
  338. pa_handle = nullptr;
  339. return false;
  340. }
  341. }
  342. #else
  343. if((err=Pa_Initialize()) != paNoError)
  344. {
  345. ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
  346. return false;
  347. }
  348. #endif
  349. return true;
  350. }
  351. bool PortBackendFactory::querySupport(BackendType type)
  352. { return (type == BackendType::Playback || type == BackendType::Capture); }
  353. std::string PortBackendFactory::probe(BackendType type)
  354. {
  355. std::string outnames;
  356. switch(type)
  357. {
  358. case BackendType::Playback:
  359. case BackendType::Capture:
  360. /* Includes null char. */
  361. outnames.append(pa_device, sizeof(pa_device));
  362. break;
  363. }
  364. return outnames;
  365. }
  366. BackendPtr PortBackendFactory::createBackend(DeviceBase *device, BackendType type)
  367. {
  368. if(type == BackendType::Playback)
  369. return BackendPtr{new PortPlayback{device}};
  370. if(type == BackendType::Capture)
  371. return BackendPtr{new PortCapture{device}};
  372. return nullptr;
  373. }
  374. BackendFactory &PortBackendFactory::getFactory()
  375. {
  376. static PortBackendFactory factory{};
  377. return factory;
  378. }