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.

461 lines
13KB

  1. /*
  2. Copyright (C) 2004-2006 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 Return the first available refnum.
  196. */
  197. /*
  198. int JackConnectionManager::AllocateRefNum()
  199. {
  200. for (int i = 0; i < CLIENT_NUM; i++) {
  201. if (fInputPort[i].IsAvailable()) {
  202. JackLog("JackConnectionManager::AllocateRefNum ref = %ld\n", i);
  203. return i;
  204. }
  205. }
  206. return -1;
  207. }
  208. */
  209. /*!
  210. \brief Release the refnum.
  211. */
  212. /*
  213. void JackConnectionManager::ReleaseRefNum(int refnum)
  214. {
  215. JackLog("JackConnectionManager::ReleaseRefNum ref = %ld\n", refnum);
  216. InitClient(refnum);
  217. }
  218. */
  219. /*!
  220. \brief Release the refnum.
  221. */
  222. void JackConnectionManager::InitRefNum(int refnum)
  223. {
  224. fInputPort[refnum].Init();
  225. fOutputPort[refnum].Init();
  226. fConnectionRef.Init(refnum);
  227. fInputCounter[refnum].SetValue(0);
  228. }
  229. /*!
  230. \brief Reset all clients activation.
  231. */
  232. void JackConnectionManager::ResetGraph(JackClientTiming* timing)
  233. {
  234. // Reset activation counter : must be done *before* starting to resume clients
  235. for (int i = 0; i < CLIENT_NUM; i++) {
  236. fInputCounter[i].Reset();
  237. timing[i].fStatus = NotTriggered;
  238. }
  239. }
  240. /*!
  241. \brief Wait on the input synchro.
  242. */
  243. int JackConnectionManager::SuspendRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing, long time_out_usec)
  244. {
  245. bool res;
  246. if ((res = table[control->fRefNum]->TimedWait(time_out_usec))) {
  247. timing[control->fRefNum].fStatus = Running;
  248. timing[control->fRefNum].fAwakeAt = GetMicroSeconds();
  249. }
  250. return (res) ? 0 : -1;
  251. }
  252. /*!
  253. \brief Signal clients connected to the given client.
  254. */
  255. int JackConnectionManager::ResumeRefNum(JackClientControl* control, JackSynchro** table, JackClientTiming* timing)
  256. {
  257. jack_time_t current_date = GetMicroSeconds();
  258. const jack_int_t* outputRef = fConnectionRef.GetItems(control->fRefNum);
  259. int res = 0;
  260. // Update state and timestamp of current client
  261. timing[control->fRefNum].fStatus = Finished;
  262. timing[control->fRefNum].fFinishedAt = current_date;
  263. for (int i = 0; i < CLIENT_NUM; i++) {
  264. // Signal connected clients or drivers
  265. if (outputRef[i] > 0) {
  266. // Update state and timestamp of destination clients
  267. timing[i].fStatus = Triggered;
  268. timing[i].fSignaledAt = current_date;
  269. if (!fInputCounter[i].Signal(table[i], control)) {
  270. JackLog("JackConnectionManager::ResumeRefNum error: ref = %ld output = %ld \n", control->fRefNum, i);
  271. res = -1;
  272. }
  273. }
  274. }
  275. return res;
  276. }
  277. /*!
  278. \brief Increment the number of ports between 2 clients, if the 2 clients become connected, then the Activation counter is updated.
  279. */
  280. void JackConnectionManager::IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
  281. {
  282. int ref1 = GetOutputRefNum(port_src);
  283. int ref2 = GetInputRefNum(port_dst);
  284. assert(ref1 >= 0 && ref2 >= 0);
  285. DirectConnect(ref1, ref2);
  286. JackLog("JackConnectionManager::IncConnectionRef: ref1 = %ld ref2 = %ld\n", ref1, ref2);
  287. }
  288. /*!
  289. \brief Decrement the number of ports between 2 clients, if the 2 clients become disconnected, then the Activation counter is updated.
  290. */
  291. void JackConnectionManager::DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
  292. {
  293. int ref1 = GetOutputRefNum(port_src);
  294. int ref2 = GetInputRefNum(port_dst);
  295. assert(ref1 >= 0 && ref2 >= 0);
  296. DirectDisconnect(ref1, ref2);
  297. JackLog("JackConnectionManager::DecConnectionRef: ref1 = %ld ref2 = %ld\n", ref1, ref2);
  298. }
  299. /*!
  300. \brief Directly connect 2 reference numbers.
  301. */
  302. void JackConnectionManager::DirectConnect(int ref1, int ref2)
  303. {
  304. assert(ref1 >= 0 && ref2 >= 0);
  305. if (fConnectionRef.IncItem(ref1, ref2) == 1) { // First connection between client ref1 and client ref2
  306. JackLog("JackConnectionManager::DirectConnect first: ref1 = %ld ref2 = %ld\n", ref1, ref2);
  307. fInputCounter[ref2].IncValue();
  308. }
  309. }
  310. /*!
  311. \brief Directly disconnect 2 reference numbers.
  312. */
  313. void JackConnectionManager::DirectDisconnect(int ref1, int ref2)
  314. {
  315. assert(ref1 >= 0 && ref2 >= 0);
  316. if (fConnectionRef.DecItem(ref1, ref2) == 0) { // Last connection between client ref1 and client ref2
  317. JackLog("JackConnectionManager::DirectDisconnect last: ref1 = %ld ref2 = %ld\n", ref1, ref2);
  318. fInputCounter[ref2].DecValue();
  319. }
  320. }
  321. /*!
  322. \brief Returns the connections state between 2 refnum.
  323. */
  324. bool JackConnectionManager::IsDirectConnection(int ref1, int ref2) const
  325. {
  326. assert(ref1 >= 0 && ref2 >= 0);
  327. return fConnectionRef.GetItemCount(ref1, ref2);
  328. }
  329. /*!
  330. \brief Get the client refnum of a given input port.
  331. */
  332. int JackConnectionManager::GetInputRefNum(jack_port_id_t port_index) const
  333. {
  334. for (int i = 0; i < CLIENT_NUM; i++) {
  335. if (fInputPort[i].CheckItem(port_index))
  336. return i;
  337. }
  338. return -1;
  339. }
  340. /*!
  341. \brief Get the client refnum of a given ouput port.
  342. */
  343. int JackConnectionManager::GetOutputRefNum(jack_port_id_t port_index) const
  344. {
  345. for (int i = 0; i < CLIENT_NUM; i++) {
  346. if (fOutputPort[i].CheckItem(port_index))
  347. return i;
  348. }
  349. return -1;
  350. }
  351. /*!
  352. \brief Test is a connection path exists between port_src and port_dst.
  353. */
  354. bool JackConnectionManager::IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const
  355. {
  356. return IsLoopPathAux(GetInputRefNum(port_dst), GetOutputRefNum(port_src));
  357. }
  358. bool JackConnectionManager::IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const
  359. {
  360. return (fLoopFeedback.GetConnectionIndex(GetOutputRefNum(port_src), GetInputRefNum(port_dst)) >= 0);
  361. }
  362. bool JackConnectionManager::IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
  363. {
  364. int ref1 = GetOutputRefNum(port_src);
  365. int ref2 = GetInputRefNum(port_dst);
  366. // Add an activation connection in the other direction
  367. JackLog("JackConnectionManager::IncFeedbackConnection ref1 = %ld ref2 = %ld\n", ref1, ref2);
  368. assert(ref1 >= 0 && ref2 >= 0);
  369. if (ref1 != ref2)
  370. DirectConnect(ref2, ref1);
  371. return fLoopFeedback.IncConnection(ref1, ref2); // Add the feedback connection
  372. }
  373. bool JackConnectionManager::DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
  374. {
  375. int ref1 = GetOutputRefNum(port_src);
  376. int ref2 = GetInputRefNum(port_dst);
  377. // Remove an activation connection in the other direction
  378. JackLog("JackConnectionManager::DecFeedbackConnection ref1 = %ld ref2 = %ld\n", ref1, ref2);
  379. assert(ref1 >= 0 && ref2 >= 0);
  380. if (ref1 != ref2)
  381. DirectDisconnect(ref2, ref1);
  382. return fLoopFeedback.DecConnection(ref1, ref2); // Remove the feedback connection
  383. }
  384. } // end of namespace