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.

libjack.cpp 43KB

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