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.

1321 lines
46KB

  1. /*
  2. * Carla JACK API for external applications
  3. * Copyright (C) 2016-2019 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. #include "libjack.hpp"
  18. #include "CarlaThread.hpp"
  19. #include <signal.h>
  20. #include <sys/prctl.h>
  21. #include <sys/time.h>
  22. // ---------------------------------------------------------------------------------------------------------------------
  23. typedef int (*CarlaInterposedCallback)(int, void*);
  24. CARLA_EXPORT
  25. int jack_carla_interposed_action(uint, uint, void*)
  26. {
  27. carla_stderr2("Non-export jack_carla_interposed_action called, this should not happen!!");
  28. return 1337;
  29. }
  30. // ---------------------------------------------------------------------------------------------------------------------
  31. CARLA_BACKEND_START_NAMESPACE
  32. // ---------------------------------------------------------------------------------------------------------------------
  33. static int64_t getCurrentTimeMilliseconds() noexcept
  34. {
  35. struct timeval tv;
  36. gettimeofday (&tv, nullptr);
  37. return ((int64_t) tv.tv_sec) * 1000 + tv.tv_usec / 1000;
  38. }
  39. static int carla_interposed_callback(int, void*);
  40. // ---------------------------------------------------------------------------------------------------------------------
  41. class CarlaJackRealtimeThread : public CarlaThread
  42. {
  43. public:
  44. struct Callback {
  45. Callback() {}
  46. virtual ~Callback() {};
  47. virtual void runRealtimeThread() = 0;
  48. };
  49. CarlaJackRealtimeThread(Callback* const callback)
  50. : CarlaThread("CarlaJackRealtimeThread"),
  51. fCallback(callback) {}
  52. protected:
  53. void run() override
  54. {
  55. fCallback->runRealtimeThread();
  56. }
  57. private:
  58. Callback* const fCallback;
  59. CARLA_DECLARE_NON_COPY_CLASS(CarlaJackRealtimeThread)
  60. };
  61. // --------------------------------------------------------------------------------------------------------------------
  62. class CarlaJackNonRealtimeThread : public CarlaThread
  63. {
  64. public:
  65. struct Callback {
  66. Callback() {}
  67. virtual ~Callback() {};
  68. virtual void runNonRealtimeThread() = 0;
  69. };
  70. CarlaJackNonRealtimeThread(Callback* const callback)
  71. : CarlaThread("CarlaJackNonRealtimeThread"),
  72. fCallback(callback) {}
  73. protected:
  74. void run() override
  75. {
  76. fCallback->runNonRealtimeThread();
  77. }
  78. private:
  79. Callback* const fCallback;
  80. CARLA_DECLARE_NON_COPY_CLASS(CarlaJackNonRealtimeThread)
  81. };
  82. // ---------------------------------------------------------------------------------------------------------------------
  83. class CarlaJackAppClient : public CarlaJackRealtimeThread::Callback,
  84. public CarlaJackNonRealtimeThread::Callback
  85. {
  86. public:
  87. JackServerState fServer;
  88. LinkedList<JackClientState*> fClients;
  89. CarlaJackAppClient()
  90. : fServer(this),
  91. fClients(),
  92. fShmAudioPool(),
  93. fShmRtClientControl(),
  94. fShmNonRtClientControl(),
  95. fShmNonRtServerControl(),
  96. fAudioPoolCopy(nullptr),
  97. fAudioTmpBuf(nullptr),
  98. fDummyMidiInBuffer(true),
  99. fDummyMidiOutBuffer(false),
  100. fMidiInBuffers(nullptr),
  101. fMidiOutBuffers(nullptr),
  102. fIsOffline(false),
  103. fIsReady(false),
  104. fLastPingTime(-1),
  105. fSessionManager(0),
  106. fSetupHints(0),
  107. fRealtimeThread(this),
  108. fNonRealtimeThread(this),
  109. fRealtimeThreadMutex()
  110. {
  111. carla_debug("CarlaJackAppClient::CarlaJackAppClient()");
  112. const char* const shmIds(std::getenv("CARLA_SHM_IDS"));
  113. CARLA_SAFE_ASSERT_INT2_RETURN(shmIds != nullptr && std::strlen(shmIds) == 6*4, std::strlen(shmIds), 6*4,);
  114. const char* const libjackSetup(std::getenv("CARLA_LIBJACK_SETUP"));
  115. CARLA_SAFE_ASSERT_RETURN(libjackSetup != nullptr && std::strlen(libjackSetup) >= 6,);
  116. // make sure we don't get loaded again
  117. carla_unsetenv("CARLA_SHM_IDS");
  118. // kill ourselves if main carla dies
  119. carla_terminateProcessOnParentExit(true);
  120. for (int i=4; --i >= 0;) {
  121. CARLA_SAFE_ASSERT_RETURN(libjackSetup[i] >= '0' && libjackSetup[i] <= '0'+64,);
  122. }
  123. CARLA_SAFE_ASSERT_RETURN(libjackSetup[4] >= '0' && libjackSetup[4] <= '0' + LIBJACK_SESSION_MANAGER_NSM,);
  124. CARLA_SAFE_ASSERT_RETURN(libjackSetup[5] >= '0' && libjackSetup[5] < '0'+0x4f,);
  125. std::memcpy(fBaseNameAudioPool, shmIds+6*0, 6);
  126. std::memcpy(fBaseNameRtClientControl, shmIds+6*1, 6);
  127. std::memcpy(fBaseNameNonRtClientControl, shmIds+6*2, 6);
  128. std::memcpy(fBaseNameNonRtServerControl, shmIds+6*3, 6);
  129. fBaseNameAudioPool[6] = '\0';
  130. fBaseNameRtClientControl[6] = '\0';
  131. fBaseNameNonRtClientControl[6] = '\0';
  132. fBaseNameNonRtServerControl[6] = '\0';
  133. fServer.numAudioIns = static_cast<uint8_t>(libjackSetup[0] - '0');
  134. fServer.numAudioOuts = static_cast<uint8_t>(libjackSetup[1] - '0');
  135. fServer.numMidiIns = static_cast<uint8_t>(libjackSetup[2] - '0');
  136. fServer.numMidiOuts = static_cast<uint8_t>(libjackSetup[3] - '0');
  137. fSessionManager = static_cast<uint>(libjackSetup[4] - '0');
  138. fSetupHints = static_cast<uint>(libjackSetup[5] - '0');
  139. jack_carla_interposed_action(LIBJACK_INTERPOSER_ACTION_SET_HINTS_AND_CALLBACK, fSetupHints, (void*)carla_interposed_callback);
  140. jack_carla_interposed_action(LIBJACK_INTERPOSER_ACTION_SET_SESSION_MANAGER, fSessionManager, nullptr);
  141. fNonRealtimeThread.startThread(false);
  142. }
  143. ~CarlaJackAppClient() noexcept override
  144. {
  145. carla_debug("CarlaJackAppClient::~CarlaJackAppClient()");
  146. fLastPingTime = -1;
  147. fNonRealtimeThread.stopThread(5000);
  148. const CarlaMutexLocker cms(fRealtimeThreadMutex);
  149. for (LinkedList<JackClientState*>::Itenerator it = fClients.begin2(); it.valid(); it.next())
  150. {
  151. JackClientState* const jclient(it.getValue(nullptr));
  152. CARLA_SAFE_ASSERT_CONTINUE(jclient != nullptr);
  153. delete jclient;
  154. }
  155. fClients.clear();
  156. }
  157. JackClientState* createClient(const char* const name)
  158. {
  159. while (fNonRealtimeThread.isThreadRunning() && ! fIsReady)
  160. carla_sleep(1);
  161. return new JackClientState(fServer, name);
  162. }
  163. void destroyClient(JackClientState* const jclient)
  164. {
  165. {
  166. const CarlaMutexLocker cms(fRealtimeThreadMutex);
  167. fClients.removeOne(jclient);
  168. }
  169. delete jclient;
  170. }
  171. bool activateClient(JackClientState* const jclient)
  172. {
  173. const CarlaMutexLocker cms(fRealtimeThreadMutex);
  174. if (! fClients.append(jclient))
  175. return false;
  176. jclient->activated = true;
  177. jclient->deactivated = false;
  178. return true;
  179. }
  180. bool deactivateClient(JackClientState* const jclient)
  181. {
  182. const CarlaMutexLocker cms(fRealtimeThreadMutex);
  183. if (! fClients.removeOne(jclient))
  184. return false;
  185. jclient->activated = false;
  186. jclient->deactivated = true;
  187. return true;
  188. }
  189. pthread_t getRealtimeThreadId() const noexcept
  190. {
  191. return (pthread_t)fRealtimeThread.getThreadId();
  192. }
  193. int handleInterposerCallback(const int cb_action, void* const ptr)
  194. {
  195. carla_debug("handleInterposerCallback(%o, %p)", cb_action, ptr);
  196. switch (cb_action)
  197. {
  198. case 1: {
  199. const CarlaMutexLocker cml(fShmNonRtServerControl.mutex);
  200. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerUiClosed);
  201. fShmNonRtServerControl.commitWrite();
  202. break;
  203. }
  204. }
  205. return 0;
  206. // maybe unused
  207. (void)ptr;
  208. }
  209. // -------------------------------------------------------------------
  210. protected:
  211. void runRealtimeThread() override;
  212. void runNonRealtimeThread() override;
  213. private:
  214. bool initSharedMemmory();
  215. void clearSharedMemory() noexcept;
  216. bool handleRtData();
  217. bool handleNonRtData();
  218. BridgeAudioPool fShmAudioPool;
  219. BridgeRtClientControl fShmRtClientControl;
  220. BridgeNonRtClientControl fShmNonRtClientControl;
  221. BridgeNonRtServerControl fShmNonRtServerControl;
  222. float* fAudioPoolCopy;
  223. float* fAudioTmpBuf;
  224. JackMidiPortBufferDummy fDummyMidiInBuffer;
  225. JackMidiPortBufferDummy fDummyMidiOutBuffer;
  226. JackMidiPortBufferOnStack* fMidiInBuffers;
  227. JackMidiPortBufferOnStack* fMidiOutBuffers;
  228. char fBaseNameAudioPool[6+1];
  229. char fBaseNameRtClientControl[6+1];
  230. char fBaseNameNonRtClientControl[6+1];
  231. char fBaseNameNonRtServerControl[6+1];
  232. bool fIsOffline;
  233. volatile bool fIsReady;
  234. int64_t fLastPingTime;
  235. uint fSessionManager;
  236. uint fSetupHints;
  237. CarlaJackRealtimeThread fRealtimeThread;
  238. CarlaJackNonRealtimeThread fNonRealtimeThread;
  239. CarlaMutex fRealtimeThreadMutex;
  240. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaJackAppClient)
  241. };
  242. // ---------------------------------------------------------------------------------------------------------------------
  243. bool CarlaJackAppClient::initSharedMemmory()
  244. {
  245. if (! fShmAudioPool.attachClient(fBaseNameAudioPool))
  246. {
  247. carla_stderr("Failed to attach to audio pool shared memory");
  248. return false;
  249. }
  250. if (! fShmRtClientControl.attachClient(fBaseNameRtClientControl))
  251. {
  252. clearSharedMemory();
  253. carla_stderr("Failed to attach to rt client control shared memory");
  254. return false;
  255. }
  256. if (! fShmRtClientControl.mapData())
  257. {
  258. clearSharedMemory();
  259. carla_stderr("Failed to map rt client control shared memory");
  260. return false;
  261. }
  262. if (! fShmNonRtClientControl.attachClient(fBaseNameNonRtClientControl))
  263. {
  264. clearSharedMemory();
  265. carla_stderr("Failed to attach to non-rt client control shared memory");
  266. return false;
  267. }
  268. if (! fShmNonRtClientControl.mapData())
  269. {
  270. clearSharedMemory();
  271. carla_stderr("Failed to map non-rt control client shared memory");
  272. return false;
  273. }
  274. if (! fShmNonRtServerControl.attachClient(fBaseNameNonRtServerControl))
  275. {
  276. clearSharedMemory();
  277. carla_stderr("Failed to attach to non-rt server control shared memory");
  278. return false;
  279. }
  280. if (! fShmNonRtServerControl.mapData())
  281. {
  282. clearSharedMemory();
  283. carla_stderr("Failed to map non-rt control server shared memory");
  284. return false;
  285. }
  286. PluginBridgeNonRtClientOpcode opcode;
  287. opcode = fShmNonRtClientControl.readOpcode();
  288. CARLA_SAFE_ASSERT_RETURN(opcode == kPluginBridgeNonRtClientVersion, false);
  289. const uint32_t apiVersion = fShmNonRtClientControl.readUInt();
  290. CARLA_SAFE_ASSERT_RETURN(apiVersion == CARLA_PLUGIN_BRIDGE_API_VERSION, false);
  291. const uint32_t shmRtClientDataSize = fShmNonRtClientControl.readUInt();
  292. CARLA_SAFE_ASSERT_INT2(shmRtClientDataSize == sizeof(BridgeRtClientData), shmRtClientDataSize, sizeof(BridgeRtClientData));
  293. const uint32_t shmNonRtClientDataSize = fShmNonRtClientControl.readUInt();
  294. CARLA_SAFE_ASSERT_INT2(shmNonRtClientDataSize == sizeof(BridgeNonRtClientData), shmNonRtClientDataSize, sizeof(BridgeNonRtClientData));
  295. const uint32_t shmNonRtServerDataSize = fShmNonRtClientControl.readUInt();
  296. CARLA_SAFE_ASSERT_INT2(shmNonRtServerDataSize == sizeof(BridgeNonRtServerData), shmNonRtServerDataSize, sizeof(BridgeNonRtServerData));
  297. if (shmRtClientDataSize != sizeof(BridgeRtClientData) ||
  298. shmNonRtClientDataSize != sizeof(BridgeNonRtClientData) ||
  299. shmNonRtServerDataSize != sizeof(BridgeNonRtServerData))
  300. {
  301. carla_stderr2("CarlaJackAppClient: data size mismatch");
  302. return false;
  303. }
  304. opcode = fShmNonRtClientControl.readOpcode();
  305. CARLA_SAFE_ASSERT_RETURN(opcode == kPluginBridgeNonRtClientInitialSetup, false);
  306. fServer.bufferSize = fShmNonRtClientControl.readUInt();
  307. fServer.sampleRate = fShmNonRtClientControl.readDouble();
  308. if (fServer.bufferSize == 0 || carla_isZero(fServer.sampleRate))
  309. {
  310. carla_stderr2("CarlaJackAppClient: invalid empty state");
  311. return false;
  312. }
  313. fAudioTmpBuf = new float[fServer.bufferSize];
  314. carla_zeroFloats(fAudioTmpBuf, fServer.bufferSize);
  315. fLastPingTime = getCurrentTimeMilliseconds();
  316. CARLA_SAFE_ASSERT(fLastPingTime > 0);
  317. {
  318. // tell backend we're live
  319. const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex);
  320. // ready!
  321. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerReady);
  322. fShmNonRtServerControl.commitWrite();
  323. fShmNonRtServerControl.waitIfDataIsReachingLimit();
  324. }
  325. fIsReady = true;
  326. return true;
  327. }
  328. void CarlaJackAppClient::clearSharedMemory() noexcept
  329. {
  330. const CarlaMutexLocker cml(fRealtimeThreadMutex);
  331. if (fAudioPoolCopy != nullptr)
  332. {
  333. delete[] fAudioPoolCopy;
  334. fAudioPoolCopy = nullptr;
  335. }
  336. if (fAudioTmpBuf != nullptr)
  337. {
  338. delete[] fAudioTmpBuf;
  339. fAudioTmpBuf = nullptr;
  340. }
  341. if (fMidiInBuffers != nullptr)
  342. {
  343. delete[] fMidiInBuffers;
  344. fMidiInBuffers = nullptr;
  345. }
  346. if (fMidiOutBuffers != nullptr)
  347. {
  348. delete[] fMidiOutBuffers;
  349. fMidiOutBuffers = nullptr;
  350. }
  351. fShmAudioPool.clear();
  352. fShmRtClientControl.clear();
  353. fShmNonRtClientControl.clear();
  354. fShmNonRtServerControl.clear();
  355. }
  356. bool CarlaJackAppClient::handleRtData()
  357. {
  358. const BridgeRtClientControl::WaitHelper helper(fShmRtClientControl);
  359. if (! helper.ok)
  360. return false;
  361. bool ret = false;
  362. for (; fShmRtClientControl.isDataAvailableForReading();)
  363. {
  364. const PluginBridgeRtClientOpcode opcode(fShmRtClientControl.readOpcode());
  365. #ifdef DEBUG
  366. if (opcode != kPluginBridgeRtClientProcess && opcode != kPluginBridgeRtClientMidiEvent) {
  367. carla_debug("CarlaJackAppClientRtThread::run() - got opcode: %s", PluginBridgeRtClientOpcode2str(opcode));
  368. }
  369. #endif
  370. switch (opcode)
  371. {
  372. case kPluginBridgeRtClientNull:
  373. break;
  374. case kPluginBridgeRtClientSetAudioPool: {
  375. const CarlaMutexLocker cml(fRealtimeThreadMutex);
  376. if (fShmAudioPool.data != nullptr)
  377. {
  378. jackbridge_shm_unmap(fShmAudioPool.shm, fShmAudioPool.data);
  379. fShmAudioPool.data = nullptr;
  380. }
  381. if (fAudioPoolCopy != nullptr)
  382. {
  383. delete[] fAudioPoolCopy;
  384. fAudioPoolCopy = nullptr;
  385. }
  386. const uint64_t poolSize(fShmRtClientControl.readULong());
  387. CARLA_SAFE_ASSERT_BREAK(poolSize > 0);
  388. fShmAudioPool.data = (float*)jackbridge_shm_map(fShmAudioPool.shm, static_cast<size_t>(poolSize));
  389. fAudioPoolCopy = new float[poolSize];
  390. break;
  391. }
  392. case kPluginBridgeRtClientSetBufferSize:
  393. if (const uint32_t newBufferSize = fShmRtClientControl.readUInt())
  394. {
  395. if (fServer.bufferSize != newBufferSize)
  396. {
  397. const CarlaMutexLocker cml(fRealtimeThreadMutex);
  398. fServer.bufferSize = newBufferSize;
  399. for (LinkedList<JackClientState*>::Itenerator it = fClients.begin2(); it.valid(); it.next())
  400. {
  401. JackClientState* const jclient(it.getValue(nullptr));
  402. CARLA_SAFE_ASSERT_CONTINUE(jclient != nullptr);
  403. if (jclient->bufferSizeCb != nullptr)
  404. jclient->bufferSizeCb(fServer.bufferSize, jclient->bufferSizeCbPtr);
  405. }
  406. delete[] fAudioTmpBuf;
  407. fAudioTmpBuf = new float[fServer.bufferSize];
  408. carla_zeroFloats(fAudioTmpBuf, fServer.bufferSize);
  409. }
  410. }
  411. break;
  412. case kPluginBridgeRtClientSetSampleRate: {
  413. const double newSampleRate = fShmRtClientControl.readDouble();
  414. if (carla_isNotZero(newSampleRate) && carla_isNotEqual(fServer.sampleRate, newSampleRate))
  415. {
  416. const CarlaMutexLocker cml(fRealtimeThreadMutex);
  417. fServer.sampleRate = newSampleRate;
  418. for (LinkedList<JackClientState*>::Itenerator it = fClients.begin2(); it.valid(); it.next())
  419. {
  420. JackClientState* const jclient(it.getValue(nullptr));
  421. CARLA_SAFE_ASSERT_CONTINUE(jclient != nullptr);
  422. if (jclient->sampleRateCb != nullptr)
  423. jclient->sampleRateCb(static_cast<uint32_t>(fServer.sampleRate), jclient->sampleRateCbPtr);
  424. }
  425. }
  426. } break;
  427. case kPluginBridgeRtClientSetOnline: {
  428. const bool offline = fShmRtClientControl.readBool();
  429. if (fIsOffline != offline)
  430. {
  431. const CarlaMutexLocker cml(fRealtimeThreadMutex);
  432. fIsOffline = offline;
  433. for (LinkedList<JackClientState*>::Itenerator it = fClients.begin2(); it.valid(); it.next())
  434. {
  435. JackClientState* const jclient(it.getValue(nullptr));
  436. CARLA_SAFE_ASSERT_CONTINUE(jclient != nullptr);
  437. if (jclient->freewheelCb != nullptr)
  438. jclient->freewheelCb(offline ? 1 : 0, jclient->freewheelCbPtr);
  439. }
  440. }
  441. } break;
  442. case kPluginBridgeRtClientControlEventParameter:
  443. case kPluginBridgeRtClientControlEventMidiBank:
  444. case kPluginBridgeRtClientControlEventMidiProgram:
  445. case kPluginBridgeRtClientControlEventAllSoundOff:
  446. case kPluginBridgeRtClientControlEventAllNotesOff:
  447. break;
  448. case kPluginBridgeRtClientMidiEvent: {
  449. const uint32_t time(fShmRtClientControl.readUInt());
  450. const uint8_t port(fShmRtClientControl.readByte());
  451. const uint8_t size(fShmRtClientControl.readByte());
  452. CARLA_SAFE_ASSERT_BREAK(size > 0);
  453. if (port >= fServer.numMidiIns || size > JackMidiPortBufferBase::kMaxEventSize || ! fRealtimeThreadMutex.tryLock())
  454. {
  455. for (uint8_t i=0; i<size; ++i)
  456. fShmRtClientControl.readByte();
  457. break;
  458. }
  459. JackMidiPortBufferOnStack& midiPortBuf(fMidiInBuffers[port]);
  460. if (midiPortBuf.count < JackMidiPortBufferBase::kMaxEventCount &&
  461. midiPortBuf.bufferPoolPos + size < JackMidiPortBufferBase::kBufferPoolSize)
  462. {
  463. jack_midi_event_t& ev(midiPortBuf.events[midiPortBuf.count++]);
  464. ev.time = time;
  465. ev.size = size;
  466. ev.buffer = midiPortBuf.bufferPool + midiPortBuf.bufferPoolPos;
  467. midiPortBuf.bufferPoolPos += size;
  468. for (uint8_t i=0; i<size; ++i)
  469. ev.buffer[i] = fShmRtClientControl.readByte();
  470. }
  471. fRealtimeThreadMutex.unlock(true);
  472. break;
  473. }
  474. case kPluginBridgeRtClientProcess: {
  475. const uint32_t frames(fShmRtClientControl.readUInt());
  476. CARLA_SAFE_ASSERT_UINT2_BREAK(frames == fServer.bufferSize, frames, fServer.bufferSize);
  477. // TODO tell client of xrun in case buffersize does not match
  478. const CarlaMutexTryLocker cmtl(fRealtimeThreadMutex, fIsOffline);
  479. if (cmtl.wasLocked())
  480. {
  481. CARLA_SAFE_ASSERT_BREAK(fShmAudioPool.data != nullptr);
  482. // mixdown is default, do buffer addition (for multiple clients) if requested
  483. const bool doBufferAddition = fSetupHints & 0x10;
  484. // location to start of audio outputs (shm buffer)
  485. float* const fdataRealOuts = fShmAudioPool.data+(fServer.bufferSize*fServer.numAudioIns);
  486. if (doBufferAddition && fServer.numAudioOuts > 0)
  487. carla_zeroFloats(fdataRealOuts, fServer.bufferSize*fServer.numAudioOuts);
  488. if (! fClients.isEmpty())
  489. {
  490. // save tranport for all clients
  491. const BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo);
  492. fServer.playing = bridgeTimeInfo.playing;
  493. fServer.position.frame = static_cast<jack_nframes_t>(bridgeTimeInfo.frame);
  494. fServer.position.usecs = bridgeTimeInfo.usecs;
  495. if (bridgeTimeInfo.validFlags & kPluginBridgeTimeInfoValidBBT)
  496. {
  497. fServer.position.valid = JackPositionBBT;
  498. fServer.position.bar = bridgeTimeInfo.bar;
  499. fServer.position.beat = bridgeTimeInfo.beat;
  500. fServer.position.tick = static_cast<int32_t>(bridgeTimeInfo.tick + 0.5);
  501. fServer.position.beats_per_bar = bridgeTimeInfo.beatsPerBar;
  502. fServer.position.beat_type = bridgeTimeInfo.beatType;
  503. fServer.position.ticks_per_beat = bridgeTimeInfo.ticksPerBeat;
  504. fServer.position.beats_per_minute = bridgeTimeInfo.beatsPerMinute;
  505. fServer.position.bar_start_tick = bridgeTimeInfo.barStartTick;
  506. }
  507. else
  508. {
  509. fServer.position.valid = static_cast<jack_position_bits_t>(0x0);
  510. }
  511. int numClientOutputsProcessed = 0;
  512. // now go through each client
  513. for (LinkedList<JackClientState*>::Itenerator it = fClients.begin2(); it.valid(); it.next())
  514. {
  515. JackClientState* const jclient(it.getValue(nullptr));
  516. CARLA_SAFE_ASSERT_CONTINUE(jclient != nullptr);
  517. // FIXME - lock if offline
  518. const CarlaMutexTryLocker cmtl2(jclient->mutex);
  519. // check if we can process
  520. if (cmtl2.wasNotLocked() || jclient->processCb == nullptr || ! jclient->activated)
  521. {
  522. if (fServer.numAudioOuts > 0)
  523. carla_zeroFloats(fdataRealOuts, fServer.bufferSize*fServer.numAudioOuts);
  524. if (jclient->deactivated)
  525. fShmRtClientControl.data->procFlags = 1;
  526. }
  527. else
  528. {
  529. uint8_t i;
  530. // direct access to shm buffer, used only for inputs
  531. float* fdataReal = fShmAudioPool.data;
  532. // safe temp location for output, mixed down to shm buffer later on
  533. float* fdataCopy = fAudioPoolCopy;
  534. // wherever we're using fAudioTmpBuf
  535. bool needsTmpBufClear = false;
  536. // set audio inputs
  537. i = 0;
  538. for (LinkedList<JackPortState*>::Itenerator it2 = jclient->audioIns.begin2(); it2.valid(); it2.next())
  539. {
  540. JackPortState* const jport = it2.getValue(nullptr);
  541. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  542. if (i++ < fServer.numAudioIns)
  543. {
  544. if (numClientOutputsProcessed == 0 || ! doBufferAddition)
  545. jport->buffer = fdataReal;
  546. else
  547. jport->buffer = fdataRealOuts + (i*fServer.bufferSize);
  548. fdataReal += fServer.bufferSize;
  549. fdataCopy += fServer.bufferSize;
  550. }
  551. else
  552. {
  553. jport->buffer = fAudioTmpBuf;
  554. needsTmpBufClear = true;
  555. }
  556. }
  557. if (i < fServer.numAudioIns)
  558. {
  559. const std::size_t remainingBufferSize = fServer.bufferSize * static_cast<uint8_t>(fServer.numAudioIns - i);
  560. //fdataReal += remainingBufferSize;
  561. fdataCopy += remainingBufferSize;
  562. }
  563. // location to start of audio outputs
  564. float* const fdataCopyOuts = fdataCopy;
  565. // set audio ouputs
  566. i = 0;
  567. for (LinkedList<JackPortState*>::Itenerator it2 = jclient->audioOuts.begin2(); it2.valid(); it2.next())
  568. {
  569. JackPortState* const jport = it2.getValue(nullptr);
  570. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  571. if (i++ < fServer.numAudioOuts)
  572. {
  573. jport->buffer = fdataCopy;
  574. fdataCopy += fServer.bufferSize;
  575. }
  576. else
  577. {
  578. jport->buffer = fAudioTmpBuf;
  579. needsTmpBufClear = true;
  580. }
  581. }
  582. if (i < fServer.numAudioOuts)
  583. {
  584. const std::size_t remainingBufferSize = fServer.bufferSize * static_cast<uint8_t>(fServer.numAudioOuts - i);
  585. carla_zeroFloats(fdataCopy, remainingBufferSize);
  586. //fdataCopy += remainingBufferSize;
  587. }
  588. // set midi inputs
  589. i = 0;
  590. for (LinkedList<JackPortState*>::Itenerator it2 = jclient->midiIns.begin2(); it2.valid(); it2.next())
  591. {
  592. JackPortState* const jport = it2.getValue(nullptr);
  593. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  594. if (i++ < fServer.numMidiIns)
  595. jport->buffer = &fMidiInBuffers[i-1];
  596. else
  597. jport->buffer = &fDummyMidiInBuffer;
  598. }
  599. // set midi outputs
  600. i = 0;
  601. for (LinkedList<JackPortState*>::Itenerator it2 = jclient->midiOuts.begin2(); it2.valid(); it2.next())
  602. {
  603. JackPortState* const jport = it2.getValue(nullptr);
  604. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  605. if (i++ < fServer.numMidiOuts)
  606. jport->buffer = &fMidiOutBuffers[i-1];
  607. else
  608. jport->buffer = &fDummyMidiOutBuffer;
  609. }
  610. if (needsTmpBufClear)
  611. carla_zeroFloats(fAudioTmpBuf, fServer.bufferSize);
  612. jclient->processCb(fServer.bufferSize, jclient->processCbPtr);
  613. if (fServer.numAudioOuts > 0)
  614. {
  615. if (++numClientOutputsProcessed == 1)
  616. {
  617. // first client, we can copy stuff over
  618. carla_copyFloats(fdataRealOuts, fdataCopyOuts,
  619. fServer.bufferSize*fServer.numAudioOuts);
  620. }
  621. else
  622. {
  623. // subsequent clients, add data (then divide by number of clients later on)
  624. carla_add(fdataRealOuts, fdataCopyOuts,
  625. fServer.bufferSize*fServer.numAudioOuts);
  626. if (doBufferAddition)
  627. {
  628. // for more than 1 client addition, we need to divide buffers now
  629. carla_multiply(fdataRealOuts,
  630. 1.0f/static_cast<float>(numClientOutputsProcessed),
  631. fServer.bufferSize*fServer.numAudioOuts);
  632. }
  633. }
  634. if (jclient->audioOuts.count() == 1 && fServer.numAudioOuts > 1)
  635. {
  636. for (uint8_t j=1; j<fServer.numAudioOuts; ++j)
  637. {
  638. carla_copyFloats(fdataRealOuts+(fServer.bufferSize*j),
  639. fdataCopyOuts,
  640. fServer.bufferSize);
  641. }
  642. }
  643. }
  644. }
  645. }
  646. if (numClientOutputsProcessed > 1 && ! doBufferAddition)
  647. {
  648. // more than 1 client active, need to divide buffers
  649. carla_multiply(fdataRealOuts,
  650. 1.0f/static_cast<float>(numClientOutputsProcessed),
  651. fServer.bufferSize*fServer.numAudioOuts);
  652. }
  653. }
  654. // fClients.isEmpty()
  655. else if (fServer.numAudioOuts > 0)
  656. {
  657. carla_zeroFloats(fdataRealOuts, fServer.bufferSize*fServer.numAudioOuts);
  658. }
  659. for (uint8_t i=0; i<fServer.numMidiIns; ++i)
  660. {
  661. fMidiInBuffers[i].count = 0;
  662. fMidiInBuffers[i].bufferPoolPos = 0;
  663. }
  664. if (fServer.numMidiOuts > 0)
  665. {
  666. uint8_t* midiData(fShmRtClientControl.data->midiOut);
  667. carla_zeroBytes(midiData, kBridgeBaseMidiOutHeaderSize);
  668. std::size_t curMidiDataPos = 0;
  669. for (uint8_t i=0; i<fServer.numMidiOuts; ++i)
  670. {
  671. JackMidiPortBufferOnStack& midiPortBuf(fMidiOutBuffers[i]);
  672. for (uint16_t j=0; j<midiPortBuf.count; ++j)
  673. {
  674. jack_midi_event_t& jmevent(midiPortBuf.events[j]);
  675. if (curMidiDataPos + kBridgeBaseMidiOutHeaderSize + jmevent.size >= kBridgeRtClientDataMidiOutSize)
  676. break;
  677. // set time
  678. *(uint32_t*)midiData = jmevent.time;
  679. midiData += 4;
  680. // set port
  681. *midiData++ = i;
  682. // set size
  683. *midiData++ = static_cast<uint8_t>(jmevent.size);
  684. // set data
  685. std::memcpy(midiData, jmevent.buffer, jmevent.size);
  686. midiData += jmevent.size;
  687. curMidiDataPos += kBridgeBaseMidiOutHeaderSize + jmevent.size;
  688. }
  689. }
  690. if (curMidiDataPos != 0 &&
  691. curMidiDataPos + kBridgeBaseMidiOutHeaderSize < kBridgeRtClientDataMidiOutSize)
  692. carla_zeroBytes(midiData, kBridgeBaseMidiOutHeaderSize);
  693. }
  694. }
  695. else
  696. {
  697. carla_stderr2("CarlaJackAppClient: fRealtimeThreadMutex tryLock failed");
  698. }
  699. break;
  700. }
  701. case kPluginBridgeRtClientQuit:
  702. ret = true;
  703. break;
  704. }
  705. #ifdef DEBUG
  706. if (opcode != kPluginBridgeRtClientProcess && opcode != kPluginBridgeRtClientMidiEvent) {
  707. carla_debug("CarlaJackAppClientRtThread::run() - opcode %s done", PluginBridgeRtClientOpcode2str(opcode));
  708. }
  709. #endif
  710. }
  711. return ret;
  712. }
  713. bool CarlaJackAppClient::handleNonRtData()
  714. {
  715. bool ret = false;
  716. for (; fShmNonRtClientControl.isDataAvailableForReading();)
  717. {
  718. const PluginBridgeNonRtClientOpcode opcode(fShmNonRtClientControl.readOpcode());
  719. #ifdef DEBUG
  720. if (opcode != kPluginBridgeNonRtClientPing) {
  721. carla_debug("CarlaJackAppClient::handleNonRtData() - got opcode: %s", PluginBridgeNonRtClientOpcode2str(opcode));
  722. }
  723. #endif
  724. if (opcode != kPluginBridgeNonRtClientNull && opcode != kPluginBridgeNonRtClientPingOnOff && fLastPingTime > 0)
  725. fLastPingTime = getCurrentTimeMilliseconds();
  726. switch (opcode)
  727. {
  728. case kPluginBridgeNonRtClientNull:
  729. break;
  730. case kPluginBridgeNonRtClientVersion: {
  731. const uint apiVersion = fShmNonRtServerControl.readUInt();
  732. CARLA_SAFE_ASSERT_UINT2(apiVersion == CARLA_PLUGIN_BRIDGE_API_VERSION, apiVersion, CARLA_PLUGIN_BRIDGE_API_VERSION);
  733. } break;
  734. case kPluginBridgeNonRtClientPing: {
  735. const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex);
  736. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerPong);
  737. fShmNonRtServerControl.commitWrite();
  738. } break;
  739. case kPluginBridgeNonRtClientPingOnOff: {
  740. const uint32_t onOff(fShmNonRtClientControl.readBool());
  741. fLastPingTime = onOff ? getCurrentTimeMilliseconds() : -1;
  742. } break;
  743. case kPluginBridgeNonRtClientActivate:
  744. case kPluginBridgeNonRtClientDeactivate:
  745. break;
  746. case kPluginBridgeNonRtClientInitialSetup:
  747. // should never happen!!
  748. fShmNonRtServerControl.readUInt();
  749. fShmNonRtServerControl.readDouble();
  750. break;
  751. case kPluginBridgeNonRtClientSetParameterValue:
  752. case kPluginBridgeNonRtClientSetParameterMidiChannel:
  753. case kPluginBridgeNonRtClientSetParameterMidiCC:
  754. case kPluginBridgeNonRtClientSetProgram:
  755. case kPluginBridgeNonRtClientSetMidiProgram:
  756. case kPluginBridgeNonRtClientSetCustomData:
  757. case kPluginBridgeNonRtClientSetChunkDataFile:
  758. break;
  759. case kPluginBridgeNonRtClientSetOption:
  760. fShmNonRtClientControl.readUInt();
  761. fShmNonRtClientControl.readBool();
  762. break;
  763. case kPluginBridgeNonRtClientSetCtrlChannel:
  764. fShmNonRtClientControl.readShort();
  765. break;
  766. case kPluginBridgeNonRtClientGetParameterText:
  767. fShmNonRtClientControl.readUInt();
  768. break;
  769. case kPluginBridgeNonRtClientPrepareForSave:
  770. {
  771. if (fSessionManager == LIBJACK_SESSION_MANAGER_AUTO && std::getenv("NSM_URL") == nullptr)
  772. {
  773. struct sigaction sig;
  774. carla_zeroStruct(sig);
  775. sigaction(SIGUSR1, nullptr, &sig);
  776. if (sig.sa_handler != nullptr)
  777. fSessionManager = LIBJACK_SESSION_MANAGER_LADISH;
  778. }
  779. if (fSessionManager == LIBJACK_SESSION_MANAGER_LADISH)
  780. ::kill(::getpid(), SIGUSR1);
  781. const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex);
  782. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerSaved);
  783. fShmNonRtServerControl.commitWrite();
  784. }
  785. break;
  786. case kPluginBridgeNonRtClientRestoreLV2State:
  787. break;
  788. case kPluginBridgeNonRtClientShowUI:
  789. if (jack_carla_interposed_action(LIBJACK_INTERPOSER_ACTION_SHOW_HIDE_GUI, 1, nullptr) == 1337)
  790. {
  791. // failed, LD_PRELOAD did not work?
  792. const char* const message("Cannot show UI, LD_PRELOAD not working?");
  793. const std::size_t messageSize(std::strlen(message));
  794. const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex);
  795. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerUiClosed);
  796. fShmNonRtServerControl.commitWrite();
  797. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerError);
  798. fShmNonRtServerControl.writeUInt(messageSize);
  799. fShmNonRtServerControl.writeCustomData(message, messageSize);
  800. fShmNonRtServerControl.commitWrite();
  801. }
  802. break;
  803. case kPluginBridgeNonRtClientHideUI:
  804. jack_carla_interposed_action(LIBJACK_INTERPOSER_ACTION_SHOW_HIDE_GUI, 0, nullptr);
  805. break;
  806. case kPluginBridgeNonRtClientUiParameterChange:
  807. case kPluginBridgeNonRtClientUiProgramChange:
  808. case kPluginBridgeNonRtClientUiMidiProgramChange:
  809. case kPluginBridgeNonRtClientUiNoteOn:
  810. case kPluginBridgeNonRtClientUiNoteOff:
  811. break;
  812. case kPluginBridgeNonRtClientQuit:
  813. ret = true;
  814. break;
  815. }
  816. #ifdef DEBUG
  817. if (opcode != kPluginBridgeNonRtClientPing) {
  818. carla_debug("CarlaJackAppClient::handleNonRtData() - opcode %s handled", PluginBridgeNonRtClientOpcode2str(opcode));
  819. }
  820. #endif
  821. }
  822. return ret;
  823. }
  824. void CarlaJackAppClient::runRealtimeThread()
  825. {
  826. carla_debug("CarlaJackAppClient runRealtimeThread START");
  827. #ifdef __SSE2_MATH__
  828. // Set FTZ and DAZ flags
  829. _mm_setcsr(_mm_getcsr() | 0x8040);
  830. #endif
  831. bool quitReceived = false;
  832. for (; ! fRealtimeThread.shouldThreadExit();)
  833. {
  834. if (handleRtData())
  835. {
  836. quitReceived = true;
  837. break;
  838. }
  839. }
  840. fNonRealtimeThread.signalThreadShouldExit();
  841. carla_debug("CarlaJackAppClient runRealtimeThread FINISHED");
  842. // TODO
  843. return; (void)quitReceived;
  844. }
  845. void CarlaJackAppClient::runNonRealtimeThread()
  846. {
  847. carla_debug("CarlaJackAppClient runNonRealtimeThread START");
  848. CARLA_SAFE_ASSERT_RETURN(initSharedMemmory(),);
  849. if (fServer.numMidiIns > 0)
  850. {
  851. fMidiInBuffers = new JackMidiPortBufferOnStack[fServer.numMidiIns];
  852. for (uint8_t i=0; i<fServer.numMidiIns; ++i)
  853. fMidiInBuffers[i].isInput = true;
  854. }
  855. if (fServer.numMidiOuts > 0)
  856. {
  857. fMidiOutBuffers = new JackMidiPortBufferOnStack[fServer.numMidiOuts];
  858. for (uint8_t i=0; i<fServer.numMidiOuts; ++i)
  859. fMidiOutBuffers[i].isInput = false;
  860. }
  861. fRealtimeThread.startThread(true);
  862. fLastPingTime = getCurrentTimeMilliseconds();
  863. carla_stdout("Carla Jack Client Ready!");
  864. bool quitReceived = false,
  865. timedOut = false;
  866. for (; ! fNonRealtimeThread.shouldThreadExit();)
  867. {
  868. carla_msleep(50);
  869. try {
  870. quitReceived = handleNonRtData();
  871. } CARLA_SAFE_EXCEPTION("handleNonRtData");
  872. if (quitReceived)
  873. break;
  874. /*
  875. if (fLastPingTime > 0 && getCurrentTimeMilliseconds() > fLastPingTime + 30000)
  876. {
  877. carla_stderr("Did not receive ping message from server for 30 secs, closing...");
  878. timedOut = true;
  879. fRealtimeThread.signalThreadShouldExit();
  880. break;
  881. }
  882. */
  883. }
  884. //callback(true, true, ENGINE_CALLBACK_ENGINE_STOPPED, 0, 0, 0, 0.0f, nullptr);
  885. if (quitReceived)
  886. {
  887. ::kill(::getpid(), SIGTERM);
  888. }
  889. else if (timedOut)
  890. {
  891. // TODO send shutdown?
  892. carla_stderr("CarlaJackAppClient error: runNonRealtimeThread ended with time out");
  893. ::kill(::getpid(), SIGTERM);
  894. }
  895. else
  896. {
  897. bool activated;
  898. {
  899. const CarlaMutexLocker cms(fRealtimeThreadMutex);
  900. if (fClients.isEmpty())
  901. {
  902. activated = false;
  903. }
  904. else if (JackClientState* const jclient = fClients.getLast(nullptr))
  905. {
  906. const CarlaMutexLocker cms2(jclient->mutex);
  907. activated = jclient->activated;
  908. }
  909. else
  910. {
  911. activated = true;
  912. }
  913. }
  914. if (activated)
  915. {
  916. carla_stderr("CarlaJackAppClient error: runNonRealtimeThread ended while client is activated");
  917. const char* const message("Plugin bridge error, process thread has stopped");
  918. const std::size_t messageSize(std::strlen(message));
  919. const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex);
  920. fShmNonRtServerControl.writeOpcode(kPluginBridgeNonRtServerError);
  921. fShmNonRtServerControl.writeUInt(messageSize);
  922. fShmNonRtServerControl.writeCustomData(message, messageSize);
  923. fShmNonRtServerControl.commitWrite();
  924. }
  925. }
  926. if (fRealtimeThread.isThreadRunning())
  927. {
  928. fRealtimeThread.signalThreadShouldExit();
  929. const CarlaMutexLocker cml(fRealtimeThreadMutex);
  930. if (fShmRtClientControl.data != nullptr)
  931. fShmRtClientControl.data->procFlags = 1;
  932. }
  933. clearSharedMemory();
  934. fRealtimeThread.stopThread(5000);
  935. carla_debug("CarlaJackAppClient runNonRealtimeThread FINISHED");
  936. }
  937. CARLA_BACKEND_END_NAMESPACE
  938. // ---------------------------------------------------------------------------------------------------------------------
  939. using CarlaBackend::CarlaJackAppClient;
  940. using CarlaBackend::JackClientState;
  941. static CarlaJackAppClient gClient;
  942. CARLA_BACKEND_START_NAMESPACE
  943. static int carla_interposed_callback(int cb_action, void* ptr)
  944. {
  945. return gClient.handleInterposerCallback(cb_action, ptr);
  946. }
  947. CARLA_BACKEND_END_NAMESPACE
  948. // ---------------------------------------------------------------------------------------------------------------------
  949. CARLA_EXPORT
  950. jack_client_t* jack_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...)
  951. {
  952. carla_debug("%s(%s, 0x%x, %p)", __FUNCTION__, client_name, options, status);
  953. if (JackClientState* const client = gClient.createClient(client_name))
  954. {
  955. if (status != nullptr)
  956. *status = static_cast<JackStatus>(0x0);
  957. return (jack_client_t*)client;
  958. }
  959. if (status != nullptr)
  960. *status = JackServerError;
  961. return nullptr;
  962. // unused
  963. (void)options;
  964. }
  965. CARLA_EXPORT
  966. jack_client_t* jack_client_new(const char* client_name)
  967. {
  968. return jack_client_open(client_name, JackNullOption, nullptr);
  969. }
  970. CARLA_EXPORT
  971. int jack_client_close(jack_client_t* client)
  972. {
  973. carla_debug("%s(%p)", __FUNCTION__, client);
  974. JackClientState* const jclient = (JackClientState*)client;
  975. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1);
  976. gClient.destroyClient(jclient);
  977. return 0;
  978. }
  979. CARLA_EXPORT
  980. int jack_activate(jack_client_t* client)
  981. {
  982. carla_debug("%s(%p)", __FUNCTION__, client);
  983. JackClientState* const jclient = (JackClientState*)client;
  984. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1);
  985. return gClient.activateClient(jclient) ? 0 : 1;
  986. }
  987. CARLA_EXPORT
  988. int jack_deactivate(jack_client_t* client)
  989. {
  990. carla_debug("%s(%p)", __FUNCTION__, client);
  991. JackClientState* const jclient = (JackClientState*)client;
  992. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 1);
  993. return gClient.deactivateClient(jclient) ? 0 : 1;
  994. }
  995. // ---------------------------------------------------------------------------------------------------------------------
  996. CARLA_EXPORT
  997. pthread_t jack_client_thread_id(jack_client_t* client)
  998. {
  999. carla_debug("%s(%p)", __FUNCTION__, client);
  1000. JackClientState* const jclient = (JackClientState*)client;
  1001. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0);
  1002. CarlaJackAppClient* const jackAppPtr = jclient->server.jackAppPtr;
  1003. CARLA_SAFE_ASSERT_RETURN(jackAppPtr != nullptr && jackAppPtr == &gClient, 0);
  1004. return jackAppPtr->getRealtimeThreadId();
  1005. }
  1006. // ---------------------------------------------------------------------------------------------------------------------
  1007. #include "jackbridge/JackBridge2.cpp"
  1008. #include "CarlaBridgeUtils.cpp"
  1009. // ---------------------------------------------------------------------------------------------------------------------
  1010. // TODO
  1011. CARLA_BACKEND_USE_NAMESPACE
  1012. CARLA_EXPORT
  1013. int jack_client_real_time_priority(jack_client_t* client)
  1014. {
  1015. carla_debug("%s(%p)", __FUNCTION__, client);
  1016. // code as used by water
  1017. const int minPriority = sched_get_priority_min(SCHED_RR);
  1018. const int maxPriority = sched_get_priority_max(SCHED_RR);
  1019. return ((maxPriority - minPriority) * 9) / 10 + minPriority;
  1020. // unused
  1021. (void)client;
  1022. }
  1023. int jack_client_create_thread(jack_client_t* client, pthread_t* thread, int priority,
  1024. int realtime, void *(*start_routine)(void*), void* arg)
  1025. {
  1026. carla_stderr2("%s(%p, %p, %i, %i, %p, %p)", __FUNCTION__, client, thread, priority, realtime, start_routine, arg);
  1027. return ENOSYS;
  1028. }
  1029. typedef void (*JackSessionCallback)(jack_session_event_t*, void*);
  1030. CARLA_EXPORT
  1031. int jack_set_session_callback(jack_client_t* client, JackSessionCallback callback, void* arg)
  1032. {
  1033. carla_stderr2("%s(%p, %p, %p)", __FUNCTION__, client, callback, arg);
  1034. return 0;
  1035. }
  1036. // ---------------------------------------------------------------------------------------------------------------------