summaryrefslogtreecommitdiff
path: root/common/JackConnectionManager.cpp
blob: a0cc04391a08191b3f9279591ba8d3fa67375620 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
/*
Copyright (C) 2004-2008 Grame

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/

#include "JackConnectionManager.h"
#include "JackClientControl.h"
#include "JackEngineControl.h"
#include "JackGlobals.h"
#include "JackError.h"
#include <set>
#include <iostream>
#include <assert.h>

namespace Jack
{

JackConnectionManager::JackConnectionManager()
{
    int i;
    jack_log("JackConnectionManager::InitConnections size = %ld ", sizeof(JackConnectionManager));

    for (i = 0; i < PORT_NUM_MAX; i++) {
        fConnection[i].Init();
    }

    fLoopFeedback.Init();

    jack_log("JackConnectionManager::InitClients");
    for (i = 0; i < CLIENT_NUM; i++) {
        InitRefNum(i);
    }
}

JackConnectionManager::~JackConnectionManager()
{}

//--------------
// Internal API
//--------------

bool JackConnectionManager::IsLoopPathAux(int ref1, int ref2) const
{
    jack_log("JackConnectionManager::IsLoopPathAux ref1 = %ld ref2 = %ld", ref1, ref2);

    if (ref1 < GetEngineControl()->fDriverNum || ref2 < GetEngineControl()->fDriverNum) {
        return false;
    } else if (ref1 == ref2) {	// Same refnum
        return true;
    } else {
        jack_int_t output[CLIENT_NUM];
        fConnectionRef.GetOutputTable(ref1, output);

        if (fConnectionRef.IsInsideTable(ref2, output)) { // If ref2 is contained in the outputs of ref1
            return true;
        } else {
            for (int i = 0; i < CLIENT_NUM && output[i] != EMPTY; i++) { // Otherwise recurse for all ref1 outputs
                if (IsLoopPathAux(output[i], ref2)) {
                    return true; // Stop when a path is found
                }
            }
            return false;
        }
    }
}

//--------------
// External API
//--------------

/*!
\brief Connect port_src to port_dst.
*/
int JackConnectionManager::Connect(jack_port_id_t port_src, jack_port_id_t port_dst)
{
    jack_log("JackConnectionManager::Connect port_src = %ld port_dst = %ld", port_src, port_dst);

    if (fConnection[port_src].AddItem(port_dst)) {
        return 0;
    } else {
        jack_error("Connection table is full !!");
        return -1;
    }
}

/*!
\brief Disconnect port_src from port_dst.
*/
int JackConnectionManager::Disconnect(jack_port_id_t port_src, jack_port_id_t port_dst)
{
    jack_log("JackConnectionManager::Disconnect port_src = %ld port_dst = %ld", port_src, port_dst);

    if (fConnection[port_src].RemoveItem(port_dst)) {
        return 0;
    } else {
        jack_error("Connection not found !!");
        return -1;
    }
}

/*!
\brief Check if port_src and port_dst are connected.
*/
bool JackConnectionManager::IsConnected(jack_port_id_t port_src, jack_port_id_t port_dst) const
{
    return fConnection[port_src].CheckItem(port_dst);
}

/*!
\brief Get the connection port array.
*/
const jack_int_t* JackConnectionManager::GetConnections(jack_port_id_t port_index) const
{
    return fConnection[port_index].GetItems();
}

//------------------------
// Client port management
//------------------------

/*!
\brief Add an input port to a client.
*/
int JackConnectionManager::AddInputPort(int refnum, jack_port_id_t port_index)
{
    if (fInputPort[refnum].AddItem(port_index)) {
        jack_log("JackConnectionManager::AddInputPort ref = %ld port = %ld", refnum, port_index);
        return 0;
    } else {
        jack_error("Maximum number of input ports is reached for application ref = %ld", refnum);
        return -1;
    }
}

/*!
\brief Add an output port to a client.
*/
int JackConnectionManager::AddOutputPort(int refnum, jack_port_id_t port_index)
{
    if (fOutputPort[refnum].AddItem(port_index)) {
        jack_log("JackConnectionManager::AddOutputPort ref = %ld port = %ld", refnum, port_index);
        return 0;
    } else {
        jack_error("Maximum number of output ports is reached for application ref = %ld", refnum);
        return -1;
    }
}

/*!
\brief Remove an input port from a client.
*/
int JackConnectionManager::RemoveInputPort(int refnum, jack_port_id_t port_index)
{
    jack_log("JackConnectionManager::RemoveInputPort ref = %ld port_index = %ld ", refnum, port_index);

    if (fInputPort[refnum].RemoveItem(port_index)) {
        return 0;
    } else {
        jack_error("Input port index = %ld not found for application ref = %ld", port_index, refnum);
        return -1;
    }
}

/*!
\brief Remove an output port from a client.
*/
int JackConnectionManager::RemoveOutputPort(int refnum, jack_port_id_t port_index)
{
    jack_log("JackConnectionManager::RemoveOutputPort ref = %ld port_index = %ld ", refnum, port_index);

    if (fOutputPort[refnum].RemoveItem(port_index)) {
        return 0;
    } else {
        jack_error("Output port index = %ld not found for application ref = %ld", port_index, refnum);
        return -1;
    }
}

/*!
\brief Get the input port array of a given refnum.
*/
const jack_int_t* JackConnectionManager::GetInputPorts(int refnum)
{
    return fInputPort[refnum].GetItems();
}

/*!
\brief Get the output port array of a given refnum.
*/
const jack_int_t* JackConnectionManager::GetOutputPorts(int refnum)
{
    return fOutputPort[refnum].GetItems();
}

/*!
\brief Init the refnum.
*/
void JackConnectionManager::InitRefNum(int refnum)
{
    fInputPort[refnum].Init();
    fOutputPort[refnum].Init();
    fConnectionRef.Init(refnum);
    fInputCounter[refnum].SetValue(0);
}

/*!
\brief Reset all clients activation.
*/
void JackConnectionManager::ResetGraph(JackClientTiming* timing)
{
    // Reset activation counter : must be done *before* starting to resume clients
    for (int i = 0; i < CLIENT_NUM; i++) {
        fInputCounter[i].Reset();
        timing[i].fStatus = NotTriggered;
    }
}

/*!
\brief Wait on the input synchro.
*/
int JackConnectionManager::SuspendRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing, long time_out_usec)
{
    bool res;
    if ((res = table[control->fRefNum].TimedWait(time_out_usec))) {
        timing[control->fRefNum].fStatus = Running;
        timing[control->fRefNum].fAwakeAt = GetMicroSeconds();
    }
    return (res) ? 0 : -1;
}

/*!
\brief Signal clients connected to the given client.
*/
int JackConnectionManager::ResumeRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing)
{
    jack_time_t current_date = GetMicroSeconds();
    const jack_int_t* output_ref = fConnectionRef.GetItems(control->fRefNum);
    int res = 0;

    // Update state and timestamp of current client
    timing[control->fRefNum].fStatus = Finished;
    timing[control->fRefNum].fFinishedAt = current_date;

    for (int i = 0; i < CLIENT_NUM; i++) {

        // Signal connected clients or drivers
        if (output_ref[i] > 0) {

            // Update state and timestamp of destination clients
            timing[i].fStatus = Triggered;
            timing[i].fSignaledAt = current_date;

            if (!fInputCounter[i].Signal(table + i, control)) {
                jack_log("JackConnectionManager::ResumeRefNum error: ref = %ld output = %ld ", control->fRefNum, i);
                res = -1;
            }
        }
    }

    return res;
}

static bool HasNoConnection(jack_int_t* table)
{
    for (int ref = 0; ref < CLIENT_NUM; ref++) {
        if (table[ref] > 0) return false;
    }
    return true;
}

// Using http://en.wikipedia.org/wiki/Topological_sorting

void JackConnectionManager::TopologicalSort(std::vector<jack_int_t>& sorted)
{
    JackFixedMatrix<CLIENT_NUM>* tmp = new JackFixedMatrix<CLIENT_NUM>;
    std::set<jack_int_t> level;

    fConnectionRef.Copy(*tmp);

    // Inputs of the graph
    level.insert(AUDIO_DRIVER_REFNUM);
    level.insert(FREEWHEEL_DRIVER_REFNUM);

    while (level.size() > 0) {
        jack_int_t refnum = *level.begin();
        sorted.push_back(refnum);
        level.erase(level.begin());
        const jack_int_t* output_ref1 = tmp->GetItems(refnum);
        for (int dst = 0; dst < CLIENT_NUM; dst++) {
            if (output_ref1[dst] > 0) {
                tmp->ClearItem(refnum, dst);
                jack_int_t output_ref2[CLIENT_NUM];
                tmp->GetOutputTable1(dst, output_ref2);
                if (HasNoConnection(output_ref2)) {
                    level.insert(dst);
                }
            }
        }
    }

    delete tmp;
}

/*!
\brief Increment the number of ports between 2 clients, if the 2 clients become connected, then the Activation counter is updated.
*/
void JackConnectionManager::IncDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
    int ref1 = GetOutputRefNum(port_src);
    int ref2 = GetInputRefNum(port_dst);

    assert(ref1 >= 0 && ref2 >= 0);

    DirectConnect(ref1, ref2);
    jack_log("JackConnectionManager::IncConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2);
}

/*!
\brief Decrement the number of ports between 2 clients, if the 2 clients become disconnected, then the Activation counter is updated.
*/
void JackConnectionManager::DecDirectConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
    int ref1 = GetOutputRefNum(port_src);
    int ref2 = GetInputRefNum(port_dst);

    assert(ref1 >= 0 && ref2 >= 0);

    DirectDisconnect(ref1, ref2);
    jack_log("JackConnectionManager::DecConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2);
}

/*!
\brief Directly connect 2 reference numbers.
*/
void JackConnectionManager::DirectConnect(int ref1, int ref2)
{
    assert(ref1 >= 0 && ref2 >= 0);

    if (fConnectionRef.IncItem(ref1, ref2) == 1) { // First connection between client ref1 and client ref2
        jack_log("JackConnectionManager::DirectConnect first: ref1 = %ld ref2 = %ld", ref1, ref2);
        fInputCounter[ref2].IncValue();
    }
}

/*!
\brief Directly disconnect 2 reference numbers.
*/
void JackConnectionManager::DirectDisconnect(int ref1, int ref2)
{
    assert(ref1 >= 0 && ref2 >= 0);

    if (fConnectionRef.DecItem(ref1, ref2) == 0) { // Last connection between client ref1 and client ref2
        jack_log("JackConnectionManager::DirectDisconnect last: ref1 = %ld ref2 = %ld", ref1, ref2);
        fInputCounter[ref2].DecValue();
    }
}

/*!
\brief Returns the connections state between 2 refnum.
*/
bool JackConnectionManager::IsDirectConnection(int ref1, int ref2) const
{
    assert(ref1 >= 0 && ref2 >= 0);
    return (fConnectionRef.GetItemCount(ref1, ref2) > 0);
}

/*!
\brief Get the client refnum of a given input port.
*/
int JackConnectionManager::GetInputRefNum(jack_port_id_t port_index) const
{
    for (int i = 0; i < CLIENT_NUM; i++) {
        if (fInputPort[i].CheckItem(port_index)) {
            return i;
        }
    }

    return -1;
}

/*!
\brief Get the client refnum of a given output port.
*/
int JackConnectionManager::GetOutputRefNum(jack_port_id_t port_index) const
{
    for (int i = 0; i < CLIENT_NUM; i++) {
        if (fOutputPort[i].CheckItem(port_index)) {
            return i;
        }
    }

    return -1;
}

/*!
\brief Test is a connection path exists between port_src and port_dst.
*/
bool JackConnectionManager::IsLoopPath(jack_port_id_t port_src, jack_port_id_t port_dst) const
{
    return IsLoopPathAux(GetInputRefNum(port_dst), GetOutputRefNum(port_src));
}

bool JackConnectionManager::IsFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst) const
{
    return (fLoopFeedback.GetConnectionIndex(GetOutputRefNum(port_src), GetInputRefNum(port_dst)) >= 0);
}

bool JackConnectionManager::IncFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
    int ref1 = GetOutputRefNum(port_src);
    int ref2 = GetInputRefNum(port_dst);

    // Add an activation connection in the other direction
    jack_log("JackConnectionManager::IncFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2);
    assert(ref1 >= 0 && ref2 >= 0);

    if (ref1 != ref2) {
        DirectConnect(ref2, ref1);
    }

    return fLoopFeedback.IncConnection(ref1, ref2); // Add the feedback connection
}

bool JackConnectionManager::DecFeedbackConnection(jack_port_id_t port_src, jack_port_id_t port_dst)
{
    int ref1 = GetOutputRefNum(port_src);
    int ref2 = GetInputRefNum(port_dst);

    // Remove an activation connection in the other direction
    jack_log("JackConnectionManager::DecFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2);
    assert(ref1 >= 0 && ref2 >= 0);

    if (ref1 != ref2) {
        DirectDisconnect(ref2, ref1);
    }

    return fLoopFeedback.DecConnection(ref1, ref2); // Remove the feedback connection
}

} // end of namespace