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.

461 lines
17KB

  1. /*
  2. * Carla JACK API for external applications
  3. * Copyright (C) 2016-2020 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "libjack.hpp"
  18. #include "CarlaStringList.hpp"
  19. CARLA_BACKEND_USE_NAMESPACE
  20. // --------------------------------------------------------------------------------------------------------------------
  21. static const char* allocate_port_name(const char* const prefixOrFullName, const uint num = UINT32_MAX)
  22. {
  23. static CarlaStringList portList;
  24. char portName[STR_MAX];
  25. carla_zeroChars(portName, STR_MAX);
  26. if (num == UINT32_MAX)
  27. std::strncpy(portName, prefixOrFullName, STR_MAX-1);
  28. else
  29. std::snprintf(portName, STR_MAX-1, "%s%u", prefixOrFullName, num+1);
  30. if (const char* const storedPortName = portList.containsAndReturnString(portName))
  31. return storedPortName;
  32. CARLA_SAFE_ASSERT_RETURN(portList.append(portName), nullptr);
  33. return portList.getLast();
  34. }
  35. // --------------------------------------------------------------------------------------------------------------------
  36. CARLA_EXPORT
  37. const char** jack_get_ports(jack_client_t* const client,
  38. const char* const port_name,
  39. const char* const port_type,
  40. const unsigned long flags)
  41. {
  42. carla_stdout("%s(%p, %s, %s, 0x%lx)", __FUNCTION__, client, port_name, port_type, flags);
  43. JackClientState* const jclient = (JackClientState*)client;
  44. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, nullptr);
  45. const JackServerState& jserver(jclient->server);
  46. const uint numIns = static_cast<uint>(jclient->audioIns.count() +
  47. jclient->midiIns.count() +
  48. jserver.numAudioIns +
  49. jserver.numMidiIns);
  50. const uint numOuts = static_cast<uint>(jclient->audioOuts.count() +
  51. jclient->midiOuts.count() +
  52. jserver.numAudioOuts +
  53. jserver.numMidiOuts);
  54. const bool wantsAudio = port_type == nullptr || port_type[0] == '\0' || std::strstr(port_type, "audio") != nullptr;
  55. const bool wantsMIDI = port_type == nullptr || port_type[0] == '\0' || std::strstr(port_type, "midi") != nullptr;
  56. if (flags == 0x0 || (flags & (JackPortIsInput|JackPortIsOutput)) == (JackPortIsInput|JackPortIsOutput))
  57. {
  58. if (const char** const ret = (const char**)calloc(numIns+numOuts+1, sizeof(const char*)))
  59. {
  60. uint i=0;
  61. if (wantsAudio)
  62. {
  63. for (uint j=0; j<jserver.numAudioIns; ++i, ++j)
  64. ret[i] = allocate_port_name("system:capture_", j);
  65. for (uint j=0; j<jserver.numAudioOuts; ++i, ++j)
  66. ret[i] = allocate_port_name("system:playback_", j);
  67. }
  68. if (wantsMIDI)
  69. {
  70. for (uint j=0; j<jserver.numMidiIns; ++i, ++j)
  71. ret[i] = allocate_port_name("system:midi_capture_", j);
  72. for (uint j=0; j<jserver.numMidiOuts; ++i, ++j)
  73. ret[i] = allocate_port_name("system:midi_playback_", j);
  74. }
  75. if ((flags & (JackPortIsPhysical|JackPortIsTerminal)) == 0x0)
  76. {
  77. if (wantsAudio)
  78. {
  79. for (LinkedList<JackPortState*>::Itenerator it = jclient->audioIns.begin2(); it.valid(); it.next())
  80. {
  81. JackPortState* const jport = it.getValue(nullptr);
  82. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  83. ret[i++] = allocate_port_name(jport->fullname);
  84. }
  85. for (LinkedList<JackPortState*>::Itenerator it = jclient->audioOuts.begin2(); it.valid(); it.next())
  86. {
  87. JackPortState* const jport = it.getValue(nullptr);
  88. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  89. ret[i++] = allocate_port_name(jport->fullname);
  90. }
  91. }
  92. if (wantsMIDI)
  93. {
  94. for (LinkedList<JackPortState*>::Itenerator it = jclient->midiIns.begin2(); it.valid(); it.next())
  95. {
  96. JackPortState* const jport = it.getValue(nullptr);
  97. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  98. ret[i++] = allocate_port_name(jport->fullname);
  99. }
  100. for (LinkedList<JackPortState*>::Itenerator it = jclient->midiOuts.begin2(); it.valid(); it.next())
  101. {
  102. JackPortState* const jport = it.getValue(nullptr);
  103. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  104. ret[i++] = allocate_port_name(jport->fullname);
  105. }
  106. }
  107. }
  108. ret[i] = nullptr;
  109. return ret;
  110. }
  111. return nullptr;
  112. }
  113. if (flags & JackPortIsInput)
  114. {
  115. if (const char** const ret = (const char**)calloc(numIns+1, sizeof(const char*)))
  116. {
  117. uint i=0;
  118. if (wantsAudio)
  119. {
  120. for (uint j=0; j<jserver.numAudioOuts; ++i, ++j)
  121. ret[i] = allocate_port_name("system:playback_", j);
  122. }
  123. if (wantsMIDI)
  124. {
  125. for (uint j=0; j<jserver.numMidiOuts; ++i, ++j)
  126. ret[i] = allocate_port_name("system:midi_playback_", j);
  127. }
  128. if ((flags & (JackPortIsPhysical|JackPortIsTerminal)) == 0x0)
  129. {
  130. if (wantsAudio)
  131. {
  132. for (LinkedList<JackPortState*>::Itenerator it = jclient->audioIns.begin2(); it.valid(); it.next())
  133. {
  134. JackPortState* const jport = it.getValue(nullptr);
  135. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  136. ret[i++] = allocate_port_name(jport->fullname);
  137. }
  138. }
  139. if (wantsMIDI)
  140. {
  141. for (LinkedList<JackPortState*>::Itenerator it = jclient->midiIns.begin2(); it.valid(); it.next())
  142. {
  143. JackPortState* const jport = it.getValue(nullptr);
  144. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  145. ret[i++] = allocate_port_name(jport->fullname);
  146. }
  147. }
  148. }
  149. ret[i] = nullptr;
  150. return ret;
  151. }
  152. return nullptr;
  153. }
  154. if (flags & JackPortIsOutput)
  155. {
  156. if (const char** const ret = (const char**)calloc(numOuts+1, sizeof(const char*)))
  157. {
  158. uint i=0;
  159. if (wantsAudio)
  160. {
  161. for (uint j=0; j<jserver.numAudioIns; ++i, ++j)
  162. ret[i] = allocate_port_name("system:capture_", j);
  163. }
  164. if (wantsMIDI)
  165. {
  166. for (uint j=0; j<jserver.numMidiIns; ++i, ++j)
  167. ret[i] = allocate_port_name("system:midi_capture_", j);
  168. }
  169. if ((flags & (JackPortIsPhysical|JackPortIsTerminal)) == 0x0)
  170. {
  171. if (wantsAudio)
  172. {
  173. for (LinkedList<JackPortState*>::Itenerator it = jclient->audioOuts.begin2(); it.valid(); it.next())
  174. {
  175. JackPortState* const jport = it.getValue(nullptr);
  176. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  177. ret[i++] = allocate_port_name(jport->fullname);
  178. }
  179. }
  180. if (wantsMIDI)
  181. {
  182. for (LinkedList<JackPortState*>::Itenerator it = jclient->midiOuts.begin2(); it.valid(); it.next())
  183. {
  184. JackPortState* const jport = it.getValue(nullptr);
  185. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  186. ret[i++] = allocate_port_name(jport->fullname);
  187. }
  188. }
  189. }
  190. ret[i] = nullptr;
  191. return ret;
  192. }
  193. return nullptr;
  194. }
  195. return nullptr;
  196. }
  197. CARLA_EXPORT
  198. jack_port_t* jack_port_by_name(jack_client_t* client, const char* name)
  199. {
  200. carla_debug("%s(%p, %s)", __FUNCTION__, client, name);
  201. JackClientState* const jclient = (JackClientState*)client;
  202. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, nullptr);
  203. if (std::strncmp(name, "system:", 7) == 0)
  204. {
  205. static std::map<uint, JackPortState*> systemPortIdMapping;
  206. const JackServerState& jserver(jclient->server);
  207. const int commonFlags = JackPortIsPhysical|JackPortIsTerminal;
  208. uint rindex, gid;
  209. int flags;
  210. bool isMidi, isConnected;
  211. const char* const fullname = name;
  212. const char* const portname = name + 7;
  213. name += 7;
  214. /**/ if (std::strncmp(name, "capture_", 8) == 0)
  215. {
  216. name += 8;
  217. const int index = std::atoi(name)-1;
  218. CARLA_SAFE_ASSERT_RETURN(index >= 0 && index < jserver.numAudioIns, nullptr);
  219. rindex = static_cast<uint>(index);
  220. flags = commonFlags|JackPortIsOutput;
  221. gid = JackPortState::kPortIdOffsetAudioIn + rindex;
  222. isMidi = false;
  223. isConnected = jserver.numAudioIns > rindex;
  224. }
  225. else if (std::strncmp(name, "playback_", 9) == 0)
  226. {
  227. name += 9;
  228. const int index = std::atoi(name)-1;
  229. CARLA_SAFE_ASSERT_RETURN(index >= 0 && index < jserver.numAudioOuts, nullptr);
  230. rindex = static_cast<uint>(jserver.numAudioIns + index);
  231. flags = commonFlags|JackPortIsInput;
  232. gid = JackPortState::kPortIdOffsetAudioOut + rindex;
  233. isMidi = false;
  234. isConnected = jserver.numAudioOuts > rindex;
  235. }
  236. else if (std::strncmp(name, "midi_capture_", 13) == 0)
  237. {
  238. name += 13;
  239. const int index = std::atoi(name)-1;
  240. CARLA_SAFE_ASSERT_RETURN(index >= 0 && index < jserver.numMidiIns, nullptr);
  241. rindex = static_cast<uint>(index);
  242. flags = commonFlags|JackPortIsOutput;
  243. gid = JackPortState::kPortIdOffsetMidiIn + rindex;
  244. isMidi = true;
  245. isConnected = jserver.numMidiIns > rindex;
  246. }
  247. else if (std::strncmp(name, "midi_playback_", 14) == 0)
  248. {
  249. name += 14;
  250. const int index = std::atoi(name)-1;
  251. CARLA_SAFE_ASSERT_RETURN(index >= 0 && index < jserver.numMidiOuts, nullptr);
  252. rindex = static_cast<uint>(jserver.numMidiIns + index);
  253. flags = commonFlags|JackPortIsInput;
  254. gid = JackPortState::kPortIdOffsetMidiOut + rindex;
  255. isMidi = true;
  256. isConnected = jserver.numMidiOuts > rindex;
  257. }
  258. else
  259. {
  260. carla_stderr2("jack_port_by_name: invalid port short name '%s'", name);
  261. return nullptr;
  262. }
  263. if (JackPortState* const port = systemPortIdMapping[gid])
  264. return (jack_port_t*)port;
  265. JackPortState* const port = new JackPortState(fullname,
  266. portname,
  267. rindex, flags, gid,
  268. isMidi, isConnected);
  269. systemPortIdMapping[gid] = port;
  270. return (jack_port_t*)port;
  271. }
  272. else
  273. {
  274. if (JackPortState* const port = jclient->portNameMapping[name])
  275. return (jack_port_t*)port;
  276. }
  277. carla_stderr2("jack_port_by_name: invalid port name '%s'", name);
  278. return nullptr;
  279. }
  280. CARLA_EXPORT
  281. jack_port_t* jack_port_by_id(jack_client_t* client, jack_port_id_t port_id)
  282. {
  283. carla_debug("%s(%p, %u)", __FUNCTION__, client, port_id);
  284. CARLA_SAFE_ASSERT_UINT_RETURN(port_id >= JackPortState::kPortIdOffsetUser, port_id, nullptr);
  285. JackClientState* const jclient = (JackClientState*)client;
  286. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, nullptr);
  287. if (JackPortState* const port = jclient->portIdMapping[port_id])
  288. return (jack_port_t*)port;
  289. carla_stderr2("jack_port_by_id: invalid port id %u", port_id);
  290. return nullptr;
  291. }
  292. // --------------------------------------------------------------------------------------------------------------------
  293. CARLA_EXPORT
  294. const char** jack_port_get_connections(const jack_port_t* port)
  295. {
  296. carla_stderr2("%s(%p)", __FUNCTION__, port);
  297. const JackPortState* const jport = (const JackPortState*)port;
  298. CARLA_SAFE_ASSERT_RETURN(jport != nullptr, nullptr);
  299. CARLA_SAFE_ASSERT_RETURN(! jport->isSystem, nullptr);
  300. if (! jport->isConnected)
  301. return nullptr;
  302. return nullptr;
  303. }
  304. CARLA_EXPORT
  305. const char** jack_port_get_all_connections(const jack_client_t* client, const jack_port_t* port)
  306. {
  307. carla_stdout("%s(%p, %p) WIP", __FUNCTION__, client, port);
  308. const JackClientState* const jclient = (const JackClientState*)client;
  309. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, nullptr);
  310. const JackPortState* const jport = (const JackPortState*)port;
  311. CARLA_SAFE_ASSERT_RETURN(jport != nullptr, nullptr);
  312. CARLA_SAFE_ASSERT_UINT_RETURN(jport->gid >= JackPortState::kPortIdOffsetAudioIn, jport->gid, nullptr);
  313. if (! jport->isConnected)
  314. return nullptr;
  315. if (jport->isSystem)
  316. {
  317. const JackPortState* connectedPort;
  318. /**/ if (jport->gid >= JackPortState::kPortIdOffsetMidiOut)
  319. connectedPort = jclient->midiOuts.getAt(jport->gid - JackPortState::kPortIdOffsetMidiOut, nullptr);
  320. else if (jport->gid >= JackPortState::kPortIdOffsetAudioOut)
  321. connectedPort = jclient->audioOuts.getAt(jport->gid - JackPortState::kPortIdOffsetAudioOut, nullptr);
  322. else if (jport->gid >= JackPortState::kPortIdOffsetMidiIn)
  323. connectedPort = jclient->midiIns.getAt(jport->gid - JackPortState::kPortIdOffsetMidiIn, nullptr);
  324. else
  325. connectedPort = jclient->audioIns.getAt(jport->gid - JackPortState::kPortIdOffsetAudioIn, nullptr);
  326. if (connectedPort == nullptr)
  327. {
  328. carla_debug("port %s has no connections?", jport->fullname);
  329. return nullptr;
  330. }
  331. if (const char** const ret = static_cast<const char**>(malloc(sizeof(const char*)*2)))
  332. {
  333. carla_debug("port %s is connected to %s", jport->fullname, connectedPort->fullname);
  334. ret[0] = connectedPort->fullname;
  335. ret[1] = nullptr;
  336. return ret;
  337. }
  338. }
  339. else
  340. {
  341. const JackServerState& jserver(jclient->server);
  342. const char* connectedPortName = nullptr;
  343. if (jport->isMidi)
  344. {
  345. if (jport->flags & JackPortIsOutput)
  346. {
  347. if (jport->index < jserver.numMidiOuts)
  348. connectedPortName = allocate_port_name("system:midi_playback_", jport->index);
  349. }
  350. else
  351. {
  352. if (jport->index < jserver.numMidiIns)
  353. connectedPortName = allocate_port_name("system:midi_capture_", jport->index);
  354. }
  355. }
  356. else
  357. {
  358. if (jport->flags & JackPortIsOutput)
  359. {
  360. if (jport->index < jserver.numAudioOuts)
  361. connectedPortName = allocate_port_name("system:playback_", jport->index);
  362. }
  363. else
  364. {
  365. if (jport->index < jserver.numAudioIns)
  366. connectedPortName = allocate_port_name("system:capture_", jport->index);
  367. }
  368. }
  369. if (connectedPortName != nullptr)
  370. {
  371. if (const char** const ret = static_cast<const char**>(malloc(sizeof(const char*)*2)))
  372. {
  373. carla_debug("port %s is connected to %s", jport->fullname, connectedPortName);
  374. ret[0] = connectedPortName;
  375. ret[1] = nullptr;
  376. return ret;
  377. }
  378. }
  379. }
  380. return nullptr;
  381. }
  382. // --------------------------------------------------------------------------------------------------------------------