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.

419 lines
15KB

  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* client, const char* a, const char* b, unsigned long flags)
  38. {
  39. carla_stdout("%s(%p, %s, %s, 0x%lx)", __FUNCTION__, client, a, b, flags);
  40. JackClientState* const jclient = (JackClientState*)client;
  41. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, nullptr);
  42. const JackServerState& jserver(jclient->server);
  43. const uint numIns = static_cast<uint>(jclient->audioIns.count() +
  44. jclient->midiIns.count() +
  45. jserver.numAudioIns +
  46. jserver.numMidiIns);
  47. const uint numOuts = static_cast<uint>(jclient->audioOuts.count() +
  48. jclient->midiOuts.count() +
  49. jserver.numAudioOuts +
  50. jserver.numMidiOuts);
  51. if (flags == 0x0 || (flags & (JackPortIsInput|JackPortIsOutput)) == (JackPortIsInput|JackPortIsOutput))
  52. {
  53. if (const char** const ret = (const char**)calloc(numIns+numOuts+1, sizeof(const char*)))
  54. {
  55. uint i=0;
  56. for (uint j=0; j<jserver.numAudioIns; ++i, ++j)
  57. ret[i] = allocate_port_name("system:capture_", j);
  58. for (uint j=0; j<jserver.numAudioOuts; ++i, ++j)
  59. ret[i] = allocate_port_name("system:playback_", j);
  60. for (uint j=0; j<jserver.numMidiIns; ++i, ++j)
  61. ret[i] = allocate_port_name("system:midi_capture_", j);
  62. for (uint j=0; j<jserver.numMidiOuts; ++i, ++j)
  63. ret[i] = allocate_port_name("system:midi_playback_", j);
  64. if ((flags & (JackPortIsPhysical|JackPortIsTerminal)) == 0x0)
  65. {
  66. for (LinkedList<JackPortState*>::Itenerator it = jclient->audioIns.begin2(); it.valid(); it.next())
  67. {
  68. JackPortState* const jport = it.getValue(nullptr);
  69. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  70. ret[i++] = allocate_port_name(jport->fullname);
  71. }
  72. for (LinkedList<JackPortState*>::Itenerator it = jclient->midiIns.begin2(); it.valid(); it.next())
  73. {
  74. JackPortState* const jport = it.getValue(nullptr);
  75. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  76. ret[i++] = allocate_port_name(jport->fullname);
  77. }
  78. for (LinkedList<JackPortState*>::Itenerator it = jclient->audioOuts.begin2(); it.valid(); it.next())
  79. {
  80. JackPortState* const jport = it.getValue(nullptr);
  81. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  82. ret[i++] = allocate_port_name(jport->fullname);
  83. }
  84. for (LinkedList<JackPortState*>::Itenerator it = jclient->midiOuts.begin2(); it.valid(); it.next())
  85. {
  86. JackPortState* const jport = it.getValue(nullptr);
  87. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  88. ret[i++] = allocate_port_name(jport->fullname);
  89. }
  90. }
  91. ret[i] = nullptr;
  92. return ret;
  93. }
  94. return nullptr;
  95. }
  96. if (flags & JackPortIsInput)
  97. {
  98. if (const char** const ret = (const char**)calloc(numIns+1, sizeof(const char*)))
  99. {
  100. uint i=0;
  101. for (uint j=0; j<jserver.numAudioOuts; ++i, ++j)
  102. ret[i] = allocate_port_name("system:playback_", j);
  103. for (uint j=0; j<jserver.numMidiOuts; ++i, ++j)
  104. ret[i] = allocate_port_name("system:midi_playback_", j);
  105. if ((flags & (JackPortIsPhysical|JackPortIsTerminal)) == 0x0)
  106. {
  107. for (LinkedList<JackPortState*>::Itenerator it = jclient->audioIns.begin2(); it.valid(); it.next())
  108. {
  109. JackPortState* const jport = it.getValue(nullptr);
  110. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  111. ret[i++] = allocate_port_name(jport->fullname);
  112. }
  113. for (LinkedList<JackPortState*>::Itenerator it = jclient->midiIns.begin2(); it.valid(); it.next())
  114. {
  115. JackPortState* const jport = it.getValue(nullptr);
  116. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  117. ret[i++] = allocate_port_name(jport->fullname);
  118. }
  119. }
  120. ret[i] = nullptr;
  121. return ret;
  122. }
  123. return nullptr;
  124. }
  125. if (flags & JackPortIsOutput)
  126. {
  127. if (const char** const ret = (const char**)calloc(numOuts+1, sizeof(const char*)))
  128. {
  129. uint i=0;
  130. for (uint j=0; j<jserver.numAudioIns; ++i, ++j)
  131. ret[i] = allocate_port_name("system:capture_", j);
  132. for (uint j=0; j<jserver.numMidiIns; ++i, ++j)
  133. ret[i] = allocate_port_name("system:midi_capture_", j);
  134. if ((flags & (JackPortIsPhysical|JackPortIsTerminal)) == 0x0)
  135. {
  136. for (LinkedList<JackPortState*>::Itenerator it = jclient->audioOuts.begin2(); it.valid(); it.next())
  137. {
  138. JackPortState* const jport = it.getValue(nullptr);
  139. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  140. ret[i++] = allocate_port_name(jport->fullname);
  141. }
  142. for (LinkedList<JackPortState*>::Itenerator it = jclient->midiOuts.begin2(); it.valid(); it.next())
  143. {
  144. JackPortState* const jport = it.getValue(nullptr);
  145. CARLA_SAFE_ASSERT_CONTINUE(jport != nullptr);
  146. ret[i++] = allocate_port_name(jport->fullname);
  147. }
  148. }
  149. ret[i] = nullptr;
  150. return ret;
  151. }
  152. return nullptr;
  153. }
  154. return nullptr;
  155. }
  156. CARLA_EXPORT
  157. jack_port_t* jack_port_by_name(jack_client_t* client, const char* name)
  158. {
  159. carla_debug("%s(%p, %s)", __FUNCTION__, client, name);
  160. JackClientState* const jclient = (JackClientState*)client;
  161. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, nullptr);
  162. if (std::strncmp(name, "system:", 7) == 0)
  163. {
  164. static std::map<uint, JackPortState*> systemPortIdMapping;
  165. const JackServerState& jserver(jclient->server);
  166. const int commonFlags = JackPortIsPhysical|JackPortIsTerminal;
  167. uint rindex, gid;
  168. int flags;
  169. bool isMidi, isConnected;
  170. const char* const fullname = name;
  171. const char* const portname = name + 7;
  172. name += 7;
  173. /**/ if (std::strncmp(name, "capture_", 8) == 0)
  174. {
  175. name += 8;
  176. const int index = std::atoi(name)-1;
  177. CARLA_SAFE_ASSERT_RETURN(index >= 0 && index < jserver.numAudioIns, nullptr);
  178. rindex = static_cast<uint>(index);
  179. flags = commonFlags|JackPortIsOutput;
  180. gid = JackPortState::kPortIdOffsetAudioIn + rindex;
  181. isMidi = false;
  182. isConnected = jserver.numAudioIns > rindex;
  183. }
  184. else if (std::strncmp(name, "playback_", 9) == 0)
  185. {
  186. name += 9;
  187. const int index = std::atoi(name)-1;
  188. CARLA_SAFE_ASSERT_RETURN(index >= 0 && index < jserver.numAudioOuts, nullptr);
  189. rindex = static_cast<uint>(jserver.numAudioIns + index);
  190. flags = commonFlags|JackPortIsInput;
  191. gid = JackPortState::kPortIdOffsetAudioOut + rindex;
  192. isMidi = false;
  193. isConnected = jserver.numAudioOuts > rindex;
  194. }
  195. else if (std::strncmp(name, "midi_capture_", 13) == 0)
  196. {
  197. name += 13;
  198. const int index = std::atoi(name)-1;
  199. CARLA_SAFE_ASSERT_RETURN(index >= 0 && index < jserver.numMidiIns, nullptr);
  200. rindex = static_cast<uint>(index);
  201. flags = commonFlags|JackPortIsOutput;
  202. gid = JackPortState::kPortIdOffsetMidiIn + rindex;
  203. isMidi = true;
  204. isConnected = jserver.numMidiIns > rindex;
  205. }
  206. else if (std::strncmp(name, "midi_playback_", 14) == 0)
  207. {
  208. name += 14;
  209. const int index = std::atoi(name)-1;
  210. CARLA_SAFE_ASSERT_RETURN(index >= 0 && index < jserver.numMidiOuts, nullptr);
  211. rindex = static_cast<uint>(jserver.numMidiIns + index);
  212. flags = commonFlags|JackPortIsInput;
  213. gid = JackPortState::kPortIdOffsetMidiOut + rindex;
  214. isMidi = true;
  215. isConnected = jserver.numMidiOuts > rindex;
  216. }
  217. else
  218. {
  219. carla_stderr2("jack_port_by_name: invalid port short name '%s'", name);
  220. return nullptr;
  221. }
  222. if (JackPortState* const port = systemPortIdMapping[gid])
  223. return (jack_port_t*)port;
  224. JackPortState* const port = new JackPortState(fullname,
  225. portname,
  226. rindex, flags, gid,
  227. isMidi, isConnected);
  228. systemPortIdMapping[gid] = port;
  229. return (jack_port_t*)port;
  230. }
  231. else
  232. {
  233. if (JackPortState* const port = jclient->portNameMapping[name])
  234. return (jack_port_t*)port;
  235. }
  236. carla_stderr2("jack_port_by_name: invalid port name '%s'", name);
  237. return nullptr;
  238. }
  239. CARLA_EXPORT
  240. jack_port_t* jack_port_by_id(jack_client_t* client, jack_port_id_t port_id)
  241. {
  242. carla_debug("%s(%p, %u)", __FUNCTION__, client, port_id);
  243. CARLA_SAFE_ASSERT_UINT_RETURN(port_id >= JackPortState::kPortIdOffsetUser, port_id, nullptr);
  244. JackClientState* const jclient = (JackClientState*)client;
  245. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, nullptr);
  246. if (JackPortState* const port = jclient->portIdMapping[port_id])
  247. return (jack_port_t*)port;
  248. carla_stderr2("jack_port_by_id: invalid port id %u", port_id);
  249. return nullptr;
  250. }
  251. // --------------------------------------------------------------------------------------------------------------------
  252. CARLA_EXPORT
  253. const char** jack_port_get_connections(const jack_port_t* port)
  254. {
  255. carla_stderr2("%s(%p)", __FUNCTION__, port);
  256. const JackPortState* const jport = (const JackPortState*)port;
  257. CARLA_SAFE_ASSERT_RETURN(jport != nullptr, nullptr);
  258. CARLA_SAFE_ASSERT_RETURN(! jport->isSystem, nullptr);
  259. if (! jport->isConnected)
  260. return nullptr;
  261. return nullptr;
  262. }
  263. CARLA_EXPORT
  264. const char** jack_port_get_all_connections(const jack_client_t* client, const jack_port_t* port)
  265. {
  266. carla_stdout("%s(%p, %p) WIP", __FUNCTION__, client, port);
  267. JackClientState* const jclient = (JackClientState*)client;
  268. CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, nullptr);
  269. const JackPortState* const jport = (const JackPortState*)port;
  270. CARLA_SAFE_ASSERT_RETURN(jport != nullptr, nullptr);
  271. CARLA_SAFE_ASSERT_UINT_RETURN(jport->gid >= JackPortState::kPortIdOffsetAudioIn, jport->gid, nullptr);
  272. if (! jport->isConnected)
  273. return nullptr;
  274. if (jport->isSystem)
  275. {
  276. const JackPortState* connectedPort;
  277. /**/ if (jport->gid >= JackPortState::kPortIdOffsetMidiOut)
  278. connectedPort = jclient->midiOuts.getAt(jport->gid - JackPortState::kPortIdOffsetMidiOut, nullptr);
  279. else if (jport->gid >= JackPortState::kPortIdOffsetAudioOut)
  280. connectedPort = jclient->audioOuts.getAt(jport->gid - JackPortState::kPortIdOffsetAudioOut, nullptr);
  281. else if (jport->gid >= JackPortState::kPortIdOffsetMidiIn)
  282. connectedPort = jclient->midiIns.getAt(jport->gid - JackPortState::kPortIdOffsetMidiIn, nullptr);
  283. else
  284. connectedPort = jclient->audioIns.getAt(jport->gid - JackPortState::kPortIdOffsetAudioIn, nullptr);
  285. if (connectedPort == nullptr)
  286. {
  287. carla_debug("port %s has no connections?", jport->fullname);
  288. return nullptr;
  289. }
  290. if (const char** const ret = static_cast<const char**>(malloc(sizeof(const char*)*2)))
  291. {
  292. carla_debug("port %s is connected to %s", jport->fullname, connectedPort->fullname);
  293. ret[0] = connectedPort->fullname;
  294. ret[1] = nullptr;
  295. return ret;
  296. }
  297. }
  298. else
  299. {
  300. const JackServerState& jserver(jclient->server);
  301. const char* connectedPortName = nullptr;
  302. if (jport->isMidi)
  303. {
  304. if (jport->flags & JackPortIsOutput)
  305. {
  306. if (jport->index < jserver.numMidiOuts)
  307. connectedPortName = allocate_port_name("system:midi_playback_", jport->index);
  308. }
  309. else
  310. {
  311. if (jport->index < jserver.numMidiIns)
  312. connectedPortName = allocate_port_name("system:midi_capture_", jport->index);
  313. }
  314. }
  315. else
  316. {
  317. if (jport->flags & JackPortIsOutput)
  318. {
  319. if (jport->index < jserver.numAudioOuts)
  320. connectedPortName = allocate_port_name("system:playback_", jport->index);
  321. }
  322. else
  323. {
  324. if (jport->index < jserver.numAudioIns)
  325. connectedPortName = allocate_port_name("system:capture_", jport->index);
  326. }
  327. }
  328. if (connectedPortName != nullptr)
  329. {
  330. if (const char** const ret = static_cast<const char**>(malloc(sizeof(const char*)*2)))
  331. {
  332. carla_debug("port %s is connected to %s", jport->fullname, connectedPortName);
  333. ret[0] = connectedPortName;
  334. ret[1] = nullptr;
  335. return ret;
  336. }
  337. }
  338. }
  339. return nullptr;
  340. }
  341. // --------------------------------------------------------------------------------------------------------------------