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.

2409 lines
80KB

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