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

739 lines
24 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 "jack.h"
  22. #include <cstdlib>
  23. #include <cstdio>
  24. #include <cstring>
  25. #include <memory.h>
  26. #include <array>
  27. #include <thread>
  28. #include <functional>
  29. #include "alc/alconfig.h"
  30. #include "alnumeric.h"
  31. #include "core/device.h"
  32. #include "core/helpers.h"
  33. #include "core/logging.h"
  34. #include "dynload.h"
  35. #include "ringbuffer.h"
  36. #include "threads.h"
  37. #include <jack/jack.h>
  38. #include <jack/ringbuffer.h>
  39. namespace {
  40. #ifdef HAVE_DYNLOAD
  41. #define JACK_FUNCS(MAGIC) \
  42. MAGIC(jack_client_open); \
  43. MAGIC(jack_client_close); \
  44. MAGIC(jack_client_name_size); \
  45. MAGIC(jack_get_client_name); \
  46. MAGIC(jack_connect); \
  47. MAGIC(jack_activate); \
  48. MAGIC(jack_deactivate); \
  49. MAGIC(jack_port_register); \
  50. MAGIC(jack_port_unregister); \
  51. MAGIC(jack_port_get_buffer); \
  52. MAGIC(jack_port_name); \
  53. MAGIC(jack_get_ports); \
  54. MAGIC(jack_free); \
  55. MAGIC(jack_get_sample_rate); \
  56. MAGIC(jack_set_error_function); \
  57. MAGIC(jack_set_process_callback); \
  58. MAGIC(jack_set_buffer_size_callback); \
  59. MAGIC(jack_set_buffer_size); \
  60. MAGIC(jack_get_buffer_size);
  61. void *jack_handle;
  62. #define MAKE_FUNC(f) decltype(f) * p##f
  63. JACK_FUNCS(MAKE_FUNC)
  64. decltype(jack_error_callback) * pjack_error_callback;
  65. #undef MAKE_FUNC
  66. #ifndef IN_IDE_PARSER
  67. #define jack_client_open pjack_client_open
  68. #define jack_client_close pjack_client_close
  69. #define jack_client_name_size pjack_client_name_size
  70. #define jack_get_client_name pjack_get_client_name
  71. #define jack_connect pjack_connect
  72. #define jack_activate pjack_activate
  73. #define jack_deactivate pjack_deactivate
  74. #define jack_port_register pjack_port_register
  75. #define jack_port_unregister pjack_port_unregister
  76. #define jack_port_get_buffer pjack_port_get_buffer
  77. #define jack_port_name pjack_port_name
  78. #define jack_get_ports pjack_get_ports
  79. #define jack_free pjack_free
  80. #define jack_get_sample_rate pjack_get_sample_rate
  81. #define jack_set_error_function pjack_set_error_function
  82. #define jack_set_process_callback pjack_set_process_callback
  83. #define jack_set_buffer_size_callback pjack_set_buffer_size_callback
  84. #define jack_set_buffer_size pjack_set_buffer_size
  85. #define jack_get_buffer_size pjack_get_buffer_size
  86. #define jack_error_callback (*pjack_error_callback)
  87. #endif
  88. #endif
  89. constexpr char JackDefaultAudioType[] = JACK_DEFAULT_AUDIO_TYPE;
  90. jack_options_t ClientOptions = JackNullOption;
  91. bool jack_load()
  92. {
  93. bool error{false};
  94. #ifdef HAVE_DYNLOAD
  95. if(!jack_handle)
  96. {
  97. std::string missing_funcs;
  98. #ifdef _WIN32
  99. #define JACKLIB "libjack.dll"
  100. #else
  101. #define JACKLIB "libjack.so.0"
  102. #endif
  103. jack_handle = LoadLib(JACKLIB);
  104. if(!jack_handle)
  105. {
  106. WARN("Failed to load %s\n", JACKLIB);
  107. return false;
  108. }
  109. error = false;
  110. #define LOAD_FUNC(f) do { \
  111. p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \
  112. if(p##f == nullptr) { \
  113. error = true; \
  114. missing_funcs += "\n" #f; \
  115. } \
  116. } while(0)
  117. JACK_FUNCS(LOAD_FUNC);
  118. #undef LOAD_FUNC
  119. /* Optional symbols. These don't exist in all versions of JACK. */
  120. #define LOAD_SYM(f) p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f))
  121. LOAD_SYM(jack_error_callback);
  122. #undef LOAD_SYM
  123. if(error)
  124. {
  125. WARN("Missing expected functions:%s\n", missing_funcs.c_str());
  126. CloseLib(jack_handle);
  127. jack_handle = nullptr;
  128. }
  129. }
  130. #endif
  131. return !error;
  132. }
  133. struct JackDeleter {
  134. void operator()(void *ptr) { jack_free(ptr); }
  135. };
  136. using JackPortsPtr = std::unique_ptr<const char*[],JackDeleter>;
  137. struct DeviceEntry {
  138. std::string mName;
  139. std::string mPattern;
  140. };
  141. al::vector<DeviceEntry> PlaybackList;
  142. void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list)
  143. {
  144. std::remove_reference_t<decltype(list)>{}.swap(list);
  145. if(JackPortsPtr ports{jack_get_ports(client, nullptr, JackDefaultAudioType, JackPortIsInput)})
  146. {
  147. for(size_t i{0};ports[i];++i)
  148. {
  149. const char *sep{std::strchr(ports[i], ':')};
  150. if(!sep || ports[i] == sep) continue;
  151. const al::span<const char> portdev{ports[i], sep};
  152. auto check_name = [portdev](const DeviceEntry &entry) -> bool
  153. {
  154. const size_t len{portdev.size()};
  155. return entry.mName.length() == len
  156. && entry.mName.compare(0, len, portdev.data(), len) == 0;
  157. };
  158. if(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend())
  159. continue;
  160. std::string name{portdev.data(), portdev.size()};
  161. list.emplace_back(DeviceEntry{name, name+":"});
  162. const auto &entry = list.back();
  163. TRACE("Got device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
  164. }
  165. /* There are ports but couldn't get device names from them. Add a
  166. * generic entry.
  167. */
  168. if(ports[0] && list.empty())
  169. {
  170. WARN("No device names found in available ports, adding a generic name.\n");
  171. list.emplace_back(DeviceEntry{"JACK", ""});
  172. }
  173. }
  174. if(auto listopt = ConfigValueStr(nullptr, "jack", "custom-devices"))
  175. {
  176. for(size_t strpos{0};strpos < listopt->size();)
  177. {
  178. size_t nextpos{listopt->find(';', strpos)};
  179. size_t seppos{listopt->find('=', strpos)};
  180. if(seppos >= nextpos || seppos == strpos)
  181. {
  182. const std::string entry{listopt->substr(strpos, nextpos-strpos)};
  183. ERR("Invalid device entry: \"%s\"\n", entry.c_str());
  184. if(nextpos != std::string::npos) ++nextpos;
  185. strpos = nextpos;
  186. continue;
  187. }
  188. const al::span<const char> name{listopt->data()+strpos, seppos-strpos};
  189. const al::span<const char> pattern{listopt->data()+(seppos+1),
  190. std::min(nextpos, listopt->size())-(seppos+1)};
  191. /* Check if this custom pattern already exists in the list. */
  192. auto check_pattern = [pattern](const DeviceEntry &entry) -> bool
  193. {
  194. const size_t len{pattern.size()};
  195. return entry.mPattern.length() == len
  196. && entry.mPattern.compare(0, len, pattern.data(), len) == 0;
  197. };
  198. auto itemmatch = std::find_if(list.begin(), list.end(), check_pattern);
  199. if(itemmatch != list.end())
  200. {
  201. /* If so, replace the name with this custom one. */
  202. itemmatch->mName.assign(name.data(), name.size());
  203. TRACE("Customized device name: %s = %s\n", itemmatch->mName.c_str(),
  204. itemmatch->mPattern.c_str());
  205. }
  206. else
  207. {
  208. /* Otherwise, add a new device entry. */
  209. list.emplace_back(DeviceEntry{std::string{name.data(), name.size()},
  210. std::string{pattern.data(), pattern.size()}});
  211. const auto &entry = list.back();
  212. TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
  213. }
  214. if(nextpos != std::string::npos) ++nextpos;
  215. strpos = nextpos;
  216. }
  217. }
  218. if(list.size() > 1)
  219. {
  220. /* Rename entries that have matching names, by appending '#2', '#3',
  221. * etc, as needed.
  222. */
  223. for(auto curitem = list.begin()+1;curitem != list.end();++curitem)
  224. {
  225. auto check_match = [curitem](const DeviceEntry &entry) -> bool
  226. { return entry.mName == curitem->mName; };
  227. if(std::find_if(list.begin(), curitem, check_match) != curitem)
  228. {
  229. std::string name{curitem->mName};
  230. size_t count{1};
  231. auto check_name = [&name](const DeviceEntry &entry) -> bool
  232. { return entry.mName == name; };
  233. do {
  234. name = curitem->mName;
  235. name += " #";
  236. name += std::to_string(++count);
  237. } while(std::find_if(list.begin(), curitem, check_name) != curitem);
  238. curitem->mName = std::move(name);
  239. }
  240. }
  241. }
  242. }
  243. struct JackPlayback final : public BackendBase {
  244. JackPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
  245. ~JackPlayback() override;
  246. int processRt(jack_nframes_t numframes) noexcept;
  247. static int processRtC(jack_nframes_t numframes, void *arg) noexcept
  248. { return static_cast<JackPlayback*>(arg)->processRt(numframes); }
  249. int process(jack_nframes_t numframes) noexcept;
  250. static int processC(jack_nframes_t numframes, void *arg) noexcept
  251. { return static_cast<JackPlayback*>(arg)->process(numframes); }
  252. int mixerProc();
  253. void open(const char *name) override;
  254. bool reset() override;
  255. void start() override;
  256. void stop() override;
  257. ClockLatency getClockLatency() override;
  258. std::string mPortPattern;
  259. jack_client_t *mClient{nullptr};
  260. std::array<jack_port_t*,MAX_OUTPUT_CHANNELS> mPort{};
  261. std::mutex mMutex;
  262. std::atomic<bool> mPlaying{false};
  263. bool mRTMixing{false};
  264. RingBufferPtr mRing;
  265. al::semaphore mSem;
  266. std::atomic<bool> mKillNow{true};
  267. std::thread mThread;
  268. DEF_NEWDEL(JackPlayback)
  269. };
  270. JackPlayback::~JackPlayback()
  271. {
  272. if(!mClient)
  273. return;
  274. auto unregister_port = [this](jack_port_t *port) -> void
  275. { if(port) jack_port_unregister(mClient, port); };
  276. std::for_each(mPort.begin(), mPort.end(), unregister_port);
  277. mPort.fill(nullptr);
  278. jack_client_close(mClient);
  279. mClient = nullptr;
  280. }
  281. int JackPlayback::processRt(jack_nframes_t numframes) noexcept
  282. {
  283. std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out;
  284. size_t numchans{0};
  285. for(auto port : mPort)
  286. {
  287. if(!port || numchans == mDevice->RealOut.Buffer.size())
  288. break;
  289. out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
  290. }
  291. if LIKELY(mPlaying.load(std::memory_order_acquire))
  292. mDevice->renderSamples({out.data(), numchans}, static_cast<uint>(numframes));
  293. else
  294. {
  295. auto clear_buf = [numframes](float *outbuf) -> void
  296. { std::fill_n(outbuf, numframes, 0.0f); };
  297. std::for_each(out.begin(), out.begin()+numchans, clear_buf);
  298. }
  299. return 0;
  300. }
  301. int JackPlayback::process(jack_nframes_t numframes) noexcept
  302. {
  303. std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out;
  304. size_t numchans{0};
  305. for(auto port : mPort)
  306. {
  307. if(!port) break;
  308. out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
  309. }
  310. jack_nframes_t total{0};
  311. if LIKELY(mPlaying.load(std::memory_order_acquire))
  312. {
  313. auto data = mRing->getReadVector();
  314. jack_nframes_t todo{minu(numframes, static_cast<uint>(data.first.len))};
  315. auto write_first = [&data,numchans,todo](float *outbuf) -> float*
  316. {
  317. const float *RESTRICT in = reinterpret_cast<float*>(data.first.buf);
  318. auto deinterlace_input = [&in,numchans]() noexcept -> float
  319. {
  320. float ret{*in};
  321. in += numchans;
  322. return ret;
  323. };
  324. std::generate_n(outbuf, todo, deinterlace_input);
  325. data.first.buf += sizeof(float);
  326. return outbuf + todo;
  327. };
  328. std::transform(out.begin(), out.begin()+numchans, out.begin(), write_first);
  329. total += todo;
  330. todo = minu(numframes-total, static_cast<uint>(data.second.len));
  331. if(todo > 0)
  332. {
  333. auto write_second = [&data,numchans,todo](float *outbuf) -> float*
  334. {
  335. const float *RESTRICT in = reinterpret_cast<float*>(data.second.buf);
  336. auto deinterlace_input = [&in,numchans]() noexcept -> float
  337. {
  338. float ret{*in};
  339. in += numchans;
  340. return ret;
  341. };
  342. std::generate_n(outbuf, todo, deinterlace_input);
  343. data.second.buf += sizeof(float);
  344. return outbuf + todo;
  345. };
  346. std::transform(out.begin(), out.begin()+numchans, out.begin(), write_second);
  347. total += todo;
  348. }
  349. mRing->readAdvance(total);
  350. mSem.post();
  351. }
  352. if(numframes > total)
  353. {
  354. const jack_nframes_t todo{numframes - total};
  355. auto clear_buf = [todo](float *outbuf) -> void { std::fill_n(outbuf, todo, 0.0f); };
  356. std::for_each(out.begin(), out.begin()+numchans, clear_buf);
  357. }
  358. return 0;
  359. }
  360. int JackPlayback::mixerProc()
  361. {
  362. SetRTPriority();
  363. althrd_setname(MIXER_THREAD_NAME);
  364. const size_t frame_step{mDevice->channelsFromFmt()};
  365. while(!mKillNow.load(std::memory_order_acquire)
  366. && mDevice->Connected.load(std::memory_order_acquire))
  367. {
  368. if(mRing->writeSpace() < mDevice->UpdateSize)
  369. {
  370. mSem.wait();
  371. continue;
  372. }
  373. auto data = mRing->getWriteVector();
  374. size_t todo{data.first.len + data.second.len};
  375. todo -= todo%mDevice->UpdateSize;
  376. const auto len1 = static_cast<uint>(minz(data.first.len, todo));
  377. const auto len2 = static_cast<uint>(minz(data.second.len, todo-len1));
  378. std::lock_guard<std::mutex> _{mMutex};
  379. mDevice->renderSamples(data.first.buf, len1, frame_step);
  380. if(len2 > 0)
  381. mDevice->renderSamples(data.second.buf, len2, frame_step);
  382. mRing->writeAdvance(todo);
  383. }
  384. return 0;
  385. }
  386. void JackPlayback::open(const char *name)
  387. {
  388. if(!mClient)
  389. {
  390. const PathNamePair &binname = GetProcBinary();
  391. const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
  392. jack_status_t status;
  393. mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
  394. if(mClient == nullptr)
  395. throw al::backend_exception{al::backend_error::DeviceError,
  396. "Failed to open client connection: 0x%02x", status};
  397. if((status&JackServerStarted))
  398. TRACE("JACK server started\n");
  399. if((status&JackNameNotUnique))
  400. {
  401. client_name = jack_get_client_name(mClient);
  402. TRACE("Client name not unique, got '%s' instead\n", client_name);
  403. }
  404. }
  405. if(PlaybackList.empty())
  406. EnumerateDevices(mClient, PlaybackList);
  407. if(!name && !PlaybackList.empty())
  408. {
  409. name = PlaybackList[0].mName.c_str();
  410. mPortPattern = PlaybackList[0].mPattern;
  411. }
  412. else
  413. {
  414. auto check_name = [name](const DeviceEntry &entry) -> bool
  415. { return entry.mName == name; };
  416. auto iter = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), check_name);
  417. if(iter == PlaybackList.cend())
  418. throw al::backend_exception{al::backend_error::NoDevice,
  419. "Device name \"%s\" not found", name?name:""};
  420. mPortPattern = iter->mPattern;
  421. }
  422. mRTMixing = GetConfigValueBool(name, "jack", "rt-mix", 1);
  423. jack_set_process_callback(mClient,
  424. mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this);
  425. mDevice->DeviceName = name;
  426. }
  427. bool JackPlayback::reset()
  428. {
  429. auto unregister_port = [this](jack_port_t *port) -> void
  430. { if(port) jack_port_unregister(mClient, port); };
  431. std::for_each(mPort.begin(), mPort.end(), unregister_port);
  432. mPort.fill(nullptr);
  433. /* Ignore the requested buffer metrics and just keep one JACK-sized buffer
  434. * ready for when requested.
  435. */
  436. mDevice->Frequency = jack_get_sample_rate(mClient);
  437. mDevice->UpdateSize = jack_get_buffer_size(mClient);
  438. if(mRTMixing)
  439. {
  440. /* Assume only two periods when directly mixing. Should try to query
  441. * the total port latency when connected.
  442. */
  443. mDevice->BufferSize = mDevice->UpdateSize * 2;
  444. }
  445. else
  446. {
  447. const char *devname{mDevice->DeviceName.c_str()};
  448. uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
  449. bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
  450. mDevice->BufferSize = bufsize + mDevice->UpdateSize;
  451. }
  452. /* Force 32-bit float output. */
  453. mDevice->FmtType = DevFmtFloat;
  454. int port_num{0};
  455. auto ports_end = mPort.begin() + mDevice->channelsFromFmt();
  456. auto bad_port = mPort.begin();
  457. while(bad_port != ports_end)
  458. {
  459. std::string name{"channel_" + std::to_string(++port_num)};
  460. *bad_port = jack_port_register(mClient, name.c_str(), JackDefaultAudioType,
  461. JackPortIsOutput | JackPortIsTerminal, 0);
  462. if(!*bad_port) break;
  463. ++bad_port;
  464. }
  465. if(bad_port != ports_end)
  466. {
  467. ERR("Failed to register enough JACK ports for %s output\n",
  468. DevFmtChannelsString(mDevice->FmtChans));
  469. if(bad_port == mPort.begin()) return false;
  470. if(bad_port == mPort.begin()+1)
  471. mDevice->FmtChans = DevFmtMono;
  472. else
  473. {
  474. ports_end = mPort.begin()+2;
  475. while(bad_port != ports_end)
  476. {
  477. jack_port_unregister(mClient, *(--bad_port));
  478. *bad_port = nullptr;
  479. }
  480. mDevice->FmtChans = DevFmtStereo;
  481. }
  482. }
  483. setDefaultChannelOrder();
  484. return true;
  485. }
  486. void JackPlayback::start()
  487. {
  488. if(jack_activate(mClient))
  489. throw al::backend_exception{al::backend_error::DeviceError, "Failed to activate client"};
  490. const char *devname{mDevice->DeviceName.c_str()};
  491. if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true))
  492. {
  493. JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JackDefaultAudioType,
  494. JackPortIsInput)};
  495. if(!pnames)
  496. {
  497. jack_deactivate(mClient);
  498. throw al::backend_exception{al::backend_error::DeviceError, "No playback ports found"};
  499. }
  500. for(size_t i{0};i < al::size(mPort) && mPort[i];++i)
  501. {
  502. if(!pnames[i])
  503. {
  504. ERR("No physical playback port for \"%s\"\n", jack_port_name(mPort[i]));
  505. break;
  506. }
  507. if(jack_connect(mClient, jack_port_name(mPort[i]), pnames[i]))
  508. ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(mPort[i]),
  509. pnames[i]);
  510. }
  511. }
  512. /* Reconfigure buffer metrics in case the server changed it since the reset
  513. * (it won't change again after jack_activate), then allocate the ring
  514. * buffer with the appropriate size.
  515. */
  516. mDevice->Frequency = jack_get_sample_rate(mClient);
  517. mDevice->UpdateSize = jack_get_buffer_size(mClient);
  518. mDevice->BufferSize = mDevice->UpdateSize * 2;
  519. mRing = nullptr;
  520. if(mRTMixing)
  521. mPlaying.store(true, std::memory_order_release);
  522. else
  523. {
  524. uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
  525. bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
  526. mDevice->BufferSize = bufsize + mDevice->UpdateSize;
  527. mRing = RingBuffer::Create(bufsize, mDevice->frameSizeFromFmt(), true);
  528. try {
  529. mPlaying.store(true, std::memory_order_release);
  530. mKillNow.store(false, std::memory_order_release);
  531. mThread = std::thread{std::mem_fn(&JackPlayback::mixerProc), this};
  532. }
  533. catch(std::exception& e) {
  534. jack_deactivate(mClient);
  535. mPlaying.store(false, std::memory_order_release);
  536. throw al::backend_exception{al::backend_error::DeviceError,
  537. "Failed to start mixing thread: %s", e.what()};
  538. }
  539. }
  540. }
  541. void JackPlayback::stop()
  542. {
  543. if(mPlaying.load(std::memory_order_acquire))
  544. {
  545. mKillNow.store(true, std::memory_order_release);
  546. if(mThread.joinable())
  547. {
  548. mSem.post();
  549. mThread.join();
  550. }
  551. jack_deactivate(mClient);
  552. mPlaying.store(false, std::memory_order_release);
  553. }
  554. }
  555. ClockLatency JackPlayback::getClockLatency()
  556. {
  557. ClockLatency ret;
  558. std::lock_guard<std::mutex> _{mMutex};
  559. ret.ClockTime = GetDeviceClockTime(mDevice);
  560. ret.Latency = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->UpdateSize};
  561. ret.Latency /= mDevice->Frequency;
  562. return ret;
  563. }
  564. void jack_msg_handler(const char *message)
  565. {
  566. WARN("%s\n", message);
  567. }
  568. } // namespace
  569. bool JackBackendFactory::init()
  570. {
  571. if(!jack_load())
  572. return false;
  573. if(!GetConfigValueBool(nullptr, "jack", "spawn-server", 0))
  574. ClientOptions = static_cast<jack_options_t>(ClientOptions | JackNoStartServer);
  575. const PathNamePair &binname = GetProcBinary();
  576. const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
  577. void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr};
  578. jack_set_error_function(jack_msg_handler);
  579. jack_status_t status;
  580. jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)};
  581. jack_set_error_function(old_error_cb);
  582. if(!client)
  583. {
  584. WARN("jack_client_open() failed, 0x%02x\n", status);
  585. if((status&JackServerFailed) && !(ClientOptions&JackNoStartServer))
  586. ERR("Unable to connect to JACK server\n");
  587. return false;
  588. }
  589. jack_client_close(client);
  590. return true;
  591. }
  592. bool JackBackendFactory::querySupport(BackendType type)
  593. { return (type == BackendType::Playback); }
  594. std::string JackBackendFactory::probe(BackendType type)
  595. {
  596. std::string outnames;
  597. auto append_name = [&outnames](const DeviceEntry &entry) -> void
  598. {
  599. /* Includes null char. */
  600. outnames.append(entry.mName.c_str(), entry.mName.length()+1);
  601. };
  602. const PathNamePair &binname = GetProcBinary();
  603. const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
  604. jack_status_t status;
  605. switch(type)
  606. {
  607. case BackendType::Playback:
  608. if(jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)})
  609. {
  610. EnumerateDevices(client, PlaybackList);
  611. jack_client_close(client);
  612. }
  613. else
  614. WARN("jack_client_open() failed, 0x%02x\n", status);
  615. std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
  616. break;
  617. case BackendType::Capture:
  618. break;
  619. }
  620. return outnames;
  621. }
  622. BackendPtr JackBackendFactory::createBackend(DeviceBase *device, BackendType type)
  623. {
  624. if(type == BackendType::Playback)
  625. return BackendPtr{new JackPlayback{device}};
  626. return nullptr;
  627. }
  628. BackendFactory &JackBackendFactory::getFactory()
  629. {
  630. static JackBackendFactory factory{};
  631. return factory;
  632. }