Jack2  1.9.9
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  return false;
76  }
77  }
78 }
79 
80 //--------------
81 // External API
82 //--------------
83 
87 int JackConnectionManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst)
88 {
89  jack_log("JackConnectionManager::Connect port_src = %ld port_dst = %ld", port_src, port_dst);
90 
91  if (fConnection[port_src].AddItem(port_dst)) {
92  return 0;
93  } else {
94  jack_error("Connection table is full !!");
95  return -1;
96  }
97 }
98 
102 int JackConnectionManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst)
103 {
104  jack_log("JackConnectionManager::Disconnect port_src = %ld port_dst = %ld", port_src, port_dst);
105 
106  if (fConnection[port_src].RemoveItem(port_dst)) {
107  return 0;
108  } else {
109  jack_error("Connection not found !!");
110  return -1;
111  }
112 }
113 
117 bool JackConnectionManager::IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const
118 {
119  return fConnection[port_src].CheckItem(port_dst);
120 }
121 
125 const jack_int_t* JackConnectionManager::GetConnections(jack_port_id_t port_index) const
126 {
127  return fConnection[port_index].GetItems();
128 }
129 
130 //------------------------
131 // Client port management
132 //------------------------
133 
137 int JackConnectionManager::AddInputPort(int refnum, jack_port_id_t port_index)
138 {
139  if (fInputPort[refnum].AddItem(port_index)) {
140  jack_log("JackConnectionManager::AddInputPort ref = %ld port = %ld", refnum, port_index);
141  return 0;
142  } else {
143  jack_error("Maximum number of input ports is reached for application ref = %ld", refnum);
144  return -1;
145  }
146 }
147 
151 int JackConnectionManager::AddOutputPort(int refnum, jack_port_id_t port_index)
152 {
153  if (fOutputPort[refnum].AddItem(port_index)) {
154  jack_log("JackConnectionManager::AddOutputPort ref = %ld port = %ld", 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 
165 int JackConnectionManager::RemoveInputPort(int refnum, jack_port_id_t port_index)
166 {
167  jack_log("JackConnectionManager::RemoveInputPort ref = %ld port_index = %ld ", refnum, port_index);
168 
169  if (fInputPort[refnum].RemoveItem(port_index)) {
170  return 0;
171  } else {
172  jack_error("Input port index = %ld not found for application ref = %ld", port_index, refnum);
173  return -1;
174  }
175 }
176 
180 int JackConnectionManager::RemoveOutputPort(int refnum, jack_port_id_t port_index)
181 {
182  jack_log("JackConnectionManager::RemoveOutputPort ref = %ld port_index = %ld ", refnum, port_index);
183 
184  if (fOutputPort[refnum].RemoveItem(port_index)) {
185  return 0;
186  } else {
187  jack_error("Output port index = %ld not found for application ref = %ld", port_index, refnum);
188  return -1;
189  }
190 }
191 
195 const jack_int_t* JackConnectionManager::GetInputPorts(int refnum)
196 {
197  return fInputPort[refnum].GetItems();
198 }
199 
203 const jack_int_t* JackConnectionManager::GetOutputPorts(int refnum)
204 {
205  return fOutputPort[refnum].GetItems();
206 }
207 
212 {
213  fInputPort[refnum].Init();
214  fOutputPort[refnum].Init();
215  fConnectionRef.Init(refnum);
216  fInputCounter[refnum].SetValue(0);
217 }
218 
223 {
224  // Reset activation counter : must be done *before* starting to resume clients
225  for (int i = 0; i < CLIENT_NUM; i++) {
226  fInputCounter[i].Reset();
227  timing[i].fStatus = NotTriggered;
228  }
229 }
230 
234 int JackConnectionManager::SuspendRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing, long time_out_usec)
235 {
236  bool res;
237  if ((res = table[control->fRefNum].TimedWait(time_out_usec))) {
238  timing[control->fRefNum].fStatus = Running;
239  timing[control->fRefNum].fAwakeAt = GetMicroSeconds();
240  }
241  return (res) ? 0 : -1;
242 }
243 
248 {
249  jack_time_t current_date = GetMicroSeconds();
250  const jack_int_t* output_ref = fConnectionRef.GetItems(control->fRefNum);
251  int res = 0;
252 
253  // Update state and timestamp of current client
254  timing[control->fRefNum].fStatus = Finished;
255  timing[control->fRefNum].fFinishedAt = current_date;
256 
257  for (int i = 0; i < CLIENT_NUM; i++) {
258 
259  // Signal connected clients or drivers
260  if (output_ref[i] > 0) {
261 
262  // Update state and timestamp of destination clients
263  timing[i].fStatus = Triggered;
264  timing[i].fSignaledAt = current_date;
265 
266  if (!fInputCounter[i].Signal(table + i, control)) {
267  jack_log("JackConnectionManager::ResumeRefNum error: ref = %ld output = %ld ", control->fRefNum, i);
268  res = -1;
269  }
270  }
271  }
272 
273  return res;
274 }
275 
276 static bool HasNoConnection(jack_int_t* table)
277 {
278  for (int ref = 0; ref < CLIENT_NUM; ref++) {
279  if (table[ref] > 0) return false;
280  }
281  return true;
282 }
283 
284 // Using http://en.wikipedia.org/wiki/Topological_sorting
285 
286 void JackConnectionManager::TopologicalSort(std::vector<jack_int_t>& sorted)
287 {
288  JackFixedMatrix<CLIENT_NUM> tmp;
289  std::set<jack_int_t> level;
290 
291  fConnectionRef.Copy(tmp);
292 
293  // Inputs of the graph
294  level.insert(AUDIO_DRIVER_REFNUM);
295  level.insert(FREEWHEEL_DRIVER_REFNUM);
296 
297  while (level.size() > 0) {
298  jack_int_t refnum = *level.begin();
299  sorted.push_back(refnum);
300  level.erase(level.begin());
301  const jack_int_t* output_ref1 = tmp.GetItems(refnum);
302  for (int dst = 0; dst < CLIENT_NUM; dst++) {
303  if (output_ref1[dst] > 0) {
304  tmp.ClearItem(refnum, dst);
305  jack_int_t output_ref2[CLIENT_NUM];
306  tmp.GetOutputTable1(dst, output_ref2);
307  if (HasNoConnection(output_ref2))
308  level.insert(dst);
309  }
310  }
311  }
312 }
313 
317 void JackConnectionManager::IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
318 {
319  int ref1 = GetOutputRefNum(port_src);
320  int ref2 = GetInputRefNum(port_dst);
321 
322  assert(ref1 >= 0 && ref2 >= 0);
323 
324  DirectConnect(ref1, ref2);
325  jack_log("JackConnectionManager::IncConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2);
326 }
327 
331 void JackConnectionManager::DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
332 {
333  int ref1 = GetOutputRefNum(port_src);
334  int ref2 = GetInputRefNum(port_dst);
335 
336  assert(ref1 >= 0 && ref2 >= 0);
337 
338  DirectDisconnect(ref1, ref2);
339  jack_log("JackConnectionManager::DecConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2);
340 }
341 
345 void JackConnectionManager::DirectConnect(int ref1, int ref2)
346 {
347  assert(ref1 >= 0 && ref2 >= 0);
348 
349  if (fConnectionRef.IncItem(ref1, ref2) == 1) { // First connection between client ref1 and client ref2
350  jack_log("JackConnectionManager::DirectConnect first: ref1 = %ld ref2 = %ld", ref1, ref2);
351  fInputCounter[ref2].IncValue();
352  }
353 }
354 
359 {
360  assert(ref1 >= 0 && ref2 >= 0);
361 
362  if (fConnectionRef.DecItem(ref1, ref2) == 0) { // Last connection between client ref1 and client ref2
363  jack_log("JackConnectionManager::DirectDisconnect last: ref1 = %ld ref2 = %ld", ref1, ref2);
364  fInputCounter[ref2].DecValue();
365  }
366 }
367 
371 bool JackConnectionManager::IsDirectConnection(int ref1, int ref2) const
372 {
373  assert(ref1 >= 0 && ref2 >= 0);
374  return (fConnectionRef.GetItemCount(ref1, ref2) > 0);
375 }
376 
380 int JackConnectionManager::GetInputRefNum(jack_port_id_t port_index) const
381 {
382  for (int i = 0; i < CLIENT_NUM; i++) {
383  if (fInputPort[i].CheckItem(port_index))
384  return i;
385  }
386 
387  return -1;
388 }
389 
393 int JackConnectionManager::GetOutputRefNum(jack_port_id_t port_index) const
394 {
395  for (int i = 0; i < CLIENT_NUM; i++) {
396  if (fOutputPort[i].CheckItem(port_index))
397  return i;
398  }
399 
400  return -1;
401 }
402 
406 bool JackConnectionManager::IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const
407 {
408  return IsLoopPathAux(GetInputRefNum(port_dst), GetOutputRefNum(port_src));
409 }
410 
411 bool JackConnectionManager::IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const
412 {
413  return (fLoopFeedback.GetConnectionIndex(GetOutputRefNum(port_src), GetInputRefNum(port_dst)) >= 0);
414 }
415 
416 bool JackConnectionManager::IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
417 {
418  int ref1 = GetOutputRefNum(port_src);
419  int ref2 = GetInputRefNum(port_dst);
420 
421  // Add an activation connection in the other direction
422  jack_log("JackConnectionManager::IncFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2);
423  assert(ref1 >= 0 && ref2 >= 0);
424 
425  if (ref1 != ref2)
426  DirectConnect(ref2, ref1);
427 
428  return fLoopFeedback.IncConnection(ref1, ref2); // Add the feedback connection
429 }
430 
431 bool JackConnectionManager::DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
432 {
433  int ref1 = GetOutputRefNum(port_src);
434  int ref2 = GetInputRefNum(port_dst);
435 
436  // Remove an activation connection in the other direction
437  jack_log("JackConnectionManager::DecFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2);
438  assert(ref1 >= 0 && ref2 >= 0);
439 
440  if (ref1 != ref2)
441  DirectDisconnect(ref2, ref1);
442 
443  return fLoopFeedback.DecConnection(ref1, ref2); // Remove the feedback connection
444 }
445 
446 } // end of namespace
447 
448 
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.
Inter process synchronization using using Mach semaphore.
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.
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:91
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:107
const jack_int_t * GetInputPorts(int refnum)
Get the input port array of a given refnum.
Client control possibly in shared memory.