jack2 codebase
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.

432 lines
12KB

  1. /*
  2. Copyright (C) 2004-2008 Grame
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  14. */
  15. #include <iostream>
  16. #include <assert.h>
  17. #include "JackConnectionManager.h"
  18. #include "JackClientControl.h"
  19. #include "JackError.h"
  20. namespace Jack
  21. {
  22. JackConnectionManager::JackConnectionManager()
  23. {
  24. int i;
  25. JackLog("JackConnectionManager::InitConnections size = %ld \n", sizeof(JackConnectionManager));
  26. for (i = 0; i < PORT_NUM; i++) {
  27. fConnection[i].Init();
  28. }
  29. fLoopFeedback.Init();
  30. JackLog("JackConnectionManager::InitClients\n");
  31. for (i = 0; i < CLIENT_NUM; i++) {
  32. InitRefNum(i);
  33. }
  34. }
  35. JackConnectionManager::~JackConnectionManager()
  36. {}
  37. //--------------
  38. // Internal API
  39. //--------------
  40. bool JackConnectionManager::IsLoopPathAux(int ref1, int ref2) const
  41. {
  42. JackLog("JackConnectionManager::IsLoopPathAux ref1 = %ld ref2 = %ld\n", ref1, ref2);
  43. if (ref1 == AUDIO_DRIVER_REFNUM // Driver is reached
  44. || ref2 == AUDIO_DRIVER_REFNUM
  45. || ref1 == FREEWHEEL_DRIVER_REFNUM
  46. || ref2 == FREEWHEEL_DRIVER_REFNUM
  47. || ref1 == LOOPBACK_DRIVER_REFNUM
  48. || ref2 == LOOPBACK_DRIVER_REFNUM) {
  49. return false;
  50. } else if (ref1 == ref2) { // Same refnum
  51. return true;
  52. } else {
  53. jack_int_t output[CLIENT_NUM];
  54. fConnectionRef.GetOutputTable(ref1, output);
  55. if (fConnectionRef.IsInsideTable(ref2, output)) { // If ref2 is contained in the outputs of ref1
  56. return true;
  57. } else {
  58. for (int i = 0; i < CLIENT_NUM && output[i] != EMPTY; i++) { // Otherwise recurse for all ref1 outputs
  59. if (IsLoopPathAux(output[i], ref2))
  60. return true; // Stop when a path is found
  61. }
  62. return false;
  63. }
  64. }
  65. }
  66. //--------------
  67. // External API
  68. //--------------
  69. int JackConnectionManager::GetActivation(int refnum) const
  70. {
  71. return fInputCounter[refnum].GetValue();
  72. }
  73. /*!
  74. \brief Connect port_src to port_dst.
  75. */
  76. int JackConnectionManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst)
  77. {
  78. JackLog("JackConnectionManager::Connect port_src = %ld port_dst = %ld\n", port_src, port_dst);
  79. if (fConnection[port_src].AddItem(port_dst)) {
  80. return 0;
  81. } else {
  82. jack_error("Connection table is full !!");
  83. return -1;
  84. }
  85. }
  86. /*!
  87. \brief Disconnect port_src from port_dst.
  88. */
  89. int JackConnectionManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst)
  90. {
  91. JackLog("JackConnectionManager::Disconnect port_src = %ld port_dst = %ld\n", port_src, port_dst);
  92. if (fConnection[port_src].RemoveItem(port_dst)) {
  93. return 0;
  94. } else {
  95. jack_error("Connection not found !!");
  96. return -1;
  97. }
  98. }
  99. /*!
  100. \brief Check if port_src and port_dst are connected.
  101. */
  102. bool JackConnectionManager::IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const
  103. {
  104. return fConnection[port_src].CheckItem(port_dst);
  105. }
  106. /*!
  107. \brief Get the connection number of a given port.
  108. */
  109. jack_int_t JackConnectionManager::Connections(jack_port_id_t port_index) const
  110. {
  111. return fConnection[port_index].GetItemCount();
  112. }
  113. jack_port_id_t JackConnectionManager::GetPort(jack_port_id_t port_index, int connection) const
  114. {
  115. assert(connection < CONNECTION_NUM);
  116. return (jack_port_id_t)fConnection[port_index].GetItem(connection);
  117. }
  118. /*!
  119. \brief Get the connection port array.
  120. */
  121. const jack_int_t* JackConnectionManager::GetConnections(jack_port_id_t port_index) const
  122. {
  123. return fConnection[port_index].GetItems();
  124. }
  125. //------------------------
  126. // Client port management
  127. //------------------------
  128. /*!
  129. \brief Add an input port to a client.
  130. */
  131. int JackConnectionManager::AddInputPort(int refnum, jack_port_id_t port_index)
  132. {
  133. if (fInputPort[refnum].AddItem(port_index)) {
  134. JackLog("JackConnectionManager::AddInputPort ref = %ld port = %ld\n", refnum, port_index);
  135. return 0;
  136. } else {
  137. jack_error("Maximum number of input ports is reached for application ref = %ld", refnum);
  138. return -1;
  139. }
  140. }
  141. /*!
  142. \brief Add an output port to a client.
  143. */
  144. int JackConnectionManager::AddOutputPort(int refnum, jack_port_id_t port_index)
  145. {
  146. if (fOutputPort[refnum].AddItem(port_index)) {
  147. JackLog("JackConnectionManager::AddOutputPort ref = %ld port = %ld\n", refnum, port_index);
  148. return 0;
  149. } else {
  150. jack_error("Maximum number of output ports is reached for application ref = %ld", refnum);
  151. return -1;
  152. }
  153. }
  154. /*!
  155. \brief Remove an input port from a client.
  156. */
  157. int JackConnectionManager::RemoveInputPort(int refnum, jack_port_id_t port_index)
  158. {
  159. JackLog("JackConnectionManager::RemoveInputPort ref = %ld port_index = %ld \n", refnum, port_index);
  160. if (fInputPort[refnum].RemoveItem(port_index)) {
  161. return 0;
  162. } else {
  163. jack_error("Input port index = %ld not found for application ref = %ld", port_index, refnum);
  164. return -1;
  165. }
  166. }
  167. /*!
  168. \brief Remove an output port from a client.
  169. */
  170. int JackConnectionManager::RemoveOutputPort(int refnum, jack_port_id_t port_index)
  171. {
  172. JackLog("JackConnectionManager::RemoveOutputPort ref = %ld port_index = %ld \n", refnum, port_index);
  173. if (fOutputPort[refnum].RemoveItem(port_index)) {
  174. return 0;
  175. } else {
  176. jack_error("Output port index = %ld not found for application ref = %ld", port_index, refnum);
  177. return -1;
  178. }
  179. }
  180. /*!
  181. \brief Get the input port array of a given refnum.
  182. */
  183. const jack_int_t* JackConnectionManager::GetInputPorts(int refnum)
  184. {
  185. return fInputPort[refnum].GetItems();
  186. }
  187. /*!
  188. \brief Get the output port array of a given refnum.
  189. */
  190. const jack_int_t* JackConnectionManager::GetOutputPorts(int refnum)
  191. {
  192. return fOutputPort[refnum].GetItems();
  193. }
  194. /*!
  195. \brief Init the refnum.
  196. */
  197. void JackConnectionManager::InitRefNum(int refnum)
  198. {
  199. fInputPort[refnum].Init();
  200. fOutputPort[refnum].Init();
  201. fConnectionRef.Init(refnum);
  202. fInputCounter[refnum].SetValue(0);
  203. }
  204. /*!
  205. \brief Reset all clients activation.
  206. */
  207. void JackConnectionManager::ResetGraph(JackClientTiming* timing)
  208. {
  209. // Reset activation counter : must be done *before* starting to resume clients
  210. for (int i = 0; i < CLIENT_NUM; i++) {
  211. fInputCounter[i].Reset();
  212. timing[i].fStatus = NotTriggered;
  213. }
  214. }
  215. /*!
  216. \brief Wait on the input synchro.
  217. */
  218. int JackConnectionManager::SuspendRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing, long time_out_usec)
  219. {
  220. bool res;
  221. if ((res = table[control->fRefNum]->TimedWait(time_out_usec))) {
  222. timing[control->fRefNum].fStatus = Running;
  223. timing[control->fRefNum].fAwakeAt = GetMicroSeconds();
  224. }
  225. return (res) ? 0 : -1;
  226. }
  227. /*!
  228. \brief Signal clients connected to the given client.
  229. */
  230. int JackConnectionManager::ResumeRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing)
  231. {
  232. jack_time_t current_date = GetMicroSeconds();
  233. const jack_int_t* outputRef = fConnectionRef.GetItems(control->fRefNum);
  234. int res = 0;
  235. // Update state and timestamp of current client
  236. timing[control->fRefNum].fStatus = Finished;
  237. timing[control->fRefNum].fFinishedAt = current_date;
  238. for (int i = 0; i < CLIENT_NUM; i++) {
  239. // Signal connected clients or drivers
  240. if (outputRef[i] > 0) {
  241. // Update state and timestamp of destination clients
  242. timing[i].fStatus = Triggered;
  243. timing[i].fSignaledAt = current_date;
  244. if (!fInputCounter[i].Signal(table[i], control)) {
  245. JackLog("JackConnectionManager::ResumeRefNum error: ref = %ld output = %ld \n", control->fRefNum, i);
  246. res = -1;
  247. }
  248. }
  249. }
  250. return res;
  251. }
  252. /*!
  253. \brief Increment the number of ports between 2 clients, if the 2 clients become connected, then the Activation counter is updated.
  254. */
  255. void JackConnectionManager::IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
  256. {
  257. int ref1 = GetOutputRefNum(port_src);
  258. int ref2 = GetInputRefNum(port_dst);
  259. assert(ref1 >= 0 && ref2 >= 0);
  260. DirectConnect(ref1, ref2);
  261. JackLog("JackConnectionManager::IncConnectionRef: ref1 = %ld ref2 = %ld\n", ref1, ref2);
  262. }
  263. /*!
  264. \brief Decrement the number of ports between 2 clients, if the 2 clients become disconnected, then the Activation counter is updated.
  265. */
  266. void JackConnectionManager::DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
  267. {
  268. int ref1 = GetOutputRefNum(port_src);
  269. int ref2 = GetInputRefNum(port_dst);
  270. assert(ref1 >= 0 && ref2 >= 0);
  271. DirectDisconnect(ref1, ref2);
  272. JackLog("JackConnectionManager::DecConnectionRef: ref1 = %ld ref2 = %ld\n", ref1, ref2);
  273. }
  274. /*!
  275. \brief Directly connect 2 reference numbers.
  276. */
  277. void JackConnectionManager::DirectConnect(int ref1, int ref2)
  278. {
  279. assert(ref1 >= 0 && ref2 >= 0);
  280. if (fConnectionRef.IncItem(ref1, ref2) == 1) { // First connection between client ref1 and client ref2
  281. JackLog("JackConnectionManager::DirectConnect first: ref1 = %ld ref2 = %ld\n", ref1, ref2);
  282. fInputCounter[ref2].IncValue();
  283. }
  284. }
  285. /*!
  286. \brief Directly disconnect 2 reference numbers.
  287. */
  288. void JackConnectionManager::DirectDisconnect(int ref1, int ref2)
  289. {
  290. assert(ref1 >= 0 && ref2 >= 0);
  291. if (fConnectionRef.DecItem(ref1, ref2) == 0) { // Last connection between client ref1 and client ref2
  292. JackLog("JackConnectionManager::DirectDisconnect last: ref1 = %ld ref2 = %ld\n", ref1, ref2);
  293. fInputCounter[ref2].DecValue();
  294. }
  295. }
  296. /*!
  297. \brief Returns the connections state between 2 refnum.
  298. */
  299. bool JackConnectionManager::IsDirectConnection(int ref1, int ref2) const
  300. {
  301. assert(ref1 >= 0 && ref2 >= 0);
  302. return fConnectionRef.GetItemCount(ref1, ref2);
  303. }
  304. /*!
  305. \brief Get the client refnum of a given input port.
  306. */
  307. int JackConnectionManager::GetInputRefNum(jack_port_id_t port_index) const
  308. {
  309. for (int i = 0; i < CLIENT_NUM; i++) {
  310. if (fInputPort[i].CheckItem(port_index))
  311. return i;
  312. }
  313. return -1;
  314. }
  315. /*!
  316. \brief Get the client refnum of a given ouput port.
  317. */
  318. int JackConnectionManager::GetOutputRefNum(jack_port_id_t port_index) const
  319. {
  320. for (int i = 0; i < CLIENT_NUM; i++) {
  321. if (fOutputPort[i].CheckItem(port_index))
  322. return i;
  323. }
  324. return -1;
  325. }
  326. /*!
  327. \brief Test is a connection path exists between port_src and port_dst.
  328. */
  329. bool JackConnectionManager::IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const
  330. {
  331. return IsLoopPathAux(GetInputRefNum(port_dst), GetOutputRefNum(port_src));
  332. }
  333. bool JackConnectionManager::IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const
  334. {
  335. return (fLoopFeedback.GetConnectionIndex(GetOutputRefNum(port_src), GetInputRefNum(port_dst)) >= 0);
  336. }
  337. bool JackConnectionManager::IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
  338. {
  339. int ref1 = GetOutputRefNum(port_src);
  340. int ref2 = GetInputRefNum(port_dst);
  341. // Add an activation connection in the other direction
  342. JackLog("JackConnectionManager::IncFeedbackConnection ref1 = %ld ref2 = %ld\n", ref1, ref2);
  343. assert(ref1 >= 0 && ref2 >= 0);
  344. if (ref1 != ref2)
  345. DirectConnect(ref2, ref1);
  346. return fLoopFeedback.IncConnection(ref1, ref2); // Add the feedback connection
  347. }
  348. bool JackConnectionManager::DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
  349. {
  350. int ref1 = GetOutputRefNum(port_src);
  351. int ref2 = GetInputRefNum(port_dst);
  352. // Remove an activation connection in the other direction
  353. JackLog("JackConnectionManager::DecFeedbackConnection ref1 = %ld ref2 = %ld\n", ref1, ref2);
  354. assert(ref1 >= 0 && ref2 >= 0);
  355. if (ref1 != ref2)
  356. DirectDisconnect(ref2, ref1);
  357. return fLoopFeedback.DecConnection(ref1, ref2); // Remove the feedback connection
  358. }
  359. } // end of namespace