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.

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