Jack2  1.9.12
JackConnectionManager.cpp
1 /*
2 Copyright (C) 2004-2008 Grame
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13 
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 
18 */
19 
20 #include "JackConnectionManager.h"
21 #include "JackClientControl.h"
22 #include "JackEngineControl.h"
23 #include "JackGlobals.h"
24 #include "JackError.h"
25 #include <set>
26 #include <iostream>
27 #include <assert.h>
28 
29 namespace Jack
30 {
31 
32 JackConnectionManager::JackConnectionManager()
33 {
34  int i;
35  jack_log("JackConnectionManager::InitConnections size = %ld ", sizeof(JackConnectionManager));
36 
37  for (i = 0; i < PORT_NUM_MAX; i++) {
38  fConnection[i].Init();
39  }
40 
41  fLoopFeedback.Init();
42 
43  jack_log("JackConnectionManager::InitClients");
44  for (i = 0; i < CLIENT_NUM; i++) {
45  InitRefNum(i);
46  }
47 }
48 
49 JackConnectionManager::~JackConnectionManager()
50 {}
51 
52 //--------------
53 // Internal API
54 //--------------
55 
56 bool JackConnectionManager::IsLoopPathAux(int ref1, int ref2) const
57 {
58  jack_log("JackConnectionManager::IsLoopPathAux ref1 = %ld ref2 = %ld", ref1, ref2);
59 
60  if (ref1 < GetEngineControl()->fDriverNum || ref2 < GetEngineControl()->fDriverNum) {
61  return false;
62  } else if (ref1 == ref2) { // Same refnum
63  return true;
64  } else {
65  jack_int_t output[CLIENT_NUM];
66  fConnectionRef.GetOutputTable(ref1, output);
67 
68  if (fConnectionRef.IsInsideTable(ref2, output)) { // If ref2 is contained in the outputs of ref1
69  return true;
70  } else {
71  for (int i = 0; i < CLIENT_NUM && output[i] != EMPTY; i++) { // Otherwise recurse for all ref1 outputs
72  if (IsLoopPathAux(output[i], ref2)) {
73  return true; // Stop when a path is found
74  }
75  }
76  return false;
77  }
78  }
79 }
80 
81 //--------------
82 // External API
83 //--------------
84 
88 int JackConnectionManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst)
89 {
90  jack_log("JackConnectionManager::Connect port_src = %ld port_dst = %ld", port_src, port_dst);
91 
92  if (fConnection[port_src].AddItem(port_dst)) {
93  return 0;
94  } else {
95  jack_error("Connection table is full !!");
96  return -1;
97  }
98 }
99 
103 int JackConnectionManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst)
104 {
105  jack_log("JackConnectionManager::Disconnect port_src = %ld port_dst = %ld", port_src, port_dst);
106 
107  if (fConnection[port_src].RemoveItem(port_dst)) {
108  return 0;
109  } else {
110  jack_error("Connection not found !!");
111  return -1;
112  }
113 }
114 
118 bool JackConnectionManager::IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const
119 {
120  return fConnection[port_src].CheckItem(port_dst);
121 }
122 
126 const jack_int_t* JackConnectionManager::GetConnections(jack_port_id_t port_index) const
127 {
128  return fConnection[port_index].GetItems();
129 }
130 
131 //------------------------
132 // Client port management
133 //------------------------
134 
138 int JackConnectionManager::AddInputPort(int refnum, jack_port_id_t port_index)
139 {
140  if (fInputPort[refnum].AddItem(port_index)) {
141  jack_log("JackConnectionManager::AddInputPort ref = %ld port = %ld", 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 
152 int JackConnectionManager::AddOutputPort(int refnum, jack_port_id_t port_index)
153 {
154  if (fOutputPort[refnum].AddItem(port_index)) {
155  jack_log("JackConnectionManager::AddOutputPort ref = %ld port = %ld", refnum, port_index);
156  return 0;
157  } else {
158  jack_error("Maximum number of output ports is reached for application ref = %ld", refnum);
159  return -1;
160  }
161 }
162 
166 int JackConnectionManager::RemoveInputPort(int refnum, jack_port_id_t port_index)
167 {
168  jack_log("JackConnectionManager::RemoveInputPort ref = %ld port_index = %ld ", refnum, port_index);
169 
170  if (fInputPort[refnum].RemoveItem(port_index)) {
171  return 0;
172  } else {
173  jack_error("Input port index = %ld not found for application ref = %ld", port_index, refnum);
174  return -1;
175  }
176 }
177 
181 int JackConnectionManager::RemoveOutputPort(int refnum, jack_port_id_t port_index)
182 {
183  jack_log("JackConnectionManager::RemoveOutputPort ref = %ld port_index = %ld ", refnum, port_index);
184 
185  if (fOutputPort[refnum].RemoveItem(port_index)) {
186  return 0;
187  } else {
188  jack_error("Output port index = %ld not found for application ref = %ld", port_index, refnum);
189  return -1;
190  }
191 }
192 
196 const jack_int_t* JackConnectionManager::GetInputPorts(int refnum)
197 {
198  return fInputPort[refnum].GetItems();
199 }
200 
204 const jack_int_t* JackConnectionManager::GetOutputPorts(int refnum)
205 {
206  return fOutputPort[refnum].GetItems();
207 }
208 
213 {
214  fInputPort[refnum].Init();
215  fOutputPort[refnum].Init();
216  fConnectionRef.Init(refnum);
217  fInputCounter[refnum].SetValue(0);
218 }
219 
224 {
225  // Reset activation counter : must be done *before* starting to resume clients
226  for (int i = 0; i < CLIENT_NUM; i++) {
227  fInputCounter[i].Reset();
228  timing[i].fStatus = NotTriggered;
229  }
230 }
231 
235 int JackConnectionManager::SuspendRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing, long time_out_usec)
236 {
237  bool res;
238  if ((res = table[control->fRefNum].TimedWait(time_out_usec))) {
239  timing[control->fRefNum].fStatus = Running;
240  timing[control->fRefNum].fAwakeAt = GetMicroSeconds();
241  }
242  return (res) ? 0 : -1;
243 }
244 
249 {
250  jack_time_t current_date = GetMicroSeconds();
251  const jack_int_t* output_ref = fConnectionRef.GetItems(control->fRefNum);
252  int res = 0;
253 
254  // Update state and timestamp of current client
255  timing[control->fRefNum].fStatus = Finished;
256  timing[control->fRefNum].fFinishedAt = current_date;
257 
258  for (int i = 0; i < CLIENT_NUM; i++) {
259 
260  // Signal connected clients or drivers
261  if (output_ref[i] > 0) {
262 
263  // Update state and timestamp of destination clients
264  timing[i].fStatus = Triggered;
265  timing[i].fSignaledAt = current_date;
266 
267  if (!fInputCounter[i].Signal(table + i, control)) {
268  jack_log("JackConnectionManager::ResumeRefNum error: ref = %ld output = %ld ", control->fRefNum, i);
269  res = -1;
270  }
271  }
272  }
273 
274  return res;
275 }
276 
277 static bool HasNoConnection(jack_int_t* table)
278 {
279  for (int ref = 0; ref < CLIENT_NUM; ref++) {
280  if (table[ref] > 0) return false;
281  }
282  return true;
283 }
284 
285 // Using http://en.wikipedia.org/wiki/Topological_sorting
286 
287 void JackConnectionManager::TopologicalSort(std::vector<jack_int_t>& sorted)
288 {
289  JackFixedMatrix<CLIENT_NUM>* tmp = new JackFixedMatrix<CLIENT_NUM>;
290  std::set<jack_int_t> level;
291 
292  fConnectionRef.Copy(*tmp);
293 
294  // Inputs of the graph
295  level.insert(AUDIO_DRIVER_REFNUM);
296  level.insert(FREEWHEEL_DRIVER_REFNUM);
297 
298  while (level.size() > 0) {
299  jack_int_t refnum = *level.begin();
300  sorted.push_back(refnum);
301  level.erase(level.begin());
302  const jack_int_t* output_ref1 = tmp->GetItems(refnum);
303  for (int dst = 0; dst < CLIENT_NUM; dst++) {
304  if (output_ref1[dst] > 0) {
305  tmp->ClearItem(refnum, dst);
306  jack_int_t output_ref2[CLIENT_NUM];
307  tmp->GetOutputTable1(dst, output_ref2);
308  if (HasNoConnection(output_ref2)) {
309  level.insert(dst);
310  }
311  }
312  }
313  }
314 
315  delete tmp;
316 }
317 
321 void JackConnectionManager::IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
322 {
323  int ref1 = GetOutputRefNum(port_src);
324  int ref2 = GetInputRefNum(port_dst);
325 
326  assert(ref1 >= 0 && ref2 >= 0);
327 
328  DirectConnect(ref1, ref2);
329  jack_log("JackConnectionManager::IncConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2);
330 }
331 
335 void JackConnectionManager::DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
336 {
337  int ref1 = GetOutputRefNum(port_src);
338  int ref2 = GetInputRefNum(port_dst);
339 
340  assert(ref1 >= 0 && ref2 >= 0);
341 
342  DirectDisconnect(ref1, ref2);
343  jack_log("JackConnectionManager::DecConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2);
344 }
345 
349 void JackConnectionManager::DirectConnect(int ref1, int ref2)
350 {
351  assert(ref1 >= 0 && ref2 >= 0);
352 
353  if (fConnectionRef.IncItem(ref1, ref2) == 1) { // First connection between client ref1 and client ref2
354  jack_log("JackConnectionManager::DirectConnect first: ref1 = %ld ref2 = %ld", ref1, ref2);
355  fInputCounter[ref2].IncValue();
356  }
357 }
358 
363 {
364  assert(ref1 >= 0 && ref2 >= 0);
365 
366  if (fConnectionRef.DecItem(ref1, ref2) == 0) { // Last connection between client ref1 and client ref2
367  jack_log("JackConnectionManager::DirectDisconnect last: ref1 = %ld ref2 = %ld", ref1, ref2);
368  fInputCounter[ref2].DecValue();
369  }
370 }
371 
375 bool JackConnectionManager::IsDirectConnection(int ref1, int ref2) const
376 {
377  assert(ref1 >= 0 && ref2 >= 0);
378  return (fConnectionRef.GetItemCount(ref1, ref2) > 0);
379 }
380 
384 int JackConnectionManager::GetInputRefNum(jack_port_id_t port_index) const
385 {
386  for (int i = 0; i < CLIENT_NUM; i++) {
387  if (fInputPort[i].CheckItem(port_index)) {
388  return i;
389  }
390  }
391 
392  return -1;
393 }
394 
398 int JackConnectionManager::GetOutputRefNum(jack_port_id_t port_index) const
399 {
400  for (int i = 0; i < CLIENT_NUM; i++) {
401  if (fOutputPort[i].CheckItem(port_index)) {
402  return i;
403  }
404  }
405 
406  return -1;
407 }
408 
412 bool JackConnectionManager::IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const
413 {
414  return IsLoopPathAux(GetInputRefNum(port_dst), GetOutputRefNum(port_src));
415 }
416 
417 bool JackConnectionManager::IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const
418 {
419  return (fLoopFeedback.GetConnectionIndex(GetOutputRefNum(port_src), GetInputRefNum(port_dst)) >= 0);
420 }
421 
422 bool JackConnectionManager::IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
423 {
424  int ref1 = GetOutputRefNum(port_src);
425  int ref2 = GetInputRefNum(port_dst);
426 
427  // Add an activation connection in the other direction
428  jack_log("JackConnectionManager::IncFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2);
429  assert(ref1 >= 0 && ref2 >= 0);
430 
431  if (ref1 != ref2) {
432  DirectConnect(ref2, ref1);
433  }
434 
435  return fLoopFeedback.IncConnection(ref1, ref2); // Add the feedback connection
436 }
437 
438 bool JackConnectionManager::DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
439 {
440  int ref1 = GetOutputRefNum(port_src);
441  int ref2 = GetInputRefNum(port_dst);
442 
443  // Remove an activation connection in the other direction
444  jack_log("JackConnectionManager::DecFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2);
445  assert(ref1 >= 0 && ref2 >= 0);
446 
447  if (ref1 != ref2) {
448  DirectDisconnect(ref2, ref1);
449  }
450 
451  return fLoopFeedback.DecConnection(ref1, ref2); // Remove the feedback connection
452 }
453 
454 } // end of namespace
455 
456 
void DirectDisconnect(int ref1, int ref2)
Directly disconnect 2 reference numbers.
bool IsDirectConnection(int ref1, int ref2) const
Returns the connections state between 2 refnum.
bool IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const
Test is a connection path exists between port_src and port_dst.
int Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst)
Disconnect port_src from port_dst.
int GetOutputRefNum(jack_port_id_t port_index) const
Get the client refnum of a given ouput port.
void IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
Increment the number of ports between 2 clients, if the 2 clients become connected, then the Activation counter is updated.
int SuspendRefNum(JackClientControl *control, JackSynchro *table, JackClientTiming *timing, long time_out_usec)
Wait on the input synchro.
Inter process synchronization using POSIX semaphore.
void ResetGraph(JackClientTiming *timing)
Reset all clients activation.
int GetConnectionIndex(int ref1, int ref2) const
Test if a connection between 2 refnum is a feedback connection.
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:92
int RemoveInputPort(int refnum, jack_port_id_t port_index)
Remove an input port from a client.
int AddOutputPort(int refnum, jack_port_id_t port_index)
Add an output port to a client.
int GetInputRefNum(jack_port_id_t port_index) const
Get the client refnum of a given input port.
void DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
Decrement the number of ports between 2 clients, if the 2 clients become disconnected, then the Activation counter is updated.
int Connect(jack_port_id_t port_src, jack_port_id_t port_dst)
Connect port_src to port_dst.
void GetOutputTable(jack_int_t index, jack_int_t *output) const
Get the output indexes of a given index.
const jack_int_t * GetOutputPorts(int refnum)
Get the output port array of a given refnum.
bool IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const
Check if port_src and port_dst are connected.
void DirectConnect(int ref1, int ref2)
Directly connect 2 reference numbers.
const jack_int_t * GetConnections(jack_port_id_t port_index) const
Get the connection port array.
int RemoveOutputPort(int refnum, jack_port_id_t port_index)
Remove an output port from a client.
For client timing measurements.
void InitRefNum(int refnum)
Init the refnum.
int AddInputPort(int refnum, jack_port_id_t port_index)
Add an input port to a client.
int ResumeRefNum(JackClientControl *control, JackSynchro *table, JackClientTiming *timing)
Signal clients connected to the given client.
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:108
const jack_int_t * GetInputPorts(int refnum)
Get the input port array of a given refnum.
Client control possibly in shared memory.