1 /*
2 Copyright (C) 2001 Paul Davis
3 Copyright (C) 2004-2008 Grame
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14 
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 
19 */
20 
21 #include "JackGraphManager.h"
22 #include "JackConstants.h"
23 #include "JackError.h"
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <algorithm>
27 #ifdef HAVE_TRE_REGEX_H
28 #include <tre/regex.h>
29 #else
30 #include <regex.h>
31 #endif
32 
33 namespace Jack
34 {
35 
AssertBufferSize(jack_nframes_t buffer_size)36 static void AssertBufferSize(jack_nframes_t buffer_size)
37 {
38     if (buffer_size > BUFFER_SIZE_MAX) {
39         jack_log("JackGraphManager::AssertBufferSize frames = %ld", buffer_size);
40         assert(buffer_size <= BUFFER_SIZE_MAX);
41     }
42 }
43 
AssertPort(jack_port_id_t port_index)44 void JackGraphManager::AssertPort(jack_port_id_t port_index)
45 {
46     if (port_index >= fPortMax) {
47         jack_log("JackGraphManager::AssertPort port_index = %ld", port_index);
48         assert(port_index < fPortMax);
49     }
50 }
51 
Allocate(int port_max)52 JackGraphManager* JackGraphManager::Allocate(int port_max)
53 {
54     // Using "Placement" new
55     void* shared_ptr = JackShmMem::operator new(sizeof(JackGraphManager) + port_max * sizeof(JackPort));
56     return new(shared_ptr) JackGraphManager(port_max);
57 }
58 
Destroy(JackGraphManager * manager)59 void JackGraphManager::Destroy(JackGraphManager* manager)
60 {
61     // "Placement" new was used
62     manager->~JackGraphManager();
63     JackShmMem::operator delete(manager);
64 }
65 
JackGraphManager(int port_max)66 JackGraphManager::JackGraphManager(int port_max)
67 {
68     assert(port_max <= PORT_NUM_MAX);
69 
70     for (int i = 0; i < port_max; i++) {
71         fPortArray[i].Release();
72     }
73 
74     fPortMax = port_max;
75 }
76 
GetPort(jack_port_id_t port_index)77 JackPort* JackGraphManager::GetPort(jack_port_id_t port_index)
78 {
79     AssertPort(port_index);
80     return &fPortArray[port_index];
81 }
82 
GetBuffer(jack_port_id_t port_index)83 jack_default_audio_sample_t* JackGraphManager::GetBuffer(jack_port_id_t port_index)
84 {
85     return fPortArray[port_index].GetBuffer();
86 }
87 
88 // Server
InitRefNum(int refnum)89 void JackGraphManager::InitRefNum(int refnum)
90 {
91     JackConnectionManager* manager = WriteNextStateStart();
92     manager->InitRefNum(refnum);
93     WriteNextStateStop();
94 }
95 
96 // RT
RunCurrentGraph()97 void JackGraphManager::RunCurrentGraph()
98 {
99     JackConnectionManager* manager = ReadCurrentState();
100     manager->ResetGraph(fClientTiming);
101 }
102 
103 // RT
RunNextGraph()104 bool JackGraphManager::RunNextGraph()
105 {
106     bool res;
107     JackConnectionManager* manager = TrySwitchState(&res);
108     manager->ResetGraph(fClientTiming);
109     return res;
110 }
111 
112 // RT
IsFinishedGraph()113 bool JackGraphManager::IsFinishedGraph()
114 {
115     JackConnectionManager* manager = ReadCurrentState();
116     return (manager->GetActivation(FREEWHEEL_DRIVER_REFNUM) == 0);
117 }
118 
119 // RT
ResumeRefNum(JackClientControl * control,JackSynchro * table)120 int JackGraphManager::ResumeRefNum(JackClientControl* control, JackSynchro* table)
121 {
122     JackConnectionManager* manager = ReadCurrentState();
123     return manager->ResumeRefNum(control, table, fClientTiming);
124 }
125 
126 // RT
SuspendRefNum(JackClientControl * control,JackSynchro * table,long usec)127 int JackGraphManager::SuspendRefNum(JackClientControl* control, JackSynchro* table, long usec)
128 {
129     JackConnectionManager* manager = ReadCurrentState();
130     return manager->SuspendRefNum(control, table, fClientTiming, usec);
131 }
132 
TopologicalSort(std::vector<jack_int_t> & sorted)133 void JackGraphManager::TopologicalSort(std::vector<jack_int_t>& sorted)
134 {
135     UInt16 cur_index;
136     UInt16 next_index;
137 
138     do {
139         cur_index = GetCurrentIndex();
140         sorted.clear();
141         ReadCurrentState()->TopologicalSort(sorted);
142         next_index = GetCurrentIndex();
143     } while (cur_index != next_index); // Until a coherent state has been read
144 }
145 
146 // Server
DirectConnect(int ref1,int ref2)147 void JackGraphManager::DirectConnect(int ref1, int ref2)
148 {
149     JackConnectionManager* manager = WriteNextStateStart();
150     manager->DirectConnect(ref1, ref2);
151     jack_log("JackGraphManager::ConnectRefNum cur_index = %ld ref1 = %ld ref2 = %ld", CurIndex(fCounter), ref1, ref2);
152     WriteNextStateStop();
153 }
154 
155 // Server
DirectDisconnect(int ref1,int ref2)156 void JackGraphManager::DirectDisconnect(int ref1, int ref2)
157 {
158     JackConnectionManager* manager = WriteNextStateStart();
159     manager->DirectDisconnect(ref1, ref2);
160     jack_log("JackGraphManager::DisconnectRefNum cur_index = %ld ref1 = %ld ref2 = %ld", CurIndex(fCounter), ref1, ref2);
161     WriteNextStateStop();
162 }
163 
164 // Server
IsDirectConnection(int ref1,int ref2)165 bool JackGraphManager::IsDirectConnection(int ref1, int ref2)
166 {
167     JackConnectionManager* manager = ReadCurrentState();
168     return manager->IsDirectConnection(ref1, ref2);
169 }
170 
171 // RT
GetBuffer(jack_port_id_t port_index,jack_nframes_t buffer_size)172 void* JackGraphManager::GetBuffer(jack_port_id_t port_index, jack_nframes_t buffer_size)
173 {
174     AssertPort(port_index);
175     AssertBufferSize(buffer_size);
176 
177     JackConnectionManager* manager = ReadCurrentState();
178     JackPort* port = GetPort(port_index);
179 
180     // This happens when a port has just been unregistered and is still used by the RT code
181     if (!port->IsUsed()) {
182         jack_log("JackGraphManager::GetBuffer : port = %ld is released state", port_index);
183         return GetBuffer(0); // port_index 0 is not used
184     }
185 
186     jack_int_t len = manager->Connections(port_index);
187 
188     // Output port
189     if (port->fFlags & JackPortIsOutput) {
190         return (port->fTied != NO_PORT) ? GetBuffer(port->fTied, buffer_size) : GetBuffer(port_index);
191     }
192 
193     // No connections : return a zero-filled buffer
194     if (len == 0) {
195         port->ClearBuffer(buffer_size);
196         return port->GetBuffer();
197 
198     // One connection
199     } else if (len == 1) {
200         jack_port_id_t src_index = manager->GetPort(port_index, 0);
201 
202         // Ports in same client : copy the buffer
203         if (GetPort(src_index)->GetRefNum() == port->GetRefNum()) {
204             void* buffers[1];
205             buffers[0] = GetBuffer(src_index, buffer_size);
206             port->MixBuffers(buffers, 1, buffer_size);
207             return port->GetBuffer();
208         // Otherwise, use zero-copy mode, just pass the buffer of the connected (output) port.
209         } else {
210             return GetBuffer(src_index, buffer_size);
211         }
212 
213     // Multiple connections : mix all buffers
214     } else {
215 
216         const jack_int_t* connections = manager->GetConnections(port_index);
217         void* buffers[CONNECTION_NUM_FOR_PORT];
218         jack_port_id_t src_index;
219         int i;
220 
221         for (i = 0; (i < CONNECTION_NUM_FOR_PORT) && ((src_index = connections[i]) != EMPTY); i++) {
222             AssertPort(src_index);
223             buffers[i] = GetBuffer(src_index, buffer_size);
224         }
225 
226         port->MixBuffers(buffers, i, buffer_size);
227         return port->GetBuffer();
228     }
229 }
230 
231 // Server
RequestMonitor(jack_port_id_t port_index,bool onoff)232 int JackGraphManager::RequestMonitor(jack_port_id_t port_index, bool onoff) // Client
233 {
234     AssertPort(port_index);
235     JackPort* port = GetPort(port_index);
236 
237     /**
238     jackd.h
239         * If @ref JackPortCanMonitor is set for this @a port, turn input
240         * monitoring on or off. Otherwise, do nothing.
241 
242      if (!(fFlags & JackPortCanMonitor))
243     	return -1;
244     */
245 
246     port->RequestMonitor(onoff);
247 
248     const jack_int_t* connections = ReadCurrentState()->GetConnections(port_index);
249     if ((port->fFlags & JackPortIsOutput) == 0) { // ?? Taken from jack, why not (port->fFlags  & JackPortIsInput) ?
250         jack_port_id_t src_index;
251         for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && ((src_index = connections[i]) != EMPTY); i++) {
252             // XXX much worse things will happen if there is a feedback loop !!!
253             RequestMonitor(src_index, onoff);
254         }
255     }
256 
257     return 0;
258 }
259 
260 // Client
ComputeTotalLatencyAux(jack_port_id_t port_index,jack_port_id_t src_port_index,JackConnectionManager * manager,int hop_count)261 jack_nframes_t JackGraphManager::ComputeTotalLatencyAux(jack_port_id_t port_index, jack_port_id_t src_port_index, JackConnectionManager* manager, int hop_count)
262 {
263     const jack_int_t* connections = ReadCurrentState()->GetConnections(port_index);
264     jack_nframes_t max_latency = 0;
265     jack_port_id_t dst_index;
266 
267     if (hop_count > 8)
268         return GetPort(port_index)->GetLatency();
269 
270     for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && ((dst_index = connections[i]) != EMPTY); i++) {
271         if (src_port_index != dst_index) {
272             AssertPort(dst_index);
273             JackPort* dst_port = GetPort(dst_index);
274             jack_nframes_t this_latency = (dst_port->fFlags & JackPortIsTerminal)
275                                           ? dst_port->GetLatency()
276                                           : ComputeTotalLatencyAux(dst_index, port_index, manager, hop_count + 1);
277             max_latency = ((max_latency > this_latency) ? max_latency : this_latency);
278         }
279     }
280 
281     return max_latency + GetPort(port_index)->GetLatency();
282 }
283 
284 // Client
ComputeTotalLatency(jack_port_id_t port_index)285 int JackGraphManager::ComputeTotalLatency(jack_port_id_t port_index)
286 {
287     UInt16 cur_index;
288     UInt16 next_index;
289     JackPort* port = GetPort(port_index);
290     AssertPort(port_index);
291 
292     do {
293         cur_index = GetCurrentIndex();
294         port->fTotalLatency = ComputeTotalLatencyAux(port_index, port_index, ReadCurrentState(), 0);
295         next_index = GetCurrentIndex();
296     } while (cur_index != next_index); // Until a coherent state has been read
297 
298     jack_log("JackGraphManager::GetTotalLatency port_index = %ld total latency = %ld", port_index, port->fTotalLatency);
299     return 0;
300 }
301 
302 // Client
ComputeTotalLatencies()303 int JackGraphManager::ComputeTotalLatencies()
304 {
305     jack_port_id_t port_index;
306     for (port_index = FIRST_AVAILABLE_PORT; port_index < fPortMax; port_index++) {
307         JackPort* port = GetPort(port_index);
308         if (port->IsUsed()) {
309             ComputeTotalLatency(port_index);
310         }
311     }
312     return 0;
313 }
314 
RecalculateLatencyAux(jack_port_id_t port_index,jack_latency_callback_mode_t mode)315 void JackGraphManager::RecalculateLatencyAux(jack_port_id_t port_index, jack_latency_callback_mode_t mode)
316 {
317     const jack_int_t* connections = ReadCurrentState()->GetConnections(port_index);
318     JackPort* port = GetPort(port_index);
319     jack_latency_range_t latency = { UINT32_MAX, 0 };
320     jack_port_id_t dst_index;
321 
322     for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && ((dst_index = connections[i]) != EMPTY); i++) {
323         AssertPort(dst_index);
324         JackPort* dst_port = GetPort(dst_index);
325         jack_latency_range_t other_latency;
326 
327         dst_port->GetLatencyRange(mode, &other_latency);
328 
329         if (other_latency.max > latency.max) {
330 			latency.max = other_latency.max;
331         }
332 		if (other_latency.min < latency.min) {
333 			latency.min = other_latency.min;
334         }
335     }
336 
337     if (latency.min == UINT32_MAX) {
338 		latency.min = 0;
339     }
340 
341 	port->SetLatencyRange(mode, &latency);
342 }
343 
RecalculateLatency(jack_port_id_t port_index,jack_latency_callback_mode_t mode)344 void JackGraphManager::RecalculateLatency(jack_port_id_t port_index, jack_latency_callback_mode_t mode)
345 {
346     UInt16 cur_index;
347     UInt16 next_index;
348 
349     do {
350         cur_index = GetCurrentIndex();
351         RecalculateLatencyAux(port_index, mode);
352         next_index = GetCurrentIndex();
353     } while (cur_index != next_index); // Until a coherent state has been read
354 
355     //jack_log("JackGraphManager::RecalculateLatency port_index = %ld", port_index);
356 }
357 
358 // Server
SetBufferSize(jack_nframes_t buffer_size)359 void JackGraphManager::SetBufferSize(jack_nframes_t buffer_size)
360 {
361     jack_log("JackGraphManager::SetBufferSize size = %ld", buffer_size);
362 
363     jack_port_id_t port_index;
364     for (port_index = FIRST_AVAILABLE_PORT; port_index < fPortMax; port_index++) {
365         JackPort* port = GetPort(port_index);
366         if (port->IsUsed()) {
367             port->ClearBuffer(buffer_size);
368         }
369     }
370 }
371 
372 // Server
AllocatePortAux(int refnum,const char * port_name,const char * port_type,JackPortFlags flags)373 jack_port_id_t JackGraphManager::AllocatePortAux(int refnum, const char* port_name, const char* port_type, JackPortFlags flags)
374 {
375     jack_port_id_t port_index;
376 
377     // Available ports start at FIRST_AVAILABLE_PORT (= 1), otherwise a port_index of 0 is "seen" as a NULL port by the external API...
378     for (port_index = FIRST_AVAILABLE_PORT; port_index < fPortMax; port_index++) {
379         JackPort* port = GetPort(port_index);
380         if (!port->IsUsed()) {
381             jack_log("JackGraphManager::AllocatePortAux port_index = %ld name = %s type = %s", port_index, port_name, port_type);
382             if (!port->Allocate(refnum, port_name, port_type, flags)) {
383                 return NO_PORT;
384             }
385             break;
386         }
387     }
388 
389     return (port_index < fPortMax) ? port_index : NO_PORT;
390 }
391 
392 // Server
AllocatePort(int refnum,const char * port_name,const char * port_type,JackPortFlags flags,jack_nframes_t buffer_size)393 jack_port_id_t JackGraphManager::AllocatePort(int refnum, const char* port_name, const char* port_type, JackPortFlags flags, jack_nframes_t buffer_size)
394 {
395     JackConnectionManager* manager = WriteNextStateStart();
396     jack_port_id_t port_index = AllocatePortAux(refnum, port_name, port_type, flags);
397 
398     if (port_index != NO_PORT) {
399         JackPort* port = GetPort(port_index);
400         assert(port);
401         port->ClearBuffer(buffer_size);
402 
403         int res;
404         if (flags & JackPortIsOutput) {
405             res = manager->AddOutputPort(refnum, port_index);
406         } else {
407             res = manager->AddInputPort(refnum, port_index);
408         }
409         // Insertion failure
410         if (res < 0) {
411             port->Release();
412             port_index = NO_PORT;
413         }
414     }
415 
416     WriteNextStateStop();
417     return port_index;
418 }
419 
420 // Server
ReleasePort(int refnum,jack_port_id_t port_index)421 int JackGraphManager::ReleasePort(int refnum, jack_port_id_t port_index)
422 {
423     JackConnectionManager* manager = WriteNextStateStart();
424     JackPort* port = GetPort(port_index);
425     int res;
426 
427     if (port->fFlags & JackPortIsOutput) {
428         DisconnectAllOutput(port_index);
429         res = manager->RemoveOutputPort(refnum, port_index);
430     } else {
431         DisconnectAllInput(port_index);
432         res = manager->RemoveInputPort(refnum, port_index);
433     }
434 
435     port->Release();
436     WriteNextStateStop();
437     return res;
438 }
439 
GetInputPorts(int refnum,jack_int_t * res)440 void JackGraphManager::GetInputPorts(int refnum, jack_int_t* res)
441 {
442     JackConnectionManager* manager = WriteNextStateStart();
443     const jack_int_t* input = manager->GetInputPorts(refnum);
444     memcpy(res, input, sizeof(jack_int_t) * PORT_NUM_FOR_CLIENT);
445     WriteNextStateStop();
446 }
447 
GetOutputPorts(int refnum,jack_int_t * res)448 void JackGraphManager::GetOutputPorts(int refnum, jack_int_t* res)
449 {
450     JackConnectionManager* manager = WriteNextStateStart();
451     const jack_int_t* output = manager->GetOutputPorts(refnum);
452     memcpy(res, output, sizeof(jack_int_t) * PORT_NUM_FOR_CLIENT);
453     WriteNextStateStop();
454 }
455 
456 // Server
RemoveAllPorts(int refnum)457 void JackGraphManager::RemoveAllPorts(int refnum)
458 {
459     jack_log("JackGraphManager::RemoveAllPorts ref = %ld", refnum);
460     JackConnectionManager* manager = WriteNextStateStart();
461     jack_port_id_t port_index;
462 
463     // Warning : ReleasePort shift port to left, thus we always remove the first port until the "input" table is empty
464     const jack_int_t* input = manager->GetInputPorts(refnum);
465     while ((port_index = input[0]) != EMPTY) {
466         int res = ReleasePort(refnum, port_index);
467         if (res < 0) {
468             jack_error("JackGraphManager::RemoveAllPorts failure ref = %ld port_index = %ld", refnum, port_index);
469             assert(true);
470             break;
471         }
472     }
473 
474     // Warning : ReleasePort shift port to left, thus we always remove the first port until the "output" table is empty
475     const jack_int_t* output = manager->GetOutputPorts(refnum);
476     while ((port_index = output[0]) != EMPTY) {
477         int res = ReleasePort(refnum, port_index);
478         if (res < 0) {
479             jack_error("JackGraphManager::RemoveAllPorts failure ref = %ld port_index = %ld", refnum, port_index);
480             assert(true);
481             break;
482         }
483     }
484 
485     WriteNextStateStop();
486 }
487 
488 // Server
DisconnectAllPorts(int refnum)489 void JackGraphManager::DisconnectAllPorts(int refnum)
490 {
491     int i;
492     jack_log("JackGraphManager::DisconnectAllPorts ref = %ld", refnum);
493     JackConnectionManager* manager = WriteNextStateStart();
494 
495     const jack_int_t* input = manager->GetInputPorts(refnum);
496     for (i = 0; i < PORT_NUM_FOR_CLIENT && input[i] != EMPTY ; i++) {
497         DisconnectAllInput(input[i]);
498     }
499 
500     const jack_int_t* output = manager->GetOutputPorts(refnum);
501     for (i = 0; i < PORT_NUM_FOR_CLIENT && output[i] != EMPTY; i++) {
502         DisconnectAllOutput(output[i]);
503     }
504 
505     WriteNextStateStop();
506 }
507 
508 // Server
DisconnectAllInput(jack_port_id_t port_index)509 void JackGraphManager::DisconnectAllInput(jack_port_id_t port_index)
510 {
511     jack_log("JackGraphManager::DisconnectAllInput port_index = %ld", port_index);
512     JackConnectionManager* manager = WriteNextStateStart();
513 
514     for (unsigned int i = 0; i < fPortMax; i++) {
515         if (manager->IsConnected(i, port_index)) {
516             jack_log("JackGraphManager::Disconnect i = %ld  port_index = %ld", i, port_index);
517             Disconnect(i, port_index);
518         }
519     }
520     WriteNextStateStop();
521 }
522 
523 // Server
DisconnectAllOutput(jack_port_id_t port_index)524 void JackGraphManager::DisconnectAllOutput(jack_port_id_t port_index)
525 {
526     jack_log("JackGraphManager::DisconnectAllOutput port_index = %ld ", port_index);
527     JackConnectionManager* manager = WriteNextStateStart();
528 
529     const jack_int_t* connections = manager->GetConnections(port_index);
530     while (connections[0] != EMPTY) {
531         Disconnect(port_index, connections[0]); // Warning : Disconnect shift port to left
532     }
533     WriteNextStateStop();
534 }
535 
536 // Server
DisconnectAll(jack_port_id_t port_index)537 int JackGraphManager::DisconnectAll(jack_port_id_t port_index)
538 {
539     AssertPort(port_index);
540 
541     JackPort* port = GetPort(port_index);
542     if (port->fFlags & JackPortIsOutput) {
543         DisconnectAllOutput(port_index);
544     } else {
545         DisconnectAllInput(port_index);
546     }
547     return 0;
548 }
549 
550 // Server
GetConnections(jack_port_id_t port_index,jack_int_t * res)551 void JackGraphManager::GetConnections(jack_port_id_t port_index, jack_int_t* res)
552 {
553     JackConnectionManager* manager = WriteNextStateStart();
554     const jack_int_t* connections = manager->GetConnections(port_index);
555     memcpy(res, connections, sizeof(jack_int_t) * CONNECTION_NUM_FOR_PORT);
556     WriteNextStateStop();
557 }
558 
559 // Server
Activate(int refnum)560 void JackGraphManager::Activate(int refnum)
561 {
562     DirectConnect(FREEWHEEL_DRIVER_REFNUM, refnum);
563     DirectConnect(refnum, FREEWHEEL_DRIVER_REFNUM);
564 }
565 
566 /*
567 	Disconnection from the FW must be done in last otherwise an intermediate "unconnected"
568 	(thus unactivated) state may happen where the client is still checked for its end.
569 */
570 
571 // Server
Deactivate(int refnum)572 void JackGraphManager::Deactivate(int refnum)
573 {
574     // Disconnect only when needed
575     if (IsDirectConnection(refnum, FREEWHEEL_DRIVER_REFNUM)) {
576         DirectDisconnect(refnum, FREEWHEEL_DRIVER_REFNUM);
577     } else {
578         jack_log("JackServer::Deactivate client = %ld was not activated", refnum);
579     }
580 
581     // Disconnect only when needed
582     if (IsDirectConnection(FREEWHEEL_DRIVER_REFNUM, refnum)) {
583         DirectDisconnect(FREEWHEEL_DRIVER_REFNUM, refnum);
584     } else {
585         jack_log("JackServer::Deactivate client = %ld was not activated", refnum);
586     }
587 }
588 
589 // Server
GetInputRefNum(jack_port_id_t port_index)590 int JackGraphManager::GetInputRefNum(jack_port_id_t port_index)
591 {
592     AssertPort(port_index);
593     JackConnectionManager* manager = WriteNextStateStart();
594     int res = manager->GetInputRefNum(port_index);
595     WriteNextStateStop();
596     return res;
597 }
598 
599 // Server
GetOutputRefNum(jack_port_id_t port_index)600 int JackGraphManager::GetOutputRefNum(jack_port_id_t port_index)
601 {
602     AssertPort(port_index);
603     JackConnectionManager* manager = WriteNextStateStart();
604     int res = manager->GetOutputRefNum(port_index);
605     WriteNextStateStop();
606     return res;
607 }
608 
Connect(jack_port_id_t port_src,jack_port_id_t port_dst)609 int JackGraphManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst)
610 {
611     JackConnectionManager* manager = WriteNextStateStart();
612     jack_log("JackGraphManager::Connect port_src = %ld port_dst = %ld", port_src, port_dst);
613     JackPort* src = GetPort(port_src);
614     JackPort* dst = GetPort(port_dst);
615     int res = 0;
616 
617     if (!src->fInUse || !dst->fInUse) {
618         if (!src->fInUse)
619             jack_error("JackGraphManager::Connect port_src = %ld not used name = %s", port_src, GetPort(port_src)->fName);
620         if (!dst->fInUse)
621             jack_error("JackGraphManager::Connect port_dst = %ld not used name = %s", port_dst, GetPort(port_dst)->fName);
622         res = -1;
623         goto end;
624     }
625     if (src->fTypeId != dst->fTypeId) {
626         jack_error("JackGraphManager::Connect different port types port_src = %ld port_dst = %ld", port_src, port_dst);
627         res = -1;
628         goto end;
629     }
630     if (manager->IsConnected(port_src, port_dst)) {
631         jack_error("JackGraphManager::Connect already connected port_src = %ld port_dst = %ld", port_src, port_dst);
632         res = EEXIST;
633         goto end;
634     }
635 
636     res = manager->Connect(port_src, port_dst);
637     if (res < 0) {
638         jack_error("JackGraphManager::Connect failed port_src = %ld port_dst = %ld", port_src, port_dst);
639         goto end;
640     }
641     res = manager->Connect(port_dst, port_src);
642     if (res < 0) {
643         jack_error("JackGraphManager::Connect failed port_dst = %ld port_src = %ld", port_dst, port_src);
644         goto end;
645     }
646 
647     if (manager->IsLoopPath(port_src, port_dst)) {
648         jack_log("JackGraphManager::Connect: LOOP detected");
649         manager->IncFeedbackConnection(port_src, port_dst);
650     } else {
651         manager->IncDirectConnection(port_src, port_dst);
652     }
653 
654 end:
655     WriteNextStateStop();
656     return res;
657 }
658 
659 // Server
Disconnect(jack_port_id_t port_src,jack_port_id_t port_dst)660 int JackGraphManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst)
661 {
662     JackConnectionManager* manager = WriteNextStateStart();
663     jack_log("JackGraphManager::Disconnect port_src = %ld port_dst = %ld", port_src, port_dst);
664     bool in_use_src = GetPort(port_src)->fInUse;
665     bool in_use_dst = GetPort(port_dst)->fInUse;
666     int res = 0;
667 
668     if (!in_use_src || !in_use_dst) {
669         if (!in_use_src)
670             jack_error("JackGraphManager::Disconnect: port_src = %ld not used name = %s", port_src, GetPort(port_src)->fName);
671         if (!in_use_dst)
672             jack_error("JackGraphManager::Disconnect: port_src = %ld not used name = %s", port_dst, GetPort(port_dst)->fName);
673         res = -1;
674         goto end;
675     }
676     if (!manager->IsConnected(port_src, port_dst)) {
677         jack_error("JackGraphManager::Disconnect not connected port_src = %ld port_dst = %ld", port_src, port_dst);
678         res = -1;
679         goto end;
680     }
681 
682     res = manager->Disconnect(port_src, port_dst);
683     if (res < 0) {
684         jack_error("JackGraphManager::Disconnect failed port_src = %ld port_dst = %ld", port_src, port_dst);
685         goto end;
686     }
687     res = manager->Disconnect(port_dst, port_src);
688     if (res < 0) {
689         jack_error("JackGraphManager::Disconnect failed port_dst = %ld port_src = %ld", port_dst, port_src);
690         goto end;
691     }
692 
693     if (manager->IsFeedbackConnection(port_src, port_dst)) {
694         jack_log("JackGraphManager::Disconnect: FEEDBACK removed");
695         manager->DecFeedbackConnection(port_src, port_dst);
696     } else {
697         manager->DecDirectConnection(port_src, port_dst);
698     }
699 
700 end:
701     WriteNextStateStop();
702     return res;
703 }
704 
705 // Client
IsConnected(jack_port_id_t port_src,jack_port_id_t port_dst)706 int JackGraphManager::IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst)
707 {
708     JackConnectionManager* manager = ReadCurrentState();
709     return manager->IsConnected(port_src, port_dst);
710 }
711 
712 // Server
CheckPorts(jack_port_id_t port_src,jack_port_id_t port_dst)713 int JackGraphManager::CheckPorts(jack_port_id_t port_src, jack_port_id_t port_dst)
714 {
715     JackPort* src = GetPort(port_src);
716     JackPort* dst = GetPort(port_dst);
717 
718     if ((dst->fFlags & JackPortIsInput) == 0) {
719         jack_error("Destination port in attempted (dis)connection of %s and %s is not an input port", src->fName, dst->fName);
720         return -1;
721     }
722 
723     if ((src->fFlags & JackPortIsOutput) == 0) {
724         jack_error("Source port in attempted (dis)connection of %s and %s is not an output port", src->fName, dst->fName);
725         return -1;
726     }
727 
728     return 0;
729 }
730 
GetTwoPorts(const char * src_name,const char * dst_name,jack_port_id_t * port_src,jack_port_id_t * port_dst)731 int JackGraphManager::GetTwoPorts(const char* src_name, const char* dst_name, jack_port_id_t* port_src, jack_port_id_t* port_dst)
732 {
733     jack_log("JackGraphManager::CheckConnect src_name = %s dst_name = %s", src_name, dst_name);
734 
735     if ((*port_src = GetPort(src_name)) == NO_PORT) {
736         jack_error("Unknown source port in attempted (dis)connection src_name [%s] dst_name [%s]", src_name, dst_name);
737         return -1;
738     }
739 
740     if ((*port_dst = GetPort(dst_name)) == NO_PORT) {
741         jack_error("Unknown destination port in attempted (dis)connection src_name [%s] dst_name [%s]", src_name, dst_name);
742         return -1;
743     }
744 
745     return 0;
746 }
747 
748 // Client : port array
GetPort(const char * name)749 jack_port_id_t JackGraphManager::GetPort(const char* name)
750 {
751     for (unsigned int i = 0; i < fPortMax; i++) {
752         JackPort* port = GetPort(i);
753         if (port->IsUsed() && port->NameEquals(name)) {
754             return i;
755         }
756     }
757     return NO_PORT;
758 }
759 
760 /*!
761 \brief Get the connection port name array.
762 */
763 
764 // Client
GetConnectionsAux(JackConnectionManager * manager,const char ** res,jack_port_id_t port_index)765 void JackGraphManager::GetConnectionsAux(JackConnectionManager* manager, const char** res, jack_port_id_t port_index)
766 {
767     const jack_int_t* connections = manager->GetConnections(port_index);
768     jack_int_t index;
769     int i;
770 
771     // Cleanup connection array
772     memset(res, 0, sizeof(char*) * CONNECTION_NUM_FOR_PORT);
773 
774     for (i = 0; (i < CONNECTION_NUM_FOR_PORT) && ((index = connections[i]) != EMPTY); i++) {
775         JackPort* port = GetPort(index);
776         res[i] = port->fName;
777     }
778 
779     res[i] = NULL;
780 }
781 
782 /*
783 	Use the state returned by ReadCurrentState and check that the state was not changed during the read operation.
784 	The operation is lock-free since there is no intermediate state in the write operation that could cause the
785 	read to loop forever.
786 */
787 
788 // Client
GetConnections(jack_port_id_t port_index)789 const char** JackGraphManager::GetConnections(jack_port_id_t port_index)
790 {
791     const char** res = (const char**)malloc(sizeof(char*) * CONNECTION_NUM_FOR_PORT);
792     UInt16 cur_index, next_index;
793 
794     if (!res)
795         return NULL;
796 
797     do {
798         cur_index = GetCurrentIndex();
799         GetConnectionsAux(ReadCurrentState(), res, port_index);
800         next_index = GetCurrentIndex();
801     } while (cur_index != next_index); // Until a coherent state has been read
802 
803     if (res[0]) {	// At least one connection
804         return res;
805     } else {		// Empty array, should return NULL
806         free(res);
807         return NULL;
808     }
809 }
810 
811 // Client
GetPortsAux(const char ** matching_ports,const char * port_name_pattern,const char * type_name_pattern,unsigned long flags)812 void JackGraphManager::GetPortsAux(const char** matching_ports, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags)
813 {
814     // Cleanup port array
815     memset(matching_ports, 0, sizeof(char*) * fPortMax);
816 
817     int match_cnt = 0;
818     regex_t port_regex, type_regex;
819 
820     if (port_name_pattern && port_name_pattern[0]) {
821         if (regcomp(&port_regex, port_name_pattern, REG_EXTENDED | REG_NOSUB)!=0) {
822              jack_log("JackGraphManager::GetPortsAux could not compile regex for port_name_pattern '%s'", port_name_pattern);
823              return;
824         }
825     }
826     if (type_name_pattern && type_name_pattern[0]) {
827         if (regcomp(&type_regex, type_name_pattern, REG_EXTENDED | REG_NOSUB)!=0) {
828              jack_log("JackGraphManager::GetPortsAux could not compile regex for type_name_pattern '%s'", type_name_pattern);
829              return;
830         }
831     }
832 
833     for (unsigned int i = 0; i < fPortMax; i++) {
834         bool matching = true;
835         JackPort* port = GetPort(i);
836 
837         if (port->IsUsed()) {
838 
839             if (flags) {
840                 if ((port->fFlags & flags) != flags) {
841                     matching = false;
842                 }
843             }
844 
845             if (matching && port_name_pattern && port_name_pattern[0]) {
846                 if (regexec(&port_regex, port->GetName(), 0, NULL, 0)) {
847                     matching = false;
848                 }
849             }
850             if (matching && type_name_pattern && type_name_pattern[0]) {
851                 if (regexec(&type_regex, port->GetType(), 0, NULL, 0)) {
852                     matching = false;
853                 }
854             }
855 
856             if (matching) {
857                 matching_ports[match_cnt++] = port->fName;
858             }
859         }
860     }
861 
862     matching_ports[match_cnt] = 0;
863 
864     if (port_name_pattern && port_name_pattern[0]) {
865         regfree(&port_regex);
866     }
867     if (type_name_pattern && type_name_pattern[0]) {
868         regfree(&type_regex);
869     }
870 }
871 
872 // Client
873 /*
874 	Check that the state was not changed during the read operation.
875 	The operation is lock-free since there is no intermediate state in the write operation that could cause the
876 	read to loop forever.
877 */
GetPorts(const char * port_name_pattern,const char * type_name_pattern,unsigned long flags)878 const char** JackGraphManager::GetPorts(const char* port_name_pattern, const char* type_name_pattern, unsigned long flags)
879 {
880     const char** res = (const char**)malloc(sizeof(char*) * fPortMax);
881     UInt16 cur_index, next_index;
882 
883     if (!res)
884         return NULL;
885 
886     do {
887         cur_index = GetCurrentIndex();
888         GetPortsAux(res, port_name_pattern, type_name_pattern, flags);
889         next_index = GetCurrentIndex();
890     } while (cur_index != next_index);  // Until a coherent state has been read
891 
892     if (res[0]) {    // At least one port
893         return res;
894     } else {
895         free(res);   // Empty array, should return NULL
896         return NULL;
897     }
898 }
899 
900 // Server
Save(JackConnectionManager * dst)901 void JackGraphManager::Save(JackConnectionManager* dst)
902 {
903     JackConnectionManager* manager = WriteNextStateStart();
904     memcpy(dst, manager, sizeof(JackConnectionManager));
905     WriteNextStateStop();
906 }
907 
908 // Server
Restore(JackConnectionManager * src)909 void JackGraphManager::Restore(JackConnectionManager* src)
910 {
911     JackConnectionManager* manager = WriteNextStateStart();
912     memcpy(manager, src, sizeof(JackConnectionManager));
913     WriteNextStateStop();
914 }
915 
916 } // end of namespace
917 
918 
919