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.

460 lines
13KB

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