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 Lesser General Public License as published by
  5. the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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. jack_log("JackConnectionManager::InitConnections size = %ld ", sizeof(JackConnectionManager));
  26. for (i = 0; i < PORT_NUM; i++) {
  27. fConnection[i].Init();
  28. }
  29. fLoopFeedback.Init();
  30. jack_log("JackConnectionManager::InitClients");
  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. jack_log("JackConnectionManager::IsLoopPathAux ref1 = %ld ref2 = %ld", 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. jack_log("JackConnectionManager::Connect port_src = %ld port_dst = %ld", 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. jack_log("JackConnectionManager::Disconnect port_src = %ld port_dst = %ld", 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_FOR_PORT);
  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. jack_log("JackConnectionManager::AddInputPort ref = %ld port = %ld", 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. jack_log("JackConnectionManager::AddOutputPort ref = %ld port = %ld", 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. jack_log("JackConnectionManager::RemoveInputPort ref = %ld port_index = %ld ", 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. jack_log("JackConnectionManager::RemoveOutputPort ref = %ld port_index = %ld ", 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. jack_log("JackConnectionManager::ResumeRefNum error: ref = %ld output = %ld ", 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. jack_log("JackConnectionManager::IncConnectionRef: ref1 = %ld ref2 = %ld", 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. jack_log("JackConnectionManager::DecConnectionRef: ref1 = %ld ref2 = %ld", 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. jack_log("JackConnectionManager::DirectConnect first: ref1 = %ld ref2 = %ld", 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. jack_log("JackConnectionManager::DirectDisconnect last: ref1 = %ld ref2 = %ld", 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) > 0);
  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. jack_log("JackConnectionManager::IncFeedbackConnection ref1 = %ld ref2 = %ld", 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. jack_log("JackConnectionManager::DecFeedbackConnection ref1 = %ld ref2 = %ld", 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