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

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