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.

2348 lines
79KB

  1. /*
  2. * Carla Plugin Host
  3. * Copyright (C) 2011-2014 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 "CarlaEngineGraph.hpp"
  18. #include "CarlaEngineInternal.hpp"
  19. #include "CarlaPlugin.hpp"
  20. #include "CarlaMathUtils.hpp"
  21. #include "CarlaMIDI.h"
  22. // FIXME: update to new Juce API
  23. #if defined(__clang__)
  24. # pragma clang diagnostic push
  25. # pragma clang diagnostic ignored "-Wdeprecated-declarations"
  26. #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  27. # pragma GCC diagnostic push
  28. # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  29. #endif
  30. using juce::AudioBuffer;
  31. using juce::AudioPluginInstance;
  32. using juce::AudioProcessor;
  33. using juce::AudioProcessorEditor;
  34. using juce::FloatVectorOperations;
  35. using juce::MemoryBlock;
  36. using juce::PluginDescription;
  37. using juce::String;
  38. using juce::StringArray;
  39. using juce::jmin;
  40. using juce::jmax;
  41. CARLA_BACKEND_START_NAMESPACE
  42. // -----------------------------------------------------------------------
  43. // Fallback data
  44. static const PortNameToId kPortNameToIdFallback = { 0, 0, { '\0' }, { '\0' } };
  45. static /* */ PortNameToId kPortNameToIdFallbackNC = { 0, 0, { '\0' }, { '\0' } };
  46. // -----------------------------------------------------------------------
  47. // External Graph stuff
  48. static inline
  49. uint getExternalGraphPortIdFromName(const char* const shortname) noexcept
  50. {
  51. if (std::strcmp(shortname, "AudioIn1") == 0 || std::strcmp(shortname, "audio-in1") == 0)
  52. return kExternalGraphCarlaPortAudioIn1;
  53. if (std::strcmp(shortname, "AudioIn2") == 0 || std::strcmp(shortname, "audio-in2") == 0)
  54. return kExternalGraphCarlaPortAudioIn2;
  55. if (std::strcmp(shortname, "AudioOut1") == 0 || std::strcmp(shortname, "audio-out1") == 0)
  56. return kExternalGraphCarlaPortAudioOut1;
  57. if (std::strcmp(shortname, "AudioOut2") == 0 || std::strcmp(shortname, "audio-out2") == 0)
  58. return kExternalGraphCarlaPortAudioOut2;
  59. if (std::strcmp(shortname, "MidiIn") == 0 || std::strcmp(shortname, "midi-in") == 0)
  60. return kExternalGraphCarlaPortMidiIn;
  61. if (std::strcmp(shortname, "MidiOut") == 0 || std::strcmp(shortname, "midi-out") == 0)
  62. return kExternalGraphCarlaPortMidiOut;
  63. carla_stderr("CarlaBackend::getExternalGraphPortIdFromName(%s) - invalid short name", shortname);
  64. return kExternalGraphCarlaPortNull;
  65. }
  66. static inline
  67. const char* getExternalGraphFullPortNameFromId(const /*RackGraphCarlaPortIds*/ uint portId)
  68. {
  69. switch (portId)
  70. {
  71. case kExternalGraphCarlaPortAudioIn1:
  72. return "Carla:AudioIn1";
  73. case kExternalGraphCarlaPortAudioIn2:
  74. return "Carla:AudioIn2";
  75. case kExternalGraphCarlaPortAudioOut1:
  76. return "Carla:AudioOut1";
  77. case kExternalGraphCarlaPortAudioOut2:
  78. return "Carla:AudioOut2";
  79. case kExternalGraphCarlaPortMidiIn:
  80. return "Carla:MidiIn";
  81. case kExternalGraphCarlaPortMidiOut:
  82. return "Carla:MidiOut";
  83. //case kExternalGraphCarlaPortNull:
  84. //case kExternalGraphCarlaPortMax:
  85. // break;
  86. }
  87. carla_stderr("CarlaBackend::getExternalGraphFullPortNameFromId(%i) - invalid port id", portId);
  88. return nullptr;
  89. }
  90. // -----------------------------------------------------------------------
  91. ExternalGraphPorts::ExternalGraphPorts() noexcept
  92. : ins(),
  93. outs() {}
  94. const char* ExternalGraphPorts::getName(const bool isInput, const uint portId) const noexcept
  95. {
  96. for (LinkedList<PortNameToId>::Itenerator it = isInput ? ins.begin2() : outs.begin2(); it.valid(); it.next())
  97. {
  98. const PortNameToId& portNameToId(it.getValue(kPortNameToIdFallback));
  99. CARLA_SAFE_ASSERT_CONTINUE(portNameToId.group > 0);
  100. if (portNameToId.port == portId)
  101. return portNameToId.name;
  102. }
  103. return nullptr;
  104. }
  105. uint ExternalGraphPorts::getPortId(const bool isInput, const char portName[], bool* const ok) const noexcept
  106. {
  107. for (LinkedList<PortNameToId>::Itenerator it = isInput ? ins.begin2() : outs.begin2(); it.valid(); it.next())
  108. {
  109. const PortNameToId& portNameToId(it.getValue(kPortNameToIdFallback));
  110. CARLA_SAFE_ASSERT_CONTINUE(portNameToId.group > 0);
  111. if (std::strncmp(portNameToId.name, portName, STR_MAX) == 0)
  112. {
  113. if (ok != nullptr)
  114. *ok = true;
  115. return portNameToId.port;
  116. }
  117. }
  118. if (ok != nullptr)
  119. *ok = false;
  120. return 0;
  121. }
  122. // -----------------------------------------------------------------------
  123. ExternalGraph::ExternalGraph(CarlaEngine* const engine) noexcept
  124. : connections(),
  125. audioPorts(),
  126. midiPorts(),
  127. retCon(),
  128. kEngine(engine) {}
  129. void ExternalGraph::clear() noexcept
  130. {
  131. connections.clear();
  132. audioPorts.ins.clear();
  133. audioPorts.outs.clear();
  134. midiPorts.ins.clear();
  135. midiPorts.outs.clear();
  136. }
  137. bool ExternalGraph::connect(const uint groupA, const uint portA, const uint groupB, const uint portB, const bool sendCallback) noexcept
  138. {
  139. uint otherGroup, otherPort, carlaPort;
  140. if (groupA == kExternalGraphGroupCarla)
  141. {
  142. CARLA_SAFE_ASSERT_RETURN(groupB != kExternalGraphGroupCarla, false);
  143. carlaPort = portA;
  144. otherGroup = groupB;
  145. otherPort = portB;
  146. }
  147. else
  148. {
  149. CARLA_SAFE_ASSERT_RETURN(groupB == kExternalGraphGroupCarla, false);
  150. carlaPort = portB;
  151. otherGroup = groupA;
  152. otherPort = portA;
  153. }
  154. CARLA_SAFE_ASSERT_RETURN(carlaPort > kExternalGraphCarlaPortNull && carlaPort < kExternalGraphCarlaPortMax, false);
  155. CARLA_SAFE_ASSERT_RETURN(otherGroup > kExternalGraphGroupCarla && otherGroup < kExternalGraphGroupMax, false);
  156. bool makeConnection = false;
  157. switch (carlaPort)
  158. {
  159. case kExternalGraphCarlaPortAudioIn1:
  160. CARLA_SAFE_ASSERT_RETURN(otherGroup == kExternalGraphGroupAudioIn, false);
  161. makeConnection = kEngine->connectExternalGraphPort(kExternalGraphConnectionAudioIn1, otherPort, nullptr);
  162. break;
  163. case kExternalGraphCarlaPortAudioIn2:
  164. CARLA_SAFE_ASSERT_RETURN(otherGroup == kExternalGraphGroupAudioIn, false);
  165. makeConnection = kEngine->connectExternalGraphPort(kExternalGraphConnectionAudioIn2, otherPort, nullptr);
  166. break;
  167. case kExternalGraphCarlaPortAudioOut1:
  168. CARLA_SAFE_ASSERT_RETURN(otherGroup == kExternalGraphGroupAudioOut, false);
  169. makeConnection = kEngine->connectExternalGraphPort(kExternalGraphConnectionAudioOut1, otherPort, nullptr);
  170. break;
  171. case kExternalGraphCarlaPortAudioOut2:
  172. CARLA_SAFE_ASSERT_RETURN(otherGroup == kExternalGraphGroupAudioOut, false);
  173. makeConnection = kEngine->connectExternalGraphPort(kExternalGraphConnectionAudioOut2, otherPort, nullptr);
  174. break;
  175. case kExternalGraphCarlaPortMidiIn:
  176. CARLA_SAFE_ASSERT_RETURN(otherGroup == kExternalGraphGroupMidiIn, false);
  177. if (const char* const portName = midiPorts.getName(true, otherPort))
  178. makeConnection = kEngine->connectExternalGraphPort(kExternalGraphConnectionMidiInput, 0, portName);
  179. break;
  180. case kExternalGraphCarlaPortMidiOut:
  181. CARLA_SAFE_ASSERT_RETURN(otherGroup == kExternalGraphGroupMidiOut, false);
  182. if (const char* const portName = midiPorts.getName(false, otherPort))
  183. makeConnection = kEngine->connectExternalGraphPort(kExternalGraphConnectionMidiOutput, 0, portName);
  184. break;
  185. }
  186. if (! makeConnection)
  187. {
  188. kEngine->setLastError("Invalid rack connection");
  189. return false;
  190. }
  191. ConnectionToId connectionToId;
  192. connectionToId.setData(++connections.lastId, groupA, portA, groupB, portB);
  193. char strBuf[STR_MAX+1];
  194. strBuf[STR_MAX] = '\0';
  195. std::snprintf(strBuf, STR_MAX, "%u:%u:%u:%u", groupA, portA, groupB, portB);
  196. if (sendCallback)
  197. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  198. connections.list.append(connectionToId);
  199. return true;
  200. }
  201. bool ExternalGraph::disconnect(const uint connectionId) noexcept
  202. {
  203. CARLA_SAFE_ASSERT_RETURN(connections.list.count() > 0, false);
  204. for (LinkedList<ConnectionToId>::Itenerator it=connections.list.begin2(); it.valid(); it.next())
  205. {
  206. static const ConnectionToId fallback = { 0, 0, 0, 0, 0 };
  207. const ConnectionToId& connectionToId(it.getValue(fallback));
  208. CARLA_SAFE_ASSERT_CONTINUE(connectionToId.id > 0);
  209. if (connectionToId.id != connectionId)
  210. continue;
  211. uint otherGroup, otherPort, carlaPort;
  212. if (connectionToId.groupA == kExternalGraphGroupCarla)
  213. {
  214. CARLA_SAFE_ASSERT_RETURN(connectionToId.groupB != kExternalGraphGroupCarla, false);
  215. carlaPort = connectionToId.portA;
  216. otherGroup = connectionToId.groupB;
  217. otherPort = connectionToId.portB;
  218. }
  219. else
  220. {
  221. CARLA_SAFE_ASSERT_RETURN(connectionToId.groupB == kExternalGraphGroupCarla, false);
  222. carlaPort = connectionToId.portB;
  223. otherGroup = connectionToId.groupA;
  224. otherPort = connectionToId.portA;
  225. }
  226. CARLA_SAFE_ASSERT_RETURN(carlaPort > kExternalGraphCarlaPortNull && carlaPort < kExternalGraphCarlaPortMax, false);
  227. CARLA_SAFE_ASSERT_RETURN(otherGroup > kExternalGraphGroupCarla && otherGroup < kExternalGraphGroupMax, false);
  228. bool makeDisconnection = false;
  229. switch (carlaPort)
  230. {
  231. case kExternalGraphCarlaPortAudioIn1:
  232. makeDisconnection = kEngine->disconnectExternalGraphPort(kExternalGraphConnectionAudioIn1, otherPort, nullptr);
  233. break;
  234. case kExternalGraphCarlaPortAudioIn2:
  235. makeDisconnection = kEngine->disconnectExternalGraphPort(kExternalGraphConnectionAudioIn2, otherPort, nullptr);
  236. break;
  237. case kExternalGraphCarlaPortAudioOut1:
  238. makeDisconnection = kEngine->disconnectExternalGraphPort(kExternalGraphConnectionAudioOut1, otherPort, nullptr);
  239. break;
  240. case kExternalGraphCarlaPortAudioOut2:
  241. makeDisconnection = kEngine->disconnectExternalGraphPort(kExternalGraphConnectionAudioOut2, otherPort, nullptr);
  242. break;
  243. case kExternalGraphCarlaPortMidiIn:
  244. if (const char* const portName = midiPorts.getName(true, otherPort))
  245. makeDisconnection = kEngine->disconnectExternalGraphPort(kExternalGraphConnectionMidiInput, 0, portName);
  246. break;
  247. case kExternalGraphCarlaPortMidiOut:
  248. if (const char* const portName = midiPorts.getName(false, otherPort))
  249. makeDisconnection = kEngine->disconnectExternalGraphPort(kExternalGraphConnectionMidiOutput, 0, portName);
  250. break;
  251. }
  252. if (! makeDisconnection)
  253. {
  254. kEngine->setLastError("Invalid rack connection");
  255. return false;
  256. }
  257. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED, connectionToId.id, 0, 0, 0.0f, nullptr);
  258. connections.list.remove(it);
  259. return true;
  260. }
  261. kEngine->setLastError("Failed to find connection");
  262. return false;
  263. }
  264. void ExternalGraph::refresh(const char* const deviceName)
  265. {
  266. CARLA_SAFE_ASSERT_RETURN(deviceName != nullptr,);
  267. const bool isRack(kEngine->getOptions().processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK);
  268. // Main
  269. {
  270. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, kExternalGraphGroupCarla, PATCHBAY_ICON_CARLA, -1, 0.0f, kEngine->getName());
  271. if (isRack)
  272. {
  273. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, kExternalGraphGroupCarla, kExternalGraphCarlaPortAudioIn1, PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, "audio-in1");
  274. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, kExternalGraphGroupCarla, kExternalGraphCarlaPortAudioIn2, PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, "audio-in2");
  275. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, kExternalGraphGroupCarla, kExternalGraphCarlaPortAudioOut1, PATCHBAY_PORT_TYPE_AUDIO, 0.0f, "audio-out1");
  276. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, kExternalGraphGroupCarla, kExternalGraphCarlaPortAudioOut2, PATCHBAY_PORT_TYPE_AUDIO, 0.0f, "audio-out2");
  277. }
  278. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, kExternalGraphGroupCarla, kExternalGraphCarlaPortMidiIn, PATCHBAY_PORT_TYPE_MIDI|PATCHBAY_PORT_IS_INPUT, 0.0f, "midi-in");
  279. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, kExternalGraphGroupCarla, kExternalGraphCarlaPortMidiOut, PATCHBAY_PORT_TYPE_MIDI, 0.0f, "midi-out");
  280. }
  281. char strBuf[STR_MAX+1];
  282. strBuf[STR_MAX] = '\0';
  283. if (isRack)
  284. {
  285. // Audio In
  286. if (deviceName[0] != '\0')
  287. std::snprintf(strBuf, STR_MAX, "Capture (%s)", deviceName);
  288. else
  289. std::strncpy(strBuf, "Capture", STR_MAX);
  290. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, kExternalGraphGroupAudioIn, PATCHBAY_ICON_HARDWARE, -1, 0.0f, strBuf);
  291. const CarlaString groupNameIn(strBuf);
  292. int h = 0;
  293. for (LinkedList<PortNameToId>::Itenerator it = audioPorts.ins.begin2(); it.valid(); it.next())
  294. {
  295. PortNameToId& portNameToId(it.getValue(kPortNameToIdFallbackNC));
  296. CARLA_SAFE_ASSERT_CONTINUE(portNameToId.group > 0);
  297. portNameToId.setFullName(groupNameIn + portNameToId.name);
  298. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, kExternalGraphGroupAudioIn, ++h,
  299. PATCHBAY_PORT_TYPE_AUDIO, 0.0f, portNameToId.name);
  300. }
  301. // Audio Out
  302. if (deviceName[0] != '\0')
  303. std::snprintf(strBuf, STR_MAX, "Playback (%s)", deviceName);
  304. else
  305. std::strncpy(strBuf, "Playback", STR_MAX);
  306. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, kExternalGraphGroupAudioOut, PATCHBAY_ICON_HARDWARE, -1, 0.0f, strBuf);
  307. const CarlaString groupNameOut(strBuf);
  308. h = 0;
  309. for (LinkedList<PortNameToId>::Itenerator it = audioPorts.outs.begin2(); it.valid(); it.next())
  310. {
  311. PortNameToId& portNameToId(it.getValue(kPortNameToIdFallbackNC));
  312. CARLA_SAFE_ASSERT_CONTINUE(portNameToId.group > 0);
  313. portNameToId.setFullName(groupNameOut + portNameToId.name);
  314. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, kExternalGraphGroupAudioOut, ++h,
  315. PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, portNameToId.name);
  316. }
  317. }
  318. // MIDI In
  319. {
  320. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, kExternalGraphGroupMidiIn, PATCHBAY_ICON_HARDWARE, -1, 0.0f, "Readable MIDI ports");
  321. const CarlaString groupNamePlus("Readable MIDI ports:");
  322. int h = 0;
  323. for (LinkedList<PortNameToId>::Itenerator it = midiPorts.ins.begin2(); it.valid(); it.next())
  324. {
  325. PortNameToId& portNameToId(it.getValue(kPortNameToIdFallbackNC));
  326. CARLA_SAFE_ASSERT_CONTINUE(portNameToId.group > 0);
  327. portNameToId.setFullName(groupNamePlus + portNameToId.name);
  328. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, kExternalGraphGroupMidiIn, ++h,
  329. PATCHBAY_PORT_TYPE_MIDI, 0.0f, portNameToId.name);
  330. }
  331. }
  332. // MIDI Out
  333. {
  334. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, kExternalGraphGroupMidiOut, PATCHBAY_ICON_HARDWARE, -1, 0.0f, "Writable MIDI ports");
  335. const CarlaString groupNamePlus("Writable MIDI ports:");
  336. int h = 0;
  337. for (LinkedList<PortNameToId>::Itenerator it = midiPorts.outs.begin2(); it.valid(); it.next())
  338. {
  339. PortNameToId& portNameToId(it.getValue(kPortNameToIdFallbackNC));
  340. CARLA_SAFE_ASSERT_CONTINUE(portNameToId.group > 0);
  341. portNameToId.setFullName(groupNamePlus + portNameToId.name);
  342. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, kExternalGraphGroupMidiOut, ++h,
  343. PATCHBAY_PORT_TYPE_MIDI|PATCHBAY_PORT_IS_INPUT, 0.0f, portNameToId.name);
  344. }
  345. }
  346. }
  347. const char* const* ExternalGraph::getConnections() const noexcept
  348. {
  349. if (connections.list.count() == 0)
  350. return nullptr;
  351. CarlaStringList connList;
  352. char strBuf[STR_MAX+1];
  353. strBuf[STR_MAX] = '\0';
  354. for (LinkedList<ConnectionToId>::Itenerator it=connections.list.begin2(); it.valid(); it.next())
  355. {
  356. static const ConnectionToId fallback = { 0, 0, 0, 0, 0 };
  357. const ConnectionToId& connectionToId(it.getValue(fallback));
  358. CARLA_SAFE_ASSERT_CONTINUE(connectionToId.id > 0);
  359. uint otherGroup, otherPort, carlaPort;
  360. if (connectionToId.groupA == kExternalGraphGroupCarla)
  361. {
  362. CARLA_SAFE_ASSERT_CONTINUE(connectionToId.groupB != kExternalGraphGroupCarla);
  363. carlaPort = connectionToId.portA;
  364. otherGroup = connectionToId.groupB;
  365. otherPort = connectionToId.portB;
  366. }
  367. else
  368. {
  369. CARLA_SAFE_ASSERT_CONTINUE(connectionToId.groupB == kExternalGraphGroupCarla);
  370. carlaPort = connectionToId.portB;
  371. otherGroup = connectionToId.groupA;
  372. otherPort = connectionToId.portA;
  373. }
  374. CARLA_SAFE_ASSERT_CONTINUE(carlaPort > kExternalGraphCarlaPortNull && carlaPort < kExternalGraphCarlaPortMax);
  375. CARLA_SAFE_ASSERT_CONTINUE(otherGroup > kExternalGraphGroupCarla && otherGroup < kExternalGraphGroupMax);
  376. switch (carlaPort)
  377. {
  378. case kExternalGraphCarlaPortAudioIn1:
  379. case kExternalGraphCarlaPortAudioIn2:
  380. std::snprintf(strBuf, STR_MAX, "AudioIn:%s", audioPorts.getName(true, otherPort));
  381. connList.append(strBuf);
  382. connList.append(getExternalGraphFullPortNameFromId(carlaPort));
  383. break;
  384. case kExternalGraphCarlaPortAudioOut1:
  385. case kExternalGraphCarlaPortAudioOut2:
  386. std::snprintf(strBuf, STR_MAX, "AudioOut:%s", audioPorts.getName(false, otherPort));
  387. connList.append(getExternalGraphFullPortNameFromId(carlaPort));
  388. connList.append(strBuf);
  389. break;
  390. case kExternalGraphCarlaPortMidiIn:
  391. std::snprintf(strBuf, STR_MAX, "MidiIn:%s", midiPorts.getName(true, otherPort));
  392. connList.append(strBuf);
  393. connList.append(getExternalGraphFullPortNameFromId(carlaPort));
  394. break;
  395. case kExternalGraphCarlaPortMidiOut:
  396. std::snprintf(strBuf, STR_MAX, "MidiOut:%s", midiPorts.getName(false, otherPort));
  397. connList.append(getExternalGraphFullPortNameFromId(carlaPort));
  398. connList.append(strBuf);
  399. break;
  400. }
  401. }
  402. if (connList.count() == 0)
  403. return nullptr;
  404. retCon = connList.toCharStringListPtr();
  405. return retCon;
  406. }
  407. bool ExternalGraph::getGroupAndPortIdFromFullName(const char* const fullPortName, uint& groupId, uint& portId) const noexcept
  408. {
  409. CARLA_SAFE_ASSERT_RETURN(fullPortName != nullptr && fullPortName[0] != '\0', false);
  410. if (std::strncmp(fullPortName, "Carla:", 6) == 0)
  411. {
  412. groupId = kExternalGraphGroupCarla;
  413. portId = getExternalGraphPortIdFromName(fullPortName+6);
  414. if (portId > kExternalGraphCarlaPortNull && portId < kExternalGraphCarlaPortMax)
  415. return true;
  416. }
  417. else if (std::strncmp(fullPortName, "AudioIn:", 8) == 0)
  418. {
  419. groupId = kExternalGraphGroupAudioIn;
  420. if (const char* const portName = fullPortName+8)
  421. {
  422. bool ok;
  423. portId = audioPorts.getPortId(true, portName, &ok);
  424. return ok;
  425. }
  426. }
  427. else if (std::strncmp(fullPortName, "AudioOut:", 9) == 0)
  428. {
  429. groupId = kExternalGraphGroupAudioOut;
  430. if (const char* const portName = fullPortName+9)
  431. {
  432. bool ok;
  433. portId = audioPorts.getPortId(false, portName, &ok);
  434. return ok;
  435. }
  436. }
  437. else if (std::strncmp(fullPortName, "MidiIn:", 7) == 0)
  438. {
  439. groupId = kExternalGraphGroupMidiIn;
  440. if (const char* const portName = fullPortName+7)
  441. {
  442. bool ok;
  443. portId = midiPorts.getPortId(true, portName, &ok);
  444. return ok;
  445. }
  446. }
  447. else if (std::strncmp(fullPortName, "MidiOut:", 8) == 0)
  448. {
  449. groupId = kExternalGraphGroupMidiOut;
  450. if (const char* const portName = fullPortName+8)
  451. {
  452. bool ok;
  453. portId = midiPorts.getPortId(false, portName, &ok);
  454. return ok;
  455. }
  456. }
  457. return false;
  458. }
  459. // -----------------------------------------------------------------------
  460. // RackGraph Buffers
  461. RackGraph::Buffers::Buffers() noexcept
  462. : mutex(),
  463. connectedIn1(),
  464. connectedIn2(),
  465. connectedOut1(),
  466. connectedOut2()
  467. #ifdef CARLA_PROPER_CPP11_SUPPORT
  468. , inBuf{nullptr, nullptr},
  469. inBufTmp{nullptr, nullptr},
  470. outBuf{nullptr, nullptr} {}
  471. #else
  472. {
  473. inBuf[0] = inBuf[1] = nullptr;
  474. inBufTmp[0] = inBufTmp[1] = nullptr;
  475. outBuf[0] = outBuf[1] = nullptr;
  476. }
  477. #endif
  478. RackGraph::Buffers::~Buffers() noexcept
  479. {
  480. const CarlaRecursiveMutexLocker cml(mutex);
  481. if (inBuf[0] != nullptr) { delete[] inBuf[0]; inBuf[0] = nullptr; }
  482. if (inBuf[1] != nullptr) { delete[] inBuf[1]; inBuf[1] = nullptr; }
  483. if (inBufTmp[0] != nullptr) { delete[] inBufTmp[0]; inBufTmp[0] = nullptr; }
  484. if (inBufTmp[1] != nullptr) { delete[] inBufTmp[1]; inBufTmp[1] = nullptr; }
  485. if (outBuf[0] != nullptr) { delete[] outBuf[0]; outBuf[0] = nullptr; }
  486. if (outBuf[1] != nullptr) { delete[] outBuf[1]; outBuf[1] = nullptr; }
  487. connectedIn1.clear();
  488. connectedIn2.clear();
  489. connectedOut1.clear();
  490. connectedOut2.clear();
  491. }
  492. void RackGraph::Buffers::setBufferSize(const uint32_t bufferSize, const bool createBuffers) noexcept
  493. {
  494. const int bufferSizei(static_cast<int>(bufferSize));
  495. const CarlaRecursiveMutexLocker cml(mutex);
  496. if (inBuf[0] != nullptr) { delete[] inBuf[0]; inBuf[0] = nullptr; }
  497. if (inBuf[1] != nullptr) { delete[] inBuf[1]; inBuf[1] = nullptr; }
  498. if (inBufTmp[0] != nullptr) { delete[] inBufTmp[0]; inBufTmp[0] = nullptr; }
  499. if (inBufTmp[1] != nullptr) { delete[] inBufTmp[1]; inBufTmp[1] = nullptr; }
  500. if (outBuf[0] != nullptr) { delete[] outBuf[0]; outBuf[0] = nullptr; }
  501. if (outBuf[1] != nullptr) { delete[] outBuf[1]; outBuf[1] = nullptr; }
  502. CARLA_SAFE_ASSERT_RETURN(bufferSize > 0,);
  503. try {
  504. inBufTmp[0] = new float[bufferSize];
  505. inBufTmp[1] = new float[bufferSize];
  506. if (createBuffers)
  507. {
  508. inBuf[0] = new float[bufferSize];
  509. inBuf[1] = new float[bufferSize];
  510. outBuf[0] = new float[bufferSize];
  511. outBuf[1] = new float[bufferSize];
  512. }
  513. }
  514. catch(...) {
  515. if (inBufTmp[0] != nullptr) { delete[] inBufTmp[0]; inBufTmp[0] = nullptr; }
  516. if (inBufTmp[1] != nullptr) { delete[] inBufTmp[1]; inBufTmp[1] = nullptr; }
  517. if (createBuffers)
  518. {
  519. if (inBuf[0] != nullptr) { delete[] inBuf[0]; inBuf[0] = nullptr; }
  520. if (inBuf[1] != nullptr) { delete[] inBuf[1]; inBuf[1] = nullptr; }
  521. if (outBuf[0] != nullptr) { delete[] outBuf[0]; outBuf[0] = nullptr; }
  522. if (outBuf[1] != nullptr) { delete[] outBuf[1]; outBuf[1] = nullptr; }
  523. }
  524. return;
  525. }
  526. FloatVectorOperations::clear(inBufTmp[0], bufferSizei);
  527. FloatVectorOperations::clear(inBufTmp[1], bufferSizei);
  528. if (createBuffers)
  529. {
  530. FloatVectorOperations::clear(inBuf[0], bufferSizei);
  531. FloatVectorOperations::clear(inBuf[1], bufferSizei);
  532. FloatVectorOperations::clear(outBuf[0], bufferSizei);
  533. FloatVectorOperations::clear(outBuf[1], bufferSizei);
  534. }
  535. }
  536. // -----------------------------------------------------------------------
  537. // RackGraph
  538. RackGraph::RackGraph(CarlaEngine* const engine, const uint32_t ins, const uint32_t outs) noexcept
  539. : extGraph(engine),
  540. inputs(ins),
  541. outputs(outs),
  542. isOffline(false),
  543. audioBuffers(),
  544. kEngine(engine)
  545. {
  546. setBufferSize(engine->getBufferSize());
  547. }
  548. RackGraph::~RackGraph() noexcept
  549. {
  550. extGraph.clear();
  551. }
  552. void RackGraph::setBufferSize(const uint32_t bufferSize) noexcept
  553. {
  554. audioBuffers.setBufferSize(bufferSize, (inputs > 0 || outputs > 0));
  555. }
  556. void RackGraph::setOffline(const bool offline) noexcept
  557. {
  558. isOffline = offline;
  559. }
  560. bool RackGraph::connect(const uint groupA, const uint portA, const uint groupB, const uint portB) noexcept
  561. {
  562. return extGraph.connect(groupA, portA, groupB, portB, true);
  563. }
  564. bool RackGraph::disconnect(const uint connectionId) noexcept
  565. {
  566. return extGraph.disconnect(connectionId);
  567. }
  568. void RackGraph::refresh(const char* const deviceName)
  569. {
  570. extGraph.refresh(deviceName);
  571. char strBuf[STR_MAX+1];
  572. strBuf[STR_MAX] = '\0';
  573. // Connections
  574. const CarlaRecursiveMutexLocker cml(audioBuffers.mutex);
  575. for (LinkedList<uint>::Itenerator it = audioBuffers.connectedIn1.begin2(); it.valid(); it.next())
  576. {
  577. const uint& portId(it.getValue(0));
  578. CARLA_SAFE_ASSERT_CONTINUE(portId > 0);
  579. CARLA_SAFE_ASSERT_CONTINUE(portId <= extGraph.audioPorts.ins.count());
  580. ConnectionToId connectionToId;
  581. connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupAudioIn, portId, kExternalGraphGroupCarla, kExternalGraphCarlaPortAudioIn1);
  582. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  583. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  584. extGraph.connections.list.append(connectionToId);
  585. }
  586. for (LinkedList<uint>::Itenerator it = audioBuffers.connectedIn2.begin2(); it.valid(); it.next())
  587. {
  588. const uint& portId(it.getValue(0));
  589. CARLA_SAFE_ASSERT_CONTINUE(portId > 0);
  590. CARLA_SAFE_ASSERT_CONTINUE(portId <= extGraph.audioPorts.ins.count());
  591. ConnectionToId connectionToId;
  592. connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupAudioIn, portId, kExternalGraphGroupCarla, kExternalGraphCarlaPortAudioIn2);
  593. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  594. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  595. extGraph.connections.list.append(connectionToId);
  596. }
  597. for (LinkedList<uint>::Itenerator it = audioBuffers.connectedOut1.begin2(); it.valid(); it.next())
  598. {
  599. const uint& portId(it.getValue(0));
  600. CARLA_SAFE_ASSERT_CONTINUE(portId > 0);
  601. CARLA_SAFE_ASSERT_CONTINUE(portId <= extGraph.audioPorts.outs.count());
  602. ConnectionToId connectionToId;
  603. connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupCarla, kExternalGraphCarlaPortAudioOut1, kExternalGraphGroupAudioOut, portId);
  604. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  605. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  606. extGraph.connections.list.append(connectionToId);
  607. }
  608. for (LinkedList<uint>::Itenerator it = audioBuffers.connectedOut2.begin2(); it.valid(); it.next())
  609. {
  610. const uint& portId(it.getValue(0));
  611. CARLA_SAFE_ASSERT_CONTINUE(portId > 0);
  612. CARLA_SAFE_ASSERT_CONTINUE(portId <= extGraph.audioPorts.outs.count());
  613. ConnectionToId connectionToId;
  614. connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupCarla, kExternalGraphCarlaPortAudioOut2, kExternalGraphGroupAudioOut, portId);
  615. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);
  616. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  617. extGraph.connections.list.append(connectionToId);
  618. }
  619. }
  620. const char* const* RackGraph::getConnections() const noexcept
  621. {
  622. return extGraph.getConnections();
  623. }
  624. bool RackGraph::getGroupAndPortIdFromFullName(const char* const fullPortName, uint& groupId, uint& portId) const noexcept
  625. {
  626. return extGraph.getGroupAndPortIdFromFullName(fullPortName, groupId, portId);
  627. }
  628. void RackGraph::process(CarlaEngine::ProtectedData* const data, const float* inBufReal[2], float* outBuf[2], const uint32_t frames)
  629. {
  630. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  631. CARLA_SAFE_ASSERT_RETURN(data->events.in != nullptr,);
  632. CARLA_SAFE_ASSERT_RETURN(data->events.out != nullptr,);
  633. const int iframes(static_cast<int>(frames));
  634. // safe copy
  635. float inBuf0[frames];
  636. float inBuf1[frames];
  637. const float* inBuf[2] = { inBuf0, inBuf1 };
  638. // initialize audio inputs
  639. FloatVectorOperations::copy(inBuf0, inBufReal[0], iframes);
  640. FloatVectorOperations::copy(inBuf1, inBufReal[1], iframes);
  641. // initialize audio outputs (zero)
  642. FloatVectorOperations::clear(outBuf[0], iframes);
  643. FloatVectorOperations::clear(outBuf[1], iframes);
  644. // initialize event outputs (zero)
  645. carla_zeroStructs(data->events.out, kMaxEngineEventInternalCount);
  646. uint32_t oldAudioInCount = 0;
  647. uint32_t oldAudioOutCount = 0;
  648. uint32_t oldMidiOutCount = 0;
  649. bool processed = false;
  650. juce::Range<float> range;
  651. // process plugins
  652. for (uint i=0; i < data->curPluginCount; ++i)
  653. {
  654. CarlaPlugin* const plugin = data->plugins[i].plugin;
  655. if (plugin == nullptr || ! plugin->isEnabled() || ! plugin->tryLock(isOffline))
  656. continue;
  657. if (processed)
  658. {
  659. // initialize audio inputs (from previous outputs)
  660. FloatVectorOperations::copy(inBuf0, outBuf[0], iframes);
  661. FloatVectorOperations::copy(inBuf1, outBuf[1], iframes);
  662. // initialize audio outputs (zero)
  663. FloatVectorOperations::clear(outBuf[0], iframes);
  664. FloatVectorOperations::clear(outBuf[1], iframes);
  665. // if plugin has no midi out, add previous events
  666. if (oldMidiOutCount == 0 && data->events.in[0].type != kEngineEventTypeNull)
  667. {
  668. if (data->events.out[0].type != kEngineEventTypeNull)
  669. {
  670. // TODO: carefully add to input, sorted events
  671. }
  672. // else nothing needed
  673. }
  674. else
  675. {
  676. // initialize event inputs from previous outputs
  677. carla_copyStructs(data->events.in, data->events.out, kMaxEngineEventInternalCount);
  678. // initialize event outputs (zero)
  679. carla_zeroStructs(data->events.out, kMaxEngineEventInternalCount);
  680. }
  681. }
  682. oldAudioInCount = plugin->getAudioInCount();
  683. oldAudioOutCount = plugin->getAudioOutCount();
  684. oldMidiOutCount = plugin->getMidiOutCount();
  685. // process
  686. plugin->initBuffers();
  687. plugin->process(inBuf, outBuf, nullptr, nullptr, frames);
  688. plugin->unlock();
  689. // if plugin has no audio inputs, add input buffer
  690. if (oldAudioInCount == 0)
  691. {
  692. FloatVectorOperations::add(outBuf[0], inBuf0, iframes);
  693. FloatVectorOperations::add(outBuf[1], inBuf1, iframes);
  694. }
  695. // if plugin only has 1 output, copy it to the 2nd
  696. if (oldAudioOutCount == 1)
  697. {
  698. FloatVectorOperations::copy(outBuf[1], outBuf[0], iframes);
  699. }
  700. // set peaks
  701. {
  702. EnginePluginData& pluginData(data->plugins[i]);
  703. if (oldAudioInCount > 0)
  704. {
  705. range = FloatVectorOperations::findMinAndMax(inBuf0, iframes);
  706. pluginData.insPeak[0] = carla_maxLimited<float>(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f);
  707. range = FloatVectorOperations::findMinAndMax(inBuf1, iframes);
  708. pluginData.insPeak[1] = carla_maxLimited<float>(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f);
  709. }
  710. else
  711. {
  712. pluginData.insPeak[0] = 0.0f;
  713. pluginData.insPeak[1] = 0.0f;
  714. }
  715. if (oldAudioOutCount > 0)
  716. {
  717. range = FloatVectorOperations::findMinAndMax(outBuf[0], iframes);
  718. pluginData.outsPeak[0] = carla_maxLimited<float>(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f);
  719. range = FloatVectorOperations::findMinAndMax(outBuf[1], iframes);
  720. pluginData.outsPeak[1] = carla_maxLimited<float>(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f);
  721. }
  722. else
  723. {
  724. pluginData.outsPeak[0] = 0.0f;
  725. pluginData.outsPeak[1] = 0.0f;
  726. }
  727. }
  728. processed = true;
  729. }
  730. }
  731. void RackGraph::processHelper(CarlaEngine::ProtectedData* const data, const float* const* const inBuf, float* const* const outBuf, const uint32_t frames)
  732. {
  733. CARLA_SAFE_ASSERT_RETURN(audioBuffers.outBuf[1] != nullptr,);
  734. const int iframes(static_cast<int>(frames));
  735. const CarlaRecursiveMutexLocker _cml(audioBuffers.mutex);
  736. if (inBuf != nullptr && inputs > 0)
  737. {
  738. bool noConnections = true;
  739. // connect input buffers
  740. for (LinkedList<uint>::Itenerator it = audioBuffers.connectedIn1.begin2(); it.valid(); it.next())
  741. {
  742. const uint& port(it.getValue(0));
  743. CARLA_SAFE_ASSERT_CONTINUE(port > 0);
  744. CARLA_SAFE_ASSERT_CONTINUE(port <= inputs);
  745. if (noConnections)
  746. {
  747. FloatVectorOperations::copy(audioBuffers.inBuf[0], inBuf[port], iframes);
  748. noConnections = false;
  749. }
  750. else
  751. {
  752. FloatVectorOperations::add(audioBuffers.inBuf[0], inBuf[port], iframes);
  753. }
  754. }
  755. if (noConnections)
  756. FloatVectorOperations::clear(audioBuffers.inBuf[0], iframes);
  757. noConnections = true;
  758. for (LinkedList<uint>::Itenerator it = audioBuffers.connectedIn2.begin2(); it.valid(); it.next())
  759. {
  760. const uint& port(it.getValue(0));
  761. CARLA_SAFE_ASSERT_CONTINUE(port > 0);
  762. CARLA_SAFE_ASSERT_CONTINUE(port <= inputs);
  763. if (noConnections)
  764. {
  765. FloatVectorOperations::copy(audioBuffers.inBuf[1], inBuf[port-1], iframes);
  766. noConnections = false;
  767. }
  768. else
  769. {
  770. FloatVectorOperations::add(audioBuffers.inBuf[1], inBuf[port-1], iframes);
  771. }
  772. }
  773. if (noConnections)
  774. FloatVectorOperations::clear(audioBuffers.inBuf[1], iframes);
  775. }
  776. else
  777. {
  778. FloatVectorOperations::clear(audioBuffers.inBuf[0], iframes);
  779. FloatVectorOperations::clear(audioBuffers.inBuf[1], iframes);
  780. }
  781. FloatVectorOperations::clear(audioBuffers.outBuf[0], iframes);
  782. FloatVectorOperations::clear(audioBuffers.outBuf[1], iframes);
  783. // process
  784. process(data, const_cast<const float**>(audioBuffers.inBuf), audioBuffers.outBuf, frames);
  785. // connect output buffers
  786. if (audioBuffers.connectedOut1.count() != 0)
  787. {
  788. for (LinkedList<uint>::Itenerator it = audioBuffers.connectedOut1.begin2(); it.valid(); it.next())
  789. {
  790. const uint& port(it.getValue(0));
  791. CARLA_SAFE_ASSERT_CONTINUE(port > 0);
  792. CARLA_SAFE_ASSERT_CONTINUE(port <= outputs);
  793. FloatVectorOperations::add(outBuf[port-1], audioBuffers.outBuf[0], iframes);
  794. }
  795. }
  796. if (audioBuffers.connectedOut2.count() != 0)
  797. {
  798. for (LinkedList<uint>::Itenerator it = audioBuffers.connectedOut2.begin2(); it.valid(); it.next())
  799. {
  800. const uint& port(it.getValue(0));
  801. CARLA_SAFE_ASSERT_CONTINUE(port > 0);
  802. CARLA_SAFE_ASSERT_CONTINUE(port <= outputs);
  803. FloatVectorOperations::add(outBuf[port-1], audioBuffers.outBuf[1], iframes);
  804. }
  805. }
  806. }
  807. // -----------------------------------------------------------------------
  808. // Patchbay Graph stuff
  809. static const uint32_t kAudioInputPortOffset = MAX_PATCHBAY_PLUGINS*1;
  810. static const uint32_t kAudioOutputPortOffset = MAX_PATCHBAY_PLUGINS*2;
  811. static const uint32_t kMidiInputPortOffset = MAX_PATCHBAY_PLUGINS*3;
  812. static const uint32_t kMidiOutputPortOffset = MAX_PATCHBAY_PLUGINS*3+1;
  813. static const uint kMidiChannelIndex = static_cast<uint>(CarlaAudioProcessorGraph::midiChannelIndex);
  814. static inline
  815. bool adjustPatchbayPortIdForJuce(uint& portId)
  816. {
  817. CARLA_SAFE_ASSERT_RETURN(portId >= kAudioInputPortOffset, false);
  818. CARLA_SAFE_ASSERT_RETURN(portId <= kMidiOutputPortOffset, false);
  819. if (portId == kMidiInputPortOffset)
  820. {
  821. portId = kMidiChannelIndex;
  822. return true;
  823. }
  824. if (portId == kMidiOutputPortOffset)
  825. {
  826. portId = kMidiChannelIndex;
  827. return true;
  828. }
  829. if (portId >= kAudioOutputPortOffset)
  830. {
  831. portId -= kAudioOutputPortOffset;
  832. return true;
  833. }
  834. if (portId >= kAudioInputPortOffset)
  835. {
  836. portId -= kAudioInputPortOffset;
  837. return true;
  838. }
  839. return false;
  840. }
  841. static inline
  842. const String getProcessorFullPortName(AudioProcessor* const proc, const uint32_t portId)
  843. {
  844. CARLA_SAFE_ASSERT_RETURN(proc != nullptr, String());
  845. CARLA_SAFE_ASSERT_RETURN(portId >= kAudioInputPortOffset, String());
  846. CARLA_SAFE_ASSERT_RETURN(portId <= kMidiOutputPortOffset, String());
  847. String fullPortName(proc->getName());
  848. if (portId == kMidiOutputPortOffset)
  849. {
  850. fullPortName += ":events-out";
  851. }
  852. else if (portId == kMidiInputPortOffset)
  853. {
  854. fullPortName += ":events-in";
  855. }
  856. else if (portId >= kAudioOutputPortOffset)
  857. {
  858. CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumOutputChannels() > 0, String());
  859. fullPortName += ":" + proc->getOutputChannelName(static_cast<int>(portId-kAudioOutputPortOffset));
  860. }
  861. else if (portId >= kAudioInputPortOffset)
  862. {
  863. CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumInputChannels() > 0, String());
  864. fullPortName += ":" + proc->getInputChannelName(static_cast<int>(portId-kAudioInputPortOffset));
  865. }
  866. else
  867. {
  868. return String();
  869. }
  870. return fullPortName;
  871. }
  872. static inline
  873. void addNodeToPatchbay(CarlaEngine* const engine, const uint32_t groupId, const int clientId, const AudioProcessor* const proc)
  874. {
  875. CARLA_SAFE_ASSERT_RETURN(engine != nullptr,);
  876. CARLA_SAFE_ASSERT_RETURN(proc != nullptr,);
  877. const int icon((clientId >= 0) ? PATCHBAY_ICON_PLUGIN : PATCHBAY_ICON_HARDWARE);
  878. engine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, groupId, icon, clientId, 0.0f, proc->getName().toRawUTF8());
  879. for (int i=0, numInputs=proc->getTotalNumInputChannels(); i<numInputs; ++i)
  880. {
  881. engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast<int>(kAudioInputPortOffset)+i,
  882. PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, proc->getInputChannelName(i).toRawUTF8());
  883. }
  884. for (int i=0, numOutputs=proc->getTotalNumOutputChannels(); i<numOutputs; ++i)
  885. {
  886. engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast<int>(kAudioOutputPortOffset)+i,
  887. PATCHBAY_PORT_TYPE_AUDIO, 0.0f, proc->getOutputChannelName(i).toRawUTF8());
  888. }
  889. if (proc->acceptsMidi())
  890. {
  891. engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast<int>(kMidiInputPortOffset),
  892. PATCHBAY_PORT_TYPE_MIDI|PATCHBAY_PORT_IS_INPUT, 0.0f, "events-in");
  893. }
  894. if (proc->producesMidi())
  895. {
  896. engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast<int>(kMidiOutputPortOffset),
  897. PATCHBAY_PORT_TYPE_MIDI, 0.0f, "events-out");
  898. }
  899. }
  900. static inline
  901. void removeNodeFromPatchbay(CarlaEngine* const engine, const uint32_t groupId, const AudioProcessor* const proc)
  902. {
  903. CARLA_SAFE_ASSERT_RETURN(engine != nullptr,);
  904. CARLA_SAFE_ASSERT_RETURN(proc != nullptr,);
  905. for (int i=0, numInputs=proc->getTotalNumInputChannels(); i<numInputs; ++i)
  906. {
  907. engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast<int>(kAudioInputPortOffset)+i,
  908. 0, 0.0f, nullptr);
  909. }
  910. for (int i=0, numOutputs=proc->getTotalNumOutputChannels(); i<numOutputs; ++i)
  911. {
  912. engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast<int>(kAudioOutputPortOffset)+i,
  913. 0, 0.0f, nullptr);
  914. }
  915. if (proc->acceptsMidi())
  916. {
  917. engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast<int>(kMidiInputPortOffset),
  918. 0, 0.0f, nullptr);
  919. }
  920. if (proc->producesMidi())
  921. {
  922. engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast<int>(kMidiOutputPortOffset),
  923. 0, 0.0f, nullptr);
  924. }
  925. engine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_REMOVED, groupId, 0, 0, 0.0f, nullptr);
  926. }
  927. // -----------------------------------------------------------------------
  928. class CarlaPluginInstance : public AudioPluginInstance
  929. {
  930. public:
  931. CarlaPluginInstance(CarlaEngine* const engine, CarlaPlugin* const plugin)
  932. : kEngine(engine),
  933. fPlugin(plugin)
  934. {
  935. setPlayConfigDetails(static_cast<int>(fPlugin->getAudioInCount()),
  936. static_cast<int>(fPlugin->getAudioOutCount()),
  937. getSampleRate(), getBlockSize());
  938. }
  939. ~CarlaPluginInstance() override
  940. {
  941. }
  942. void invalidatePlugin() noexcept
  943. {
  944. fPlugin = nullptr;
  945. }
  946. // -------------------------------------------------------------------
  947. void* getPlatformSpecificData() noexcept override
  948. {
  949. return fPlugin;
  950. }
  951. void fillInPluginDescription(PluginDescription& d) const override
  952. {
  953. d.pluginFormatName = "Carla";
  954. d.category = "Carla Plugin";
  955. d.version = "1.0";
  956. CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
  957. char strBuf[STR_MAX+1];
  958. strBuf[STR_MAX] = '\0';
  959. fPlugin->getRealName(strBuf);
  960. d.name = strBuf;
  961. fPlugin->getLabel(strBuf);
  962. d.descriptiveName = strBuf;
  963. fPlugin->getMaker(strBuf);
  964. d.manufacturerName = strBuf;
  965. d.uid = d.name.hashCode();
  966. d.isInstrument = (fPlugin->getHints() & PLUGIN_IS_SYNTH);
  967. d.numInputChannels = static_cast<int>(fPlugin->getAudioInCount());
  968. d.numOutputChannels = static_cast<int>(fPlugin->getAudioOutCount());
  969. //d.hasSharedContainer = true;
  970. }
  971. // -------------------------------------------------------------------
  972. const String getName() const override
  973. {
  974. return fPlugin->getName();
  975. }
  976. void processBlock(AudioBuffer<float>& audio, MidiBuffer& midi) override
  977. {
  978. if (fPlugin == nullptr || ! fPlugin->isEnabled())
  979. {
  980. audio.clear();
  981. midi.clear();
  982. return;
  983. }
  984. if (! fPlugin->tryLock(kEngine->isOffline()))
  985. {
  986. audio.clear();
  987. midi.clear();
  988. return;
  989. }
  990. fPlugin->initBuffers();
  991. if (CarlaEngineEventPort* const port = fPlugin->getDefaultEventInPort())
  992. {
  993. EngineEvent* const engineEvents(port->fBuffer);
  994. CARLA_SAFE_ASSERT_RETURN(engineEvents != nullptr,);
  995. carla_zeroStructs(engineEvents, kMaxEngineEventInternalCount);
  996. fillEngineEventsFromJuceMidiBuffer(engineEvents, midi);
  997. }
  998. midi.clear();
  999. // TODO - CV support
  1000. const int numSamples(audio.getNumSamples());
  1001. if (const int numChan = audio.getNumChannels())
  1002. {
  1003. if (fPlugin->getAudioInCount() == 0)
  1004. audio.clear();
  1005. float* audioBuffers[numChan];
  1006. for (int i=0; i<numChan; ++i)
  1007. audioBuffers[i] = audio.getWritePointer(i);
  1008. float inPeaks[2] = { 0.0f };
  1009. float outPeaks[2] = { 0.0f };
  1010. juce::Range<float> range;
  1011. for (int i=static_cast<int>(jmin(fPlugin->getAudioInCount(), 2U)); --i>=0;)
  1012. {
  1013. range = FloatVectorOperations::findMinAndMax(audioBuffers[i], numSamples);
  1014. inPeaks[i] = carla_maxLimited<float>(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f);
  1015. }
  1016. fPlugin->process(const_cast<const float**>(audioBuffers), audioBuffers, nullptr, nullptr, static_cast<uint32_t>(numSamples));
  1017. for (int i=static_cast<int>(jmin(fPlugin->getAudioOutCount(), 2U)); --i>=0;)
  1018. {
  1019. range = FloatVectorOperations::findMinAndMax(audioBuffers[i], numSamples);
  1020. outPeaks[i] = carla_maxLimited<float>(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f);
  1021. }
  1022. kEngine->setPluginPeaks(fPlugin->getId(), inPeaks, outPeaks);
  1023. }
  1024. else
  1025. {
  1026. fPlugin->process(nullptr, nullptr, nullptr, nullptr, static_cast<uint32_t>(numSamples));
  1027. }
  1028. midi.clear();
  1029. if (CarlaEngineEventPort* const port = fPlugin->getDefaultEventOutPort())
  1030. {
  1031. /*const*/ EngineEvent* const engineEvents(port->fBuffer);
  1032. CARLA_SAFE_ASSERT_RETURN(engineEvents != nullptr,);
  1033. fillJuceMidiBufferFromEngineEvents(midi, engineEvents);
  1034. carla_zeroStructs(engineEvents, kMaxEngineEventInternalCount);
  1035. }
  1036. fPlugin->unlock();
  1037. }
  1038. void processBlock(AudioBuffer<double>& audio, MidiBuffer& midi) override
  1039. {
  1040. ignoreUnused(audio, midi);
  1041. }
  1042. const String getInputChannelName(int i) const override
  1043. {
  1044. CARLA_SAFE_ASSERT_RETURN(i >= 0, String());
  1045. CarlaEngineClient* const client(fPlugin->getEngineClient());
  1046. return client->getAudioPortName(true, static_cast<uint>(i));
  1047. }
  1048. const String getOutputChannelName(int i) const override
  1049. {
  1050. CARLA_SAFE_ASSERT_RETURN(i >= 0, String());
  1051. CarlaEngineClient* const client(fPlugin->getEngineClient());
  1052. return client->getAudioPortName(false, static_cast<uint>(i));
  1053. }
  1054. void prepareToPlay(double, int) override {}
  1055. void releaseResources() override {}
  1056. const String getParameterName(int) override { return String(); }
  1057. String getParameterName(int, int) override { return String(); }
  1058. const String getParameterText(int) override { return String(); }
  1059. String getParameterText(int, int) override { return String(); }
  1060. const String getProgramName(int) override { return String(); }
  1061. double getTailLengthSeconds() const override { return 0.0; }
  1062. float getParameter(int) override { return 0.0f; }
  1063. bool isInputChannelStereoPair(int) const override { return false; }
  1064. bool isOutputChannelStereoPair(int) const override { return false; }
  1065. bool silenceInProducesSilenceOut() const override { return true; }
  1066. bool acceptsMidi() const override { return fPlugin->getDefaultEventInPort() != nullptr; }
  1067. bool producesMidi() const override { return fPlugin->getDefaultEventOutPort() != nullptr; }
  1068. void setParameter(int, float) override {}
  1069. void setCurrentProgram(int) override {}
  1070. void changeProgramName(int, const String&) override {}
  1071. void getStateInformation(MemoryBlock&) override {}
  1072. void setStateInformation(const void*, int) override {}
  1073. int getNumParameters() override { return 0; }
  1074. int getNumPrograms() override { return 0; }
  1075. int getCurrentProgram() override { return 0; }
  1076. #ifndef JUCE_AUDIO_PROCESSOR_NO_GUI
  1077. bool hasEditor() const override { return false; }
  1078. AudioProcessorEditor* createEditor() override { return nullptr; }
  1079. #endif
  1080. // -------------------------------------------------------------------
  1081. private:
  1082. CarlaEngine* const kEngine;
  1083. CarlaPlugin* fPlugin;
  1084. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginInstance)
  1085. };
  1086. // -----------------------------------------------------------------------
  1087. // Patchbay Graph
  1088. class NamedAudioGraphIOProcessor : public CarlaAudioProcessorGraph::AudioGraphIOProcessor
  1089. {
  1090. public:
  1091. NamedAudioGraphIOProcessor(const IODeviceType iotype)
  1092. : CarlaAudioProcessorGraph::AudioGraphIOProcessor(iotype),
  1093. inputNames(),
  1094. outputNames() {}
  1095. const String getInputChannelName (int index) const override
  1096. {
  1097. if (index < inputNames.size())
  1098. return inputNames[index];
  1099. return String("Playback ") + String(index+1);
  1100. }
  1101. const String getOutputChannelName (int index) const override
  1102. {
  1103. if (index < outputNames.size())
  1104. return outputNames[index];
  1105. return String("Capture ") + String(index+1);
  1106. }
  1107. void setNames(const bool setInputNames, const StringArray& names)
  1108. {
  1109. if (setInputNames)
  1110. inputNames = names;
  1111. else
  1112. outputNames = names;
  1113. }
  1114. private:
  1115. StringArray inputNames;
  1116. StringArray outputNames;
  1117. };
  1118. PatchbayGraph::PatchbayGraph(CarlaEngine* const engine, const uint32_t ins, const uint32_t outs)
  1119. : connections(),
  1120. graph(),
  1121. audioBuffer(),
  1122. midiBuffer(),
  1123. inputs(carla_fixedValue(0U, 32U, ins)),
  1124. outputs(carla_fixedValue(0U, 32U, outs)),
  1125. retCon(),
  1126. usingExternal(false),
  1127. extGraph(engine),
  1128. kEngine(engine)
  1129. {
  1130. const int bufferSize(static_cast<int>(engine->getBufferSize()));
  1131. const double sampleRate(engine->getSampleRate());
  1132. graph.setPlayConfigDetails(static_cast<int>(inputs), static_cast<int>(outputs), sampleRate, bufferSize);
  1133. graph.prepareToPlay(sampleRate, bufferSize);
  1134. audioBuffer.setSize(static_cast<int>(jmax(inputs, outputs)), bufferSize);
  1135. midiBuffer.ensureSize(kMaxEngineEventInternalCount*2);
  1136. midiBuffer.clear();
  1137. StringArray channelNames;
  1138. switch (inputs)
  1139. {
  1140. case 2:
  1141. channelNames = {
  1142. "Left",
  1143. "Right",
  1144. };
  1145. break;
  1146. case 3:
  1147. channelNames = {
  1148. "Left",
  1149. "Right",
  1150. "Sidechain",
  1151. };
  1152. break;
  1153. }
  1154. {
  1155. NamedAudioGraphIOProcessor* const proc(
  1156. new NamedAudioGraphIOProcessor(NamedAudioGraphIOProcessor::audioInputNode));
  1157. proc->setNames(false, channelNames);
  1158. CarlaAudioProcessorGraph::Node* const node(graph.addNode(proc));
  1159. node->properties.set("isPlugin", false);
  1160. node->properties.set("isOutput", false);
  1161. node->properties.set("isAudio", true);
  1162. node->properties.set("isCV", false);
  1163. node->properties.set("isMIDI", false);
  1164. node->properties.set("isOSC", false);
  1165. }
  1166. {
  1167. NamedAudioGraphIOProcessor* const proc(
  1168. new NamedAudioGraphIOProcessor(NamedAudioGraphIOProcessor::audioOutputNode));
  1169. proc->setNames(true, channelNames);
  1170. CarlaAudioProcessorGraph::Node* const node(graph.addNode(proc));
  1171. node->properties.set("isPlugin", false);
  1172. node->properties.set("isOutput", false);
  1173. node->properties.set("isAudio", true);
  1174. node->properties.set("isCV", false);
  1175. node->properties.set("isMIDI", false);
  1176. node->properties.set("isOSC", false);
  1177. }
  1178. {
  1179. NamedAudioGraphIOProcessor* const proc(
  1180. new NamedAudioGraphIOProcessor(NamedAudioGraphIOProcessor::midiInputNode));
  1181. CarlaAudioProcessorGraph::Node* const node(graph.addNode(proc));
  1182. node->properties.set("isPlugin", false);
  1183. node->properties.set("isOutput", false);
  1184. node->properties.set("isAudio", false);
  1185. node->properties.set("isCV", false);
  1186. node->properties.set("isMIDI", true);
  1187. node->properties.set("isOSC", false);
  1188. }
  1189. {
  1190. NamedAudioGraphIOProcessor* const proc(
  1191. new NamedAudioGraphIOProcessor(NamedAudioGraphIOProcessor::midiOutputNode));
  1192. CarlaAudioProcessorGraph::Node* const node(graph.addNode(proc));
  1193. node->properties.set("isPlugin", false);
  1194. node->properties.set("isOutput", true);
  1195. node->properties.set("isAudio", false);
  1196. node->properties.set("isCV", false);
  1197. node->properties.set("isMIDI", true);
  1198. node->properties.set("isOSC", false);
  1199. }
  1200. }
  1201. PatchbayGraph::~PatchbayGraph()
  1202. {
  1203. connections.clear();
  1204. extGraph.clear();
  1205. graph.releaseResources();
  1206. graph.clear();
  1207. audioBuffer.clear();
  1208. }
  1209. void PatchbayGraph::setBufferSize(const uint32_t bufferSize)
  1210. {
  1211. const int bufferSizei(static_cast<int>(bufferSize));
  1212. graph.releaseResources();
  1213. graph.prepareToPlay(kEngine->getSampleRate(), bufferSizei);
  1214. audioBuffer.setSize(audioBuffer.getNumChannels(), bufferSizei);
  1215. }
  1216. void PatchbayGraph::setSampleRate(const double sampleRate)
  1217. {
  1218. graph.releaseResources();
  1219. graph.prepareToPlay(sampleRate, static_cast<int>(kEngine->getBufferSize()));
  1220. }
  1221. void PatchbayGraph::setOffline(const bool offline)
  1222. {
  1223. graph.setNonRealtime(offline);
  1224. }
  1225. void PatchbayGraph::addPlugin(CarlaPlugin* const plugin)
  1226. {
  1227. CARLA_SAFE_ASSERT_RETURN(plugin != nullptr,);
  1228. carla_debug("PatchbayGraph::addPlugin(%p)", plugin);
  1229. CarlaPluginInstance* const instance(new CarlaPluginInstance(kEngine, plugin));
  1230. CarlaAudioProcessorGraph::Node* const node(graph.addNode(instance));
  1231. CARLA_SAFE_ASSERT_RETURN(node != nullptr,);
  1232. plugin->setPatchbayNodeId(node->nodeId);
  1233. node->properties.set("isPlugin", true);
  1234. node->properties.set("pluginId", static_cast<int>(plugin->getId()));
  1235. if (! usingExternal)
  1236. addNodeToPatchbay(plugin->getEngine(), node->nodeId, static_cast<int>(plugin->getId()), instance);
  1237. }
  1238. void PatchbayGraph::replacePlugin(CarlaPlugin* const oldPlugin, CarlaPlugin* const newPlugin)
  1239. {
  1240. CARLA_SAFE_ASSERT_RETURN(oldPlugin != nullptr,);
  1241. CARLA_SAFE_ASSERT_RETURN(newPlugin != nullptr,);
  1242. CARLA_SAFE_ASSERT_RETURN(oldPlugin != newPlugin,);
  1243. CARLA_SAFE_ASSERT_RETURN(oldPlugin->getId() == newPlugin->getId(),);
  1244. CarlaAudioProcessorGraph::Node* const oldNode(graph.getNodeForId(oldPlugin->getPatchbayNodeId()));
  1245. CARLA_SAFE_ASSERT_RETURN(oldNode != nullptr,);
  1246. if (! usingExternal)
  1247. {
  1248. disconnectInternalGroup(oldNode->nodeId);
  1249. removeNodeFromPatchbay(kEngine, oldNode->nodeId, oldNode->getProcessor());
  1250. }
  1251. ((CarlaPluginInstance*)oldNode->getProcessor())->invalidatePlugin();
  1252. graph.removeNode(oldNode->nodeId);
  1253. CarlaPluginInstance* const instance(new CarlaPluginInstance(kEngine, newPlugin));
  1254. CarlaAudioProcessorGraph::Node* const node(graph.addNode(instance));
  1255. CARLA_SAFE_ASSERT_RETURN(node != nullptr,);
  1256. newPlugin->setPatchbayNodeId(node->nodeId);
  1257. node->properties.set("isPlugin", true);
  1258. node->properties.set("pluginId", static_cast<int>(newPlugin->getId()));
  1259. if (! usingExternal)
  1260. addNodeToPatchbay(newPlugin->getEngine(), node->nodeId, static_cast<int>(newPlugin->getId()), instance);
  1261. }
  1262. void PatchbayGraph::renamePlugin(CarlaPlugin* const plugin, const char* const newName)
  1263. {
  1264. CARLA_SAFE_ASSERT_RETURN(plugin != nullptr,);
  1265. carla_debug("PatchbayGraph::renamePlugin(%p)", plugin, newName);
  1266. CarlaAudioProcessorGraph::Node* const node(graph.getNodeForId(plugin->getPatchbayNodeId()));
  1267. CARLA_SAFE_ASSERT_RETURN(node != nullptr,);
  1268. if (! usingExternal)
  1269. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_RENAMED, node->nodeId, 0, 0, 0.0f, newName);
  1270. }
  1271. void PatchbayGraph::removePlugin(CarlaPlugin* const plugin)
  1272. {
  1273. CARLA_SAFE_ASSERT_RETURN(plugin != nullptr,);
  1274. carla_debug("PatchbayGraph::removePlugin(%p)", plugin);
  1275. CarlaAudioProcessorGraph::Node* const node(graph.getNodeForId(plugin->getPatchbayNodeId()));
  1276. CARLA_SAFE_ASSERT_RETURN(node != nullptr,);
  1277. if (! usingExternal)
  1278. {
  1279. disconnectInternalGroup(node->nodeId);
  1280. removeNodeFromPatchbay(kEngine, node->nodeId, node->getProcessor());
  1281. }
  1282. ((CarlaPluginInstance*)node->getProcessor())->invalidatePlugin();
  1283. // Fix plugin Ids properties
  1284. for (uint i=plugin->getId()+1, count=kEngine->getCurrentPluginCount(); i<count; ++i)
  1285. {
  1286. CarlaPlugin* const plugin2(kEngine->getPlugin(i));
  1287. CARLA_SAFE_ASSERT_BREAK(plugin2 != nullptr);
  1288. if (CarlaAudioProcessorGraph::Node* const node2 = graph.getNodeForId(plugin2->getPatchbayNodeId()))
  1289. {
  1290. CARLA_SAFE_ASSERT_CONTINUE(node2->properties.getWithDefault("pluginId", -1) != juce::var(-1));
  1291. node2->properties.set("pluginId", static_cast<int>(i-1));
  1292. }
  1293. }
  1294. CARLA_SAFE_ASSERT_RETURN(graph.removeNode(node->nodeId),);
  1295. }
  1296. void PatchbayGraph::removeAllPlugins()
  1297. {
  1298. carla_debug("PatchbayGraph::removeAllPlugins()");
  1299. for (uint i=0, count=kEngine->getCurrentPluginCount(); i<count; ++i)
  1300. {
  1301. CarlaPlugin* const plugin(kEngine->getPlugin(i));
  1302. CARLA_SAFE_ASSERT_CONTINUE(plugin != nullptr);
  1303. CarlaAudioProcessorGraph::Node* const node(graph.getNodeForId(plugin->getPatchbayNodeId()));
  1304. CARLA_SAFE_ASSERT_CONTINUE(node != nullptr);
  1305. if (! usingExternal)
  1306. {
  1307. disconnectInternalGroup(node->nodeId);
  1308. removeNodeFromPatchbay(kEngine, node->nodeId, node->getProcessor());
  1309. }
  1310. ((CarlaPluginInstance*)node->getProcessor())->invalidatePlugin();
  1311. graph.removeNode(node->nodeId);
  1312. }
  1313. }
  1314. bool PatchbayGraph::connect(const bool external, const uint groupA, const uint portA, const uint groupB, const uint portB, const bool sendCallback)
  1315. {
  1316. if (external)
  1317. return extGraph.connect(groupA, portA, groupB, portB, sendCallback);
  1318. uint adjustedPortA = portA;
  1319. uint adjustedPortB = portB;
  1320. if (! adjustPatchbayPortIdForJuce(adjustedPortA))
  1321. return false;
  1322. if (! adjustPatchbayPortIdForJuce(adjustedPortB))
  1323. return false;
  1324. if (! graph.addConnection(groupA, static_cast<int>(adjustedPortA), groupB, static_cast<int>(adjustedPortB)))
  1325. {
  1326. kEngine->setLastError("Failed from juce");
  1327. return false;
  1328. }
  1329. ConnectionToId connectionToId;
  1330. connectionToId.setData(++connections.lastId, groupA, portA, groupB, portB);
  1331. char strBuf[STR_MAX+1];
  1332. strBuf[STR_MAX] = '\0';
  1333. std::snprintf(strBuf, STR_MAX, "%u:%u:%u:%u", groupA, portA, groupB, portB);
  1334. if (sendCallback)
  1335. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  1336. connections.list.append(connectionToId);
  1337. return true;
  1338. }
  1339. bool PatchbayGraph::disconnect(const uint connectionId)
  1340. {
  1341. if (usingExternal)
  1342. return extGraph.disconnect(connectionId);
  1343. for (LinkedList<ConnectionToId>::Itenerator it=connections.list.begin2(); it.valid(); it.next())
  1344. {
  1345. static const ConnectionToId fallback = { 0, 0, 0, 0, 0 };
  1346. const ConnectionToId& connectionToId(it.getValue(fallback));
  1347. CARLA_SAFE_ASSERT_CONTINUE(connectionToId.id > 0);
  1348. if (connectionToId.id != connectionId)
  1349. continue;
  1350. uint adjustedPortA = connectionToId.portA;
  1351. uint adjustedPortB = connectionToId.portB;
  1352. if (! adjustPatchbayPortIdForJuce(adjustedPortA))
  1353. return false;
  1354. if (! adjustPatchbayPortIdForJuce(adjustedPortB))
  1355. return false;
  1356. if (! graph.removeConnection(connectionToId.groupA, static_cast<int>(adjustedPortA),
  1357. connectionToId.groupB, static_cast<int>(adjustedPortB)))
  1358. return false;
  1359. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED, connectionToId.id, 0, 0, 0.0f, nullptr);
  1360. connections.list.remove(it);
  1361. return true;
  1362. }
  1363. kEngine->setLastError("Failed to find connection");
  1364. return false;
  1365. }
  1366. void PatchbayGraph::disconnectInternalGroup(const uint groupId) noexcept
  1367. {
  1368. CARLA_SAFE_ASSERT(! usingExternal);
  1369. for (LinkedList<ConnectionToId>::Itenerator it=connections.list.begin2(); it.valid(); it.next())
  1370. {
  1371. static const ConnectionToId fallback = { 0, 0, 0, 0, 0 };
  1372. const ConnectionToId& connectionToId(it.getValue(fallback));
  1373. CARLA_SAFE_ASSERT_CONTINUE(connectionToId.id > 0);
  1374. if (connectionToId.groupA != groupId && connectionToId.groupB != groupId)
  1375. continue;
  1376. /*
  1377. uint adjustedPortA = connectionToId.portA;
  1378. uint adjustedPortB = connectionToId.portB;
  1379. if (! adjustPatchbayPortIdForJuce(adjustedPortA))
  1380. return false;
  1381. if (! adjustPatchbayPortIdForJuce(adjustedPortB))
  1382. return false;
  1383. graph.removeConnection(connectionToId.groupA, static_cast<int>(adjustedPortA),
  1384. connectionToId.groupB, static_cast<int>(adjustedPortB));
  1385. */
  1386. if (! usingExternal)
  1387. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED, connectionToId.id, 0, 0, 0.0f, nullptr);
  1388. connections.list.remove(it);
  1389. }
  1390. }
  1391. void PatchbayGraph::refresh(const char* const deviceName)
  1392. {
  1393. if (usingExternal)
  1394. return extGraph.refresh(deviceName);
  1395. CARLA_SAFE_ASSERT_RETURN(deviceName != nullptr,);
  1396. connections.clear();
  1397. graph.removeIllegalConnections();
  1398. for (int i=0, count=graph.getNumNodes(); i<count; ++i)
  1399. {
  1400. CarlaAudioProcessorGraph::Node* const node(graph.getNode(i));
  1401. CARLA_SAFE_ASSERT_CONTINUE(node != nullptr);
  1402. AudioProcessor* const proc(node->getProcessor());
  1403. CARLA_SAFE_ASSERT_CONTINUE(proc != nullptr);
  1404. int clientId = -1;
  1405. // plugin node
  1406. if (node->properties.getWithDefault("isPlugin", false) == juce::var(true))
  1407. clientId = node->properties.getWithDefault("pluginId", -1);
  1408. addNodeToPatchbay(kEngine, node->nodeId, clientId, proc);
  1409. }
  1410. char strBuf[STR_MAX+1];
  1411. strBuf[STR_MAX] = '\0';
  1412. for (int i=0, count=graph.getNumConnections(); i<count; ++i)
  1413. {
  1414. const CarlaAudioProcessorGraph::Connection* const conn(graph.getConnection(i));
  1415. CARLA_SAFE_ASSERT_CONTINUE(conn != nullptr);
  1416. CARLA_SAFE_ASSERT_CONTINUE(conn->sourceChannelIndex >= 0);
  1417. CARLA_SAFE_ASSERT_CONTINUE(conn->destChannelIndex >= 0);
  1418. const uint groupA = conn->sourceNodeId;
  1419. const uint groupB = conn->destNodeId;
  1420. uint portA = static_cast<uint>(conn->sourceChannelIndex);
  1421. uint portB = static_cast<uint>(conn->destChannelIndex);
  1422. if (portA == kMidiChannelIndex)
  1423. portA = kMidiOutputPortOffset;
  1424. else
  1425. portA += kAudioOutputPortOffset;
  1426. if (portB == kMidiChannelIndex)
  1427. portB = kMidiInputPortOffset;
  1428. else
  1429. portB += kAudioInputPortOffset;
  1430. ConnectionToId connectionToId;
  1431. connectionToId.setData(++connections.lastId, groupA, portA, groupB, portB);
  1432. std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", groupA, portA, groupB, portB);
  1433. kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
  1434. connections.list.append(connectionToId);
  1435. }
  1436. }
  1437. const char* const* PatchbayGraph::getConnections(const bool external) const
  1438. {
  1439. if (external)
  1440. return extGraph.getConnections();
  1441. if (connections.list.count() == 0)
  1442. return nullptr;
  1443. CarlaStringList connList;
  1444. for (LinkedList<ConnectionToId>::Itenerator it=connections.list.begin2(); it.valid(); it.next())
  1445. {
  1446. static const ConnectionToId fallback = { 0, 0, 0, 0, 0 };
  1447. const ConnectionToId& connectionToId(it.getValue(fallback));
  1448. CARLA_SAFE_ASSERT_CONTINUE(connectionToId.id > 0);
  1449. CarlaAudioProcessorGraph::Node* const nodeA(graph.getNodeForId(connectionToId.groupA));
  1450. CARLA_SAFE_ASSERT_CONTINUE(nodeA != nullptr);
  1451. CarlaAudioProcessorGraph::Node* const nodeB(graph.getNodeForId(connectionToId.groupB));
  1452. CARLA_SAFE_ASSERT_CONTINUE(nodeB != nullptr);
  1453. AudioProcessor* const procA(nodeA->getProcessor());
  1454. CARLA_SAFE_ASSERT_CONTINUE(procA != nullptr);
  1455. AudioProcessor* const procB(nodeB->getProcessor());
  1456. CARLA_SAFE_ASSERT_CONTINUE(procB != nullptr);
  1457. String fullPortNameA(getProcessorFullPortName(procA, connectionToId.portA));
  1458. CARLA_SAFE_ASSERT_CONTINUE(fullPortNameA.isNotEmpty());
  1459. String fullPortNameB(getProcessorFullPortName(procB, connectionToId.portB));
  1460. CARLA_SAFE_ASSERT_CONTINUE(fullPortNameB.isNotEmpty());
  1461. connList.append(fullPortNameA.toRawUTF8());
  1462. connList.append(fullPortNameB.toRawUTF8());
  1463. }
  1464. if (connList.count() == 0)
  1465. return nullptr;
  1466. retCon = connList.toCharStringListPtr();
  1467. return retCon;
  1468. }
  1469. bool PatchbayGraph::getGroupAndPortIdFromFullName(const bool external, const char* const fullPortName, uint& groupId, uint& portId) const
  1470. {
  1471. if (external)
  1472. return extGraph.getGroupAndPortIdFromFullName(fullPortName, groupId, portId);
  1473. String groupName(String(fullPortName).upToFirstOccurrenceOf(":", false, false));
  1474. String portName(String(fullPortName).fromFirstOccurrenceOf(":", false, false));
  1475. for (int i=0, count=graph.getNumNodes(); i<count; ++i)
  1476. {
  1477. CarlaAudioProcessorGraph::Node* const node(graph.getNode(i));
  1478. CARLA_SAFE_ASSERT_CONTINUE(node != nullptr);
  1479. AudioProcessor* const proc(node->getProcessor());
  1480. CARLA_SAFE_ASSERT_CONTINUE(proc != nullptr);
  1481. if (proc->getName() != groupName)
  1482. continue;
  1483. groupId = node->nodeId;
  1484. if (portName == "events-in")
  1485. {
  1486. portId = kMidiInputPortOffset;
  1487. return true;
  1488. }
  1489. if (portName == "events-out")
  1490. {
  1491. portId = kMidiOutputPortOffset;
  1492. return true;
  1493. }
  1494. for (int j=0, numInputs=proc->getTotalNumInputChannels(); j<numInputs; ++j)
  1495. {
  1496. if (proc->getInputChannelName(j) != portName)
  1497. continue;
  1498. portId = kAudioInputPortOffset+static_cast<uint>(j);
  1499. return true;
  1500. }
  1501. for (int j=0, numOutputs=proc->getTotalNumOutputChannels(); j<numOutputs; ++j)
  1502. {
  1503. if (proc->getOutputChannelName(j) != portName)
  1504. continue;
  1505. portId = kAudioOutputPortOffset+static_cast<uint>(j);
  1506. return true;
  1507. }
  1508. }
  1509. return false;
  1510. }
  1511. void PatchbayGraph::process(CarlaEngine::ProtectedData* const data, const float* const* const inBuf, float* const* const outBuf, const int frames)
  1512. {
  1513. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  1514. CARLA_SAFE_ASSERT_RETURN(data->events.in != nullptr,);
  1515. CARLA_SAFE_ASSERT_RETURN(data->events.out != nullptr,);
  1516. CARLA_SAFE_ASSERT_RETURN(frames > 0,);
  1517. // put events in juce buffer
  1518. {
  1519. midiBuffer.clear();
  1520. fillJuceMidiBufferFromEngineEvents(midiBuffer, data->events.in);
  1521. }
  1522. // put carla audio in juce buffer
  1523. {
  1524. int i=0;
  1525. for (; i < static_cast<int>(inputs); ++i)
  1526. FloatVectorOperations::copy(audioBuffer.getWritePointer(i), inBuf[i], frames);
  1527. // clear remaining channels
  1528. for (const int count=audioBuffer.getNumChannels(); i<count; ++i)
  1529. audioBuffer.clear(i, 0, frames);
  1530. }
  1531. graph.processBlock(audioBuffer, midiBuffer);
  1532. // put juce audio in carla buffer
  1533. {
  1534. for (int i=0; i < static_cast<int>(outputs); ++i)
  1535. FloatVectorOperations::copy(outBuf[i], audioBuffer.getReadPointer(i), frames);
  1536. }
  1537. // put juce events in carla buffer
  1538. {
  1539. carla_zeroStructs(data->events.out, kMaxEngineEventInternalCount);
  1540. fillEngineEventsFromJuceMidiBuffer(data->events.out, midiBuffer);
  1541. midiBuffer.clear();
  1542. }
  1543. }
  1544. // -----------------------------------------------------------------------
  1545. // InternalGraph
  1546. EngineInternalGraph::EngineInternalGraph(CarlaEngine* const engine) noexcept
  1547. : fIsRack(true),
  1548. fIsReady(false),
  1549. kEngine(engine)
  1550. {
  1551. fRack = nullptr;
  1552. }
  1553. EngineInternalGraph::~EngineInternalGraph() noexcept
  1554. {
  1555. CARLA_SAFE_ASSERT(! fIsReady);
  1556. CARLA_SAFE_ASSERT(fRack == nullptr);
  1557. }
  1558. void EngineInternalGraph::create(const uint32_t inputs, const uint32_t outputs)
  1559. {
  1560. fIsRack = (kEngine->getOptions().processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK);
  1561. if (fIsRack)
  1562. {
  1563. CARLA_SAFE_ASSERT_RETURN(fRack == nullptr,);
  1564. fRack = new RackGraph(kEngine, inputs, outputs);
  1565. }
  1566. else
  1567. {
  1568. CARLA_SAFE_ASSERT_RETURN(fPatchbay == nullptr,);
  1569. fPatchbay = new PatchbayGraph(kEngine, inputs, outputs);
  1570. }
  1571. fIsReady = true;
  1572. }
  1573. void EngineInternalGraph::destroy() noexcept
  1574. {
  1575. if (! fIsReady)
  1576. {
  1577. CARLA_SAFE_ASSERT(fRack == nullptr);
  1578. return;
  1579. }
  1580. if (fIsRack)
  1581. {
  1582. CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
  1583. delete fRack;
  1584. fRack = nullptr;
  1585. }
  1586. else
  1587. {
  1588. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
  1589. delete fPatchbay;
  1590. fPatchbay = nullptr;
  1591. }
  1592. fIsReady = false;
  1593. }
  1594. void EngineInternalGraph::setBufferSize(const uint32_t bufferSize)
  1595. {
  1596. ScopedValueSetter<bool> svs(fIsReady, false, true);
  1597. if (fIsRack)
  1598. {
  1599. CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
  1600. fRack->setBufferSize(bufferSize);
  1601. }
  1602. else
  1603. {
  1604. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
  1605. fPatchbay->setBufferSize(bufferSize);
  1606. }
  1607. }
  1608. void EngineInternalGraph::setSampleRate(const double sampleRate)
  1609. {
  1610. ScopedValueSetter<bool> svs(fIsReady, false, true);
  1611. if (fIsRack)
  1612. {
  1613. CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
  1614. }
  1615. else
  1616. {
  1617. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
  1618. fPatchbay->setSampleRate(sampleRate);
  1619. }
  1620. }
  1621. void EngineInternalGraph::setOffline(const bool offline)
  1622. {
  1623. ScopedValueSetter<bool> svs(fIsReady, false, true);
  1624. if (fIsRack)
  1625. {
  1626. CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
  1627. fRack->setOffline(offline);
  1628. }
  1629. else
  1630. {
  1631. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
  1632. fPatchbay->setOffline(offline);
  1633. }
  1634. }
  1635. bool EngineInternalGraph::isReady() const noexcept
  1636. {
  1637. return fIsReady;
  1638. }
  1639. RackGraph* EngineInternalGraph::getRackGraph() const noexcept
  1640. {
  1641. CARLA_SAFE_ASSERT_RETURN(fIsRack, nullptr);
  1642. return fRack;
  1643. }
  1644. PatchbayGraph* EngineInternalGraph::getPatchbayGraph() const noexcept
  1645. {
  1646. CARLA_SAFE_ASSERT_RETURN(! fIsRack, nullptr);
  1647. return fPatchbay;
  1648. }
  1649. void EngineInternalGraph::process(CarlaEngine::ProtectedData* const data, const float* const* const inBuf, float* const* const outBuf, const uint32_t frames)
  1650. {
  1651. if (fIsRack)
  1652. {
  1653. CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
  1654. fRack->processHelper(data, inBuf, outBuf, frames);
  1655. }
  1656. else
  1657. {
  1658. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
  1659. fPatchbay->process(data, inBuf, outBuf, static_cast<int>(frames));
  1660. }
  1661. }
  1662. void EngineInternalGraph::processRack(CarlaEngine::ProtectedData* const data, const float* inBuf[2], float* outBuf[2], const uint32_t frames)
  1663. {
  1664. CARLA_SAFE_ASSERT_RETURN(fIsRack,);
  1665. CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
  1666. fRack->process(data, inBuf, outBuf, frames);
  1667. }
  1668. // -----------------------------------------------------------------------
  1669. // used for internal patchbay mode
  1670. void EngineInternalGraph::addPlugin(CarlaPlugin* const plugin)
  1671. {
  1672. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
  1673. fPatchbay->addPlugin(plugin);
  1674. }
  1675. void EngineInternalGraph::replacePlugin(CarlaPlugin* const oldPlugin, CarlaPlugin* const newPlugin)
  1676. {
  1677. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
  1678. fPatchbay->replacePlugin(oldPlugin, newPlugin);
  1679. }
  1680. void EngineInternalGraph::renamePlugin(CarlaPlugin* const plugin, const char* const newName)
  1681. {
  1682. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
  1683. fPatchbay->renamePlugin(plugin, newName);
  1684. }
  1685. void EngineInternalGraph::removePlugin(CarlaPlugin* const plugin)
  1686. {
  1687. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
  1688. fPatchbay->removePlugin(plugin);
  1689. }
  1690. void EngineInternalGraph::removeAllPlugins()
  1691. {
  1692. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
  1693. fPatchbay->removeAllPlugins();
  1694. }
  1695. bool EngineInternalGraph::isUsingExternal() const noexcept
  1696. {
  1697. if (fIsRack)
  1698. return true;
  1699. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr, false);
  1700. return fPatchbay->usingExternal;
  1701. }
  1702. void EngineInternalGraph::setUsingExternal(const bool usingExternal) noexcept
  1703. {
  1704. CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
  1705. fPatchbay->usingExternal = usingExternal;
  1706. }
  1707. // -----------------------------------------------------------------------
  1708. // CarlaEngine Patchbay stuff
  1709. bool CarlaEngine::patchbayConnect(const uint groupA, const uint portA, const uint groupB, const uint portB)
  1710. {
  1711. CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY, false);
  1712. CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
  1713. carla_debug("CarlaEngine::patchbayConnect(%u, %u, %u, %u)", groupA, portA, groupB, portB);
  1714. if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  1715. {
  1716. RackGraph* const graph = pData->graph.getRackGraph();
  1717. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
  1718. return graph->connect(groupA, portA, groupB, portB);
  1719. }
  1720. else
  1721. {
  1722. PatchbayGraph* const graph = pData->graph.getPatchbayGraph();
  1723. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
  1724. return graph->connect(graph->usingExternal, groupA, portA, groupB, portB, true);
  1725. }
  1726. return false;
  1727. }
  1728. bool CarlaEngine::patchbayDisconnect(const uint connectionId)
  1729. {
  1730. CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY, false);
  1731. CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
  1732. carla_debug("CarlaEngine::patchbayDisconnect(%u)", connectionId);
  1733. if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  1734. {
  1735. RackGraph* const graph = pData->graph.getRackGraph();
  1736. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
  1737. return graph->disconnect(connectionId);
  1738. }
  1739. else
  1740. {
  1741. PatchbayGraph* const graph = pData->graph.getPatchbayGraph();
  1742. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
  1743. return graph->disconnect(connectionId);
  1744. }
  1745. return false;
  1746. }
  1747. bool CarlaEngine::patchbayRefresh(const bool external)
  1748. {
  1749. // subclasses should handle this
  1750. CARLA_SAFE_ASSERT_RETURN(! external, false);
  1751. if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  1752. {
  1753. // This is implemented in engine subclasses
  1754. setLastError("Unsupported operation");
  1755. return false;
  1756. }
  1757. if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
  1758. {
  1759. PatchbayGraph* const graph = pData->graph.getPatchbayGraph();
  1760. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
  1761. graph->refresh("");
  1762. return true;
  1763. }
  1764. setLastError("Unsupported operation");
  1765. return false;
  1766. }
  1767. // -----------------------------------------------------------------------
  1768. const char* const* CarlaEngine::getPatchbayConnections(const bool external) const
  1769. {
  1770. CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), nullptr);
  1771. carla_debug("CarlaEngine::getPatchbayConnections(%s)", bool2str(external));
  1772. if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  1773. {
  1774. RackGraph* const graph = pData->graph.getRackGraph();
  1775. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, nullptr);
  1776. CARLA_SAFE_ASSERT_RETURN(external, nullptr);
  1777. return graph->getConnections();
  1778. }
  1779. else
  1780. {
  1781. PatchbayGraph* const graph = pData->graph.getPatchbayGraph();
  1782. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, nullptr);
  1783. return graph->getConnections(external);
  1784. }
  1785. return nullptr;
  1786. }
  1787. void CarlaEngine::restorePatchbayConnection(const bool external, const char* const sourcePort, const char* const targetPort, const bool sendCallback)
  1788. {
  1789. CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(),);
  1790. CARLA_SAFE_ASSERT_RETURN(sourcePort != nullptr && sourcePort[0] != '\0',);
  1791. CARLA_SAFE_ASSERT_RETURN(targetPort != nullptr && targetPort[0] != '\0',);
  1792. carla_debug("CarlaEngine::restorePatchbayConnection(%s, \"%s\", \"%s\")", bool2str(external), sourcePort, targetPort);
  1793. uint groupA, portA;
  1794. uint groupB, portB;
  1795. if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
  1796. {
  1797. RackGraph* const graph = pData->graph.getRackGraph();
  1798. CARLA_SAFE_ASSERT_RETURN(graph != nullptr,);
  1799. CARLA_SAFE_ASSERT_RETURN(external,);
  1800. if (! graph->getGroupAndPortIdFromFullName(sourcePort, groupA, portA))
  1801. return;
  1802. if (! graph->getGroupAndPortIdFromFullName(targetPort, groupB, portB))
  1803. return;
  1804. graph->connect(groupA, portA, groupB, portB);
  1805. }
  1806. else
  1807. {
  1808. PatchbayGraph* const graph = pData->graph.getPatchbayGraph();
  1809. CARLA_SAFE_ASSERT_RETURN(graph != nullptr,);
  1810. if (! graph->getGroupAndPortIdFromFullName(external, sourcePort, groupA, portA))
  1811. return;
  1812. if (! graph->getGroupAndPortIdFromFullName(external, targetPort, groupB, portB))
  1813. return;
  1814. graph->connect(external, groupA, portA, groupB, portB, sendCallback);
  1815. }
  1816. }
  1817. // -----------------------------------------------------------------------
  1818. bool CarlaEngine::connectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName)
  1819. {
  1820. CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
  1821. CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK, false);
  1822. RackGraph* const graph(pData->graph.getRackGraph());
  1823. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
  1824. const CarlaRecursiveMutexLocker cml(graph->audioBuffers.mutex);
  1825. switch (connectionType)
  1826. {
  1827. case kExternalGraphConnectionAudioIn1:
  1828. return graph->audioBuffers.connectedIn1.append(portId);
  1829. case kExternalGraphConnectionAudioIn2:
  1830. return graph->audioBuffers.connectedIn2.append(portId);
  1831. case kExternalGraphConnectionAudioOut1:
  1832. return graph->audioBuffers.connectedOut1.append(portId);
  1833. case kExternalGraphConnectionAudioOut2:
  1834. return graph->audioBuffers.connectedOut2.append(portId);
  1835. }
  1836. return false;
  1837. }
  1838. bool CarlaEngine::disconnectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName)
  1839. {
  1840. CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
  1841. CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK, false);
  1842. RackGraph* const graph(pData->graph.getRackGraph());
  1843. CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
  1844. const CarlaRecursiveMutexLocker cml(graph->audioBuffers.mutex);
  1845. switch (connectionType)
  1846. {
  1847. case kExternalGraphConnectionAudioIn1:
  1848. return graph->audioBuffers.connectedIn1.removeOne(portId);
  1849. case kExternalGraphConnectionAudioIn2:
  1850. return graph->audioBuffers.connectedIn2.removeOne(portId);
  1851. case kExternalGraphConnectionAudioOut1:
  1852. return graph->audioBuffers.connectedOut1.removeOne(portId);
  1853. case kExternalGraphConnectionAudioOut2:
  1854. return graph->audioBuffers.connectedOut2.removeOne(portId);
  1855. }
  1856. return false;
  1857. }
  1858. // -----------------------------------------------------------------------
  1859. CARLA_BACKEND_END_NAMESPACE
  1860. // enable -Wdeprecated-declarations again
  1861. #if defined(__clang__)
  1862. # pragma clang diagnostic pop
  1863. #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  1864. # pragma GCC diagnostic pop
  1865. #endif
  1866. // -----------------------------------------------------------------------