Audio plugin host https://kx.studio/carla
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.

688 lines
23KB

  1. /*
  2. * Carla JACK API for external applications
  3. * Copyright (C) 2016-2017 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. // need to include this first
  18. #include "libjack.hpp"
  19. using juce::File;
  20. using juce::MemoryBlock;
  21. using juce::String;
  22. using juce::Time;
  23. using juce::Thread;
  24. CARLA_BACKEND_START_NAMESPACE
  25. // --------------------------------------------------------------------------------------------------------------------
  26. class CarlaJackAppClient : public juce::Thread
  27. {
  28. public:
  29. JackServerState fServer;
  30. LinkedList<JackClientState*> fClients;
  31. CarlaJackAppClient()
  32. : Thread("CarlaJackAppClient"),
  33. fServer(),
  34. fIsValid(false),
  35. fIsOffline(false),
  36. fLastPingTime(-1)
  37. {
  38. carla_debug("CarlaJackAppClient::CarlaJackAppClient()");
  39. const char* const shmIds(std::getenv("CARLA_SHM_IDS"));
  40. CARLA_SAFE_ASSERT_RETURN(shmIds != nullptr && std::strlen(shmIds) == 6*4,);
  41. const char* const libjackSetup(std::getenv("CARLA_LIBJACK_SETUP"));
  42. CARLA_SAFE_ASSERT_RETURN(libjackSetup != nullptr && std::strlen(libjackSetup) == 5,);
  43. for (int i=4; --i >= 0;) {
  44. CARLA_SAFE_ASSERT_RETURN(libjackSetup[i] >= '0' && libjackSetup[i] <= '0'+64,);
  45. }
  46. CARLA_SAFE_ASSERT_RETURN(libjackSetup[4] >= '0' && libjackSetup[4] < '0'+0x4f,);
  47. std::memcpy(fBaseNameAudioPool, shmIds+6*0, 6);
  48. std::memcpy(fBaseNameRtClientControl, shmIds+6*1, 6);
  49. std::memcpy(fBaseNameNonRtClientControl, shmIds+6*2, 6);
  50. std::memcpy(fBaseNameNonRtServerControl, shmIds+6*3, 6);
  51. fBaseNameAudioPool[6] = '\0';
  52. fBaseNameRtClientControl[6] = '\0';
  53. fBaseNameNonRtClientControl[6] = '\0';
  54. fBaseNameNonRtServerControl[6] = '\0';
  55. fNumPorts.audioIns = libjackSetup[0] - '0';
  56. fNumPorts.audioOuts = libjackSetup[1] - '0';
  57. fNumPorts.midiIns = libjackSetup[2] - '0';
  58. fNumPorts.midiOuts = libjackSetup[3] - '0';
  59. startThread(10);
  60. }
  61. ~CarlaJackAppClient() noexcept override
  62. {
  63. carla_debug("CarlaJackAppClient::~CarlaJackAppClient()");
  64. fLastPingTime = -1;
  65. stopThread(5000);
  66. clear();
  67. const CarlaMutexLocker cms(fRealtimeThreadMutex);
  68. for (LinkedList<JackClientState*>::Itenerator it = fClients.begin2(); it.valid(); it.next())
  69. {
  70. JackClientState* const jclient(it.getValue(nullptr));
  71. CARLA_SAFE_ASSERT_CONTINUE(jclient != nullptr);
  72. delete jclient;
  73. }
  74. fClients.clear();
  75. }
  76. JackClientState* addClient(const char* const name)
  77. {
  78. JackClientState* const jclient(new JackClientState(fServer, name));
  79. const CarlaMutexLocker cms(fRealtimeThreadMutex);
  80. fClients.append(jclient);
  81. return jclient;
  82. }
  83. bool removeClient(JackClientState* const jclient)
  84. {
  85. {
  86. const CarlaMutexLocker cms(fRealtimeThreadMutex);
  87. CARLA_SAFE_ASSERT_RETURN(fClients.removeOne(jclient), false);
  88. }
  89. delete jclient;
  90. return true;
  91. }
  92. void clear() noexcept;
  93. bool isValid() const noexcept;
  94. void handleNonRtData();
  95. // -------------------------------------------------------------------
  96. protected:
  97. void run() override;
  98. private:
  99. BridgeAudioPool fShmAudioPool;
  100. BridgeRtClientControl fShmRtClientControl;
  101. BridgeNonRtClientControl fShmNonRtClientControl;
  102. BridgeNonRtServerControl fShmNonRtServerControl;
  103. char fBaseNameAudioPool[6+1];
  104. char fBaseNameRtClientControl[6+1];
  105. char fBaseNameNonRtClientControl[6+1];
  106. char fBaseNameNonRtServerControl[6+1];
  107. bool fIsValid;
  108. bool fIsOffline;
  109. int64_t fLastPingTime;
  110. struct NumPorts {
  111. uint32_t audioIns;
  112. uint32_t audioOuts;
  113. uint32_t midiIns;
  114. uint32_t midiOuts;
  115. NumPorts()
  116. : audioIns(0),
  117. audioOuts(0),
  118. midiIns(0),
  119. midiOuts(0) {}
  120. } fNumPorts;
  121. CarlaMutex fRealtimeThreadMutex;
  122. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaJackAppClient)
  123. };
  124. // --------------------------------------------------------------------------------------------------------------------
  125. void CarlaJackAppClient::clear() noexcept
  126. {
  127. fShmAudioPool.clear();
  128. fShmRtClientControl.clear();
  129. fShmNonRtClientControl.clear();
  130. fShmNonRtServerControl.clear();
  131. }
  132. bool CarlaJackAppClient::isValid() const noexcept
  133. {
  134. return fIsValid;
  135. }
  136. void CarlaJackAppClient::handleNonRtData()
  137. {
  138. for (; fShmNonRtClientControl.isDataAvailableForReading();)
  139. {
  140. const PluginBridgeNonRtClientOpcode opcode(fShmNonRtClientControl.readOpcode());
  141. // #ifdef DEBUG
  142. if (opcode != kPluginBridgeNonRtClientPing)
  143. {
  144. static int shownNull = 0;
  145. if (opcode == kPluginBridgeNonRtClientNull)
  146. {
  147. if (shownNull > 5)
  148. continue;
  149. ++shownNull;
  150. }
  151. carla_stdout("CarlaJackAppClient::handleNonRtData() - got opcode: %s", PluginBridgeNonRtClientOpcode2str(opcode));
  152. }
  153. // #endif
  154. if (opcode != kPluginBridgeNonRtClientNull && opcode != kPluginBridgeNonRtClientPingOnOff && fLastPingTime > 0)
  155. fLastPingTime = Time::currentTimeMillis();
  156. switch (opcode)
  157. {
  158. case kPluginBridgeNonRtClientNull:
  159. break;
  160. case kPluginBridgeNonRtClientPing: {
  161. const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex);
  162. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerPong);
  163. fShmNonRtServerControl.commitWrite();
  164. } break;
  165. case kPluginBridgeNonRtClientPingOnOff: {
  166. const uint32_t onOff(fShmNonRtClientControl.readBool());
  167. fLastPingTime = onOff ? Time::currentTimeMillis() : -1;
  168. } break;
  169. case kPluginBridgeNonRtClientActivate:
  170. case kPluginBridgeNonRtClientDeactivate:
  171. break;
  172. case kPluginBridgeNonRtClientSetBufferSize:
  173. fShmNonRtClientControl.readUInt();
  174. //bufferSizeChanged();
  175. break;
  176. case kPluginBridgeNonRtClientSetSampleRate:
  177. fShmNonRtClientControl.readDouble();
  178. //sampleRateChanged();
  179. break;
  180. case kPluginBridgeNonRtClientSetOffline:
  181. fIsOffline = true;
  182. //offlineModeChanged(true);
  183. break;
  184. case kPluginBridgeNonRtClientSetOnline:
  185. fIsOffline = false;
  186. //offlineModeChanged(false);
  187. break;
  188. case kPluginBridgeNonRtClientSetParameterValue:
  189. case kPluginBridgeNonRtClientSetParameterMidiChannel:
  190. case kPluginBridgeNonRtClientSetParameterMidiCC:
  191. case kPluginBridgeNonRtClientSetProgram:
  192. case kPluginBridgeNonRtClientSetMidiProgram:
  193. case kPluginBridgeNonRtClientSetCustomData:
  194. case kPluginBridgeNonRtClientSetChunkDataFile:
  195. break;
  196. case kPluginBridgeNonRtClientSetOption:
  197. fShmNonRtClientControl.readUInt();
  198. fShmNonRtClientControl.readBool();
  199. break;
  200. case kPluginBridgeNonRtClientSetCtrlChannel:
  201. fShmNonRtClientControl.readShort();
  202. break;
  203. case kPluginBridgeNonRtClientPrepareForSave:
  204. {
  205. const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex);
  206. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerSaved);
  207. fShmNonRtServerControl.commitWrite();
  208. }
  209. break;
  210. case kPluginBridgeNonRtClientShowUI:
  211. case kPluginBridgeNonRtClientHideUI:
  212. case kPluginBridgeNonRtClientUiParameterChange:
  213. case kPluginBridgeNonRtClientUiProgramChange:
  214. case kPluginBridgeNonRtClientUiMidiProgramChange:
  215. case kPluginBridgeNonRtClientUiNoteOn:
  216. case kPluginBridgeNonRtClientUiNoteOff:
  217. break;
  218. case kPluginBridgeNonRtClientQuit:
  219. signalThreadShouldExit();
  220. break;
  221. }
  222. }
  223. }
  224. void CarlaJackAppClient::run()
  225. {
  226. carla_stderr("CarlaJackAppClient run START");
  227. if (! fShmAudioPool.attachClient(fBaseNameAudioPool))
  228. {
  229. carla_stderr("Failed to attach to audio pool shared memory");
  230. return;
  231. }
  232. if (! fShmRtClientControl.attachClient(fBaseNameRtClientControl))
  233. {
  234. clear();
  235. carla_stderr("Failed to attach to rt client control shared memory");
  236. return;
  237. }
  238. if (! fShmRtClientControl.mapData())
  239. {
  240. clear();
  241. carla_stderr("Failed to map rt client control shared memory");
  242. return;
  243. }
  244. if (! fShmNonRtClientControl.attachClient(fBaseNameNonRtClientControl))
  245. {
  246. clear();
  247. carla_stderr("Failed to attach to non-rt client control shared memory");
  248. return;
  249. }
  250. if (! fShmNonRtClientControl.mapData())
  251. {
  252. clear();
  253. carla_stderr("Failed to map non-rt control client shared memory");
  254. return;
  255. }
  256. if (! fShmNonRtServerControl.attachClient(fBaseNameNonRtServerControl))
  257. {
  258. clear();
  259. carla_stderr("Failed to attach to non-rt server control shared memory");
  260. return;
  261. }
  262. if (! fShmNonRtServerControl.mapData())
  263. {
  264. clear();
  265. carla_stderr("Failed to map non-rt control server shared memory");
  266. return;
  267. }
  268. PluginBridgeNonRtClientOpcode opcode;
  269. opcode = fShmNonRtClientControl.readOpcode();
  270. CARLA_SAFE_ASSERT_INT(opcode == kPluginBridgeNonRtClientNull, opcode);
  271. const uint32_t shmRtClientDataSize = fShmNonRtClientControl.readUInt();
  272. CARLA_SAFE_ASSERT_INT2(shmRtClientDataSize == sizeof(BridgeRtClientData), shmRtClientDataSize, sizeof(BridgeRtClientData));
  273. const uint32_t shmNonRtClientDataSize = fShmNonRtClientControl.readUInt();
  274. CARLA_SAFE_ASSERT_INT2(shmNonRtClientDataSize == sizeof(BridgeNonRtClientData), shmNonRtClientDataSize, sizeof(BridgeNonRtClientData));
  275. const uint32_t shmNonRtServerDataSize = fShmNonRtClientControl.readUInt();
  276. CARLA_SAFE_ASSERT_INT2(shmNonRtServerDataSize == sizeof(BridgeNonRtServerData), shmNonRtServerDataSize, sizeof(BridgeNonRtServerData));
  277. if (shmRtClientDataSize != sizeof(BridgeRtClientData) || shmNonRtClientDataSize != sizeof(BridgeNonRtClientData) || shmNonRtServerDataSize != sizeof(BridgeNonRtServerData))
  278. {
  279. carla_stderr2("CarlaJackAppClient: data size mismatch");
  280. return;
  281. }
  282. opcode = fShmNonRtClientControl.readOpcode();
  283. CARLA_SAFE_ASSERT_INT(opcode == kPluginBridgeNonRtClientSetBufferSize, opcode);
  284. fServer.bufferSize = fShmNonRtClientControl.readUInt();
  285. opcode = fShmNonRtClientControl.readOpcode();
  286. CARLA_SAFE_ASSERT_INT(opcode == kPluginBridgeNonRtClientSetSampleRate, opcode);
  287. fServer.sampleRate = fShmNonRtClientControl.readDouble();
  288. if (fServer.bufferSize == 0 || carla_isZero(fServer.sampleRate))
  289. {
  290. carla_stderr2("CarlaJackAppClient: invalid empty state");
  291. return;
  292. }
  293. // tell backend we're live
  294. {
  295. const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex);
  296. fLastPingTime = Time::currentTimeMillis();
  297. CARLA_SAFE_ASSERT(fLastPingTime > 0);
  298. // ready!
  299. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerReady);
  300. fShmNonRtServerControl.commitWrite();
  301. fShmNonRtServerControl.waitIfDataIsReachingLimit();
  302. }
  303. fIsValid = true;
  304. fLastPingTime = Time::currentTimeMillis();
  305. carla_stdout("Carla Jack Client Ready!");
  306. #ifdef __SSE2_MATH__
  307. // Set FTZ and DAZ flags
  308. _mm_setcsr(_mm_getcsr() | 0x8040);
  309. #endif
  310. bool quitReceived = false;
  311. for (; ! quitReceived && ! threadShouldExit();)
  312. {
  313. handleNonRtData();
  314. const BridgeRtClientControl::WaitHelper helper(fShmRtClientControl);
  315. if (! helper.ok)
  316. continue;
  317. for (; fShmRtClientControl.isDataAvailableForReading();)
  318. {
  319. const PluginBridgeRtClientOpcode opcode(fShmRtClientControl.readOpcode());
  320. //#ifdef DEBUG
  321. if (opcode != kPluginBridgeRtClientProcess && opcode != kPluginBridgeRtClientMidiEvent)
  322. {
  323. carla_stdout("CarlaJackAppClientRtThread::run() - got opcode: %s", PluginBridgeRtClientOpcode2str(opcode));
  324. }
  325. //#endif
  326. switch (opcode)
  327. {
  328. case kPluginBridgeRtClientNull:
  329. break;
  330. case kPluginBridgeRtClientSetAudioPool: {
  331. if (fShmAudioPool.data != nullptr)
  332. {
  333. jackbridge_shm_unmap(fShmAudioPool.shm, fShmAudioPool.data);
  334. fShmAudioPool.data = nullptr;
  335. }
  336. const uint64_t poolSize(fShmRtClientControl.readULong());
  337. CARLA_SAFE_ASSERT_BREAK(poolSize > 0);
  338. fShmAudioPool.data = (float*)jackbridge_shm_map(fShmAudioPool.shm, static_cast<size_t>(poolSize));
  339. break;
  340. }
  341. case kPluginBridgeRtClientControlEventParameter:
  342. case kPluginBridgeRtClientControlEventMidiBank:
  343. case kPluginBridgeRtClientControlEventMidiProgram:
  344. case kPluginBridgeRtClientControlEventAllSoundOff:
  345. case kPluginBridgeRtClientControlEventAllNotesOff:
  346. case kPluginBridgeRtClientMidiEvent:
  347. break;
  348. case kPluginBridgeRtClientProcess: {
  349. CARLA_SAFE_ASSERT_BREAK(fShmAudioPool.data != nullptr);
  350. const CarlaMutexTryLocker cmtl(fRealtimeThreadMutex);
  351. if (cmtl.wasLocked())
  352. {
  353. float* fdata = fShmAudioPool.data;
  354. if (! fClients.isEmpty())
  355. {
  356. // tranport for all clients
  357. const BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo);
  358. fServer.playing = bridgeTimeInfo.playing;
  359. fServer.position.frame = bridgeTimeInfo.frame;
  360. fServer.position.usecs = bridgeTimeInfo.usecs;
  361. if (bridgeTimeInfo.valid & 0x1 /* kValidBBT */)
  362. {
  363. fServer.position.valid = JackPositionBBT;
  364. fServer.position.bar = bridgeTimeInfo.bar;
  365. fServer.position.beat = bridgeTimeInfo.beat;
  366. fServer.position.tick = bridgeTimeInfo.tick;
  367. fServer.position.beats_per_bar = bridgeTimeInfo.beatsPerBar;
  368. fServer.position.beat_type = bridgeTimeInfo.beatType;
  369. fServer.position.ticks_per_beat = bridgeTimeInfo.ticksPerBeat;
  370. fServer.position.beats_per_minute = bridgeTimeInfo.beatsPerMinute;
  371. fServer.position.bar_start_tick = bridgeTimeInfo.barStartTick;
  372. }
  373. else
  374. {
  375. fServer.position.valid = static_cast<jack_position_bits_t>(0);
  376. }
  377. for (LinkedList<JackClientState*>::Itenerator it = fClients.begin2(); it.valid(); it.next())
  378. {
  379. JackClientState* const jclient(it.getValue(nullptr));
  380. CARLA_SAFE_ASSERT_CONTINUE(jclient != nullptr);
  381. const CarlaMutexTryLocker cmtl2(jclient->mutex);
  382. if (cmtl2.wasNotLocked() || jclient->processCb == nullptr || ! jclient->activated)
  383. {
  384. if (fNumPorts.audioIns > 0)
  385. fdata += fServer.bufferSize*fNumPorts.audioIns;
  386. if (fNumPorts.audioOuts > 0)
  387. carla_zeroFloats(fdata, fServer.bufferSize*fNumPorts.audioOuts);
  388. if (jclient->deactivated)
  389. {
  390. fShmRtClientControl.data->procFlags = 1;
  391. }
  392. }
  393. else
  394. {
  395. uint32_t i;
  396. i = 0;
  397. for (LinkedList<JackPortState*>::Itenerator it = jclient->audioIns.begin2(); it.valid(); it.next())
  398. {
  399. CARLA_SAFE_ASSERT_BREAK(i++ < fNumPorts.audioIns);
  400. if (JackPortState* const jport = it.getValue(nullptr))
  401. jport->buffer = fdata;
  402. fdata += fServer.bufferSize;
  403. }
  404. for (; i++ < fNumPorts.audioIns;)
  405. fdata += fServer.bufferSize;
  406. i = 0;
  407. for (LinkedList<JackPortState*>::Itenerator it = jclient->audioOuts.begin2(); it.valid(); it.next())
  408. {
  409. CARLA_SAFE_ASSERT_BREAK(i++ < fNumPorts.audioOuts);
  410. if (JackPortState* const jport = it.getValue(nullptr))
  411. jport->buffer = fdata;
  412. fdata += fServer.bufferSize;
  413. }
  414. for (; i++ < fNumPorts.audioOuts;)
  415. {
  416. carla_zeroFloats(fdata, fServer.bufferSize);
  417. fdata += fServer.bufferSize;
  418. }
  419. jclient->processCb(fServer.bufferSize, jclient->processCbPtr);
  420. }
  421. }
  422. }
  423. else
  424. {
  425. if (fNumPorts.audioIns > 0)
  426. fdata += fServer.bufferSize*fNumPorts.audioIns;
  427. if (fNumPorts.audioOuts > 0)
  428. carla_zeroFloats(fdata, fServer.bufferSize*fNumPorts.audioOuts);
  429. }
  430. }
  431. else
  432. {
  433. carla_stderr2("CarlaJackAppClient: fRealtimeThreadMutex tryLock failed");
  434. }
  435. carla_zeroBytes(fShmRtClientControl.data->midiOut, kBridgeRtClientDataMidiOutSize);
  436. break;
  437. }
  438. case kPluginBridgeRtClientQuit:
  439. quitReceived = true;
  440. signalThreadShouldExit();
  441. break;
  442. }
  443. }
  444. }
  445. //callback(ENGINE_CALLBACK_ENGINE_STOPPED, 0, 0, 0, 0.0f, nullptr);
  446. if (quitReceived)
  447. {
  448. carla_stderr("CarlaJackAppClient run END - quit by carla");
  449. ::kill(::getpid(), SIGTERM);
  450. }
  451. else
  452. {
  453. const char* const message("Plugin bridge error, process thread has stopped");
  454. const std::size_t messageSize(std::strlen(message));
  455. bool activated;
  456. {
  457. const CarlaMutexLocker cms(fRealtimeThreadMutex);
  458. if (fClients.isEmpty())
  459. {
  460. activated = false;
  461. }
  462. else if (JackClientState* const jclient = fClients.getLast(nullptr))
  463. {
  464. const CarlaMutexLocker cms(jclient->mutex);
  465. activated = jclient->activated;
  466. }
  467. else
  468. {
  469. activated = true;
  470. }
  471. }
  472. if (activated)
  473. {
  474. carla_stderr("CarlaJackAppClient run END - quit error");
  475. const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex);
  476. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerError);
  477. fShmNonRtServerControl.writeUInt(messageSize);
  478. fShmNonRtServerControl.writeCustomData(message, messageSize);
  479. fShmNonRtServerControl.commitWrite();
  480. }
  481. else
  482. {
  483. carla_stderr("CarlaJackAppClient run END - quit itself");
  484. const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex);
  485. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerUiClosed);
  486. fShmNonRtServerControl.commitWrite();
  487. }
  488. /*
  489. if (activated)
  490. {
  491. // TODO infoShutdown
  492. if (fClient.shutdownCb != nullptr)
  493. fClient.shutdownCb(fClient.shutdownCbPtr);
  494. }
  495. */
  496. }
  497. }
  498. // --------------------------------------------------------------------------------------------------------------------
  499. static CarlaJackAppClient gClient;
  500. CARLA_EXPORT
  501. jack_client_t* jack_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...)
  502. {
  503. if (status != nullptr)
  504. *status = JackNameNotUnique;
  505. if (options & JackUseExactName)
  506. return nullptr;
  507. if (JackClientState* const client = gClient.addClient(client_name))
  508. return (jack_client_t*)client;
  509. if (status != nullptr)
  510. *status = JackServerError;
  511. return nullptr;
  512. }
  513. CARLA_EXPORT
  514. jack_client_t* jack_client_new(const char* client_name)
  515. {
  516. return jack_client_open(client_name, JackNullOption, nullptr);
  517. }
  518. CARLA_EXPORT
  519. int jack_client_close(jack_client_t* client)
  520. {
  521. JackClientState* const jclient = (JackClientState*)client;
  522. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1);
  523. gClient.removeClient(jclient);
  524. return 0;
  525. }
  526. CARLA_EXPORT
  527. pthread_t jack_client_thread_id(jack_client_t* client)
  528. {
  529. JackClientState* const jclient = (JackClientState*)client;
  530. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0);
  531. CarlaJackAppClient* const jackAppPtr = jclient->server.jackAppPtr;
  532. CARLA_SAFE_ASSERT_RETURN(jackAppPtr != nullptr, 0);
  533. return (pthread_t)jackAppPtr->getThreadId();
  534. }
  535. CARLA_BACKEND_END_NAMESPACE
  536. // --------------------------------------------------------------------------------------------------------------------
  537. #include "jackbridge/JackBridge2.cpp"
  538. #include "CarlaBridgeUtils.cpp"
  539. // --------------------------------------------------------------------------------------------------------------------
  540. // TODO
  541. CARLA_BACKEND_USE_NAMESPACE
  542. CARLA_EXPORT
  543. int jack_client_real_time_priority(jack_client_t*)
  544. {
  545. carla_stdout("CarlaJackAppClient :: %s", __FUNCTION__);
  546. return -1;
  547. }
  548. // --------------------------------------------------------------------------------------------------------------------