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.

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