JackConnectionManager.cpp 11.7 KB
Newer Older
sletz's avatar
sletz committed
1
/*
sletz's avatar
sletz committed
2
Copyright (C) 2004-2008 Grame
sletz's avatar
sletz committed
3
4

This program is free software; you can redistribute it and/or modify
sletz's avatar
sletz committed
5
6
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
sletz's avatar
sletz committed
7
8
9
10
11
(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
sletz's avatar
sletz committed
12
GNU Lesser General Public License for more details.
sletz's avatar
sletz committed
13

sletz's avatar
sletz committed
14
15
16
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.
sletz's avatar
sletz committed
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

*/

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

namespace Jack
{

JackConnectionManager::JackConnectionManager()
{
    int i;
sletz's avatar
sletz committed
32
    jack_log("JackConnectionManager::InitConnections size = %ld ", sizeof(JackConnectionManager));
sletz's avatar
sletz committed
33
34
35
36
37
38
39

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

    fLoopFeedback.Init();

sletz's avatar
sletz committed
40
    jack_log("JackConnectionManager::InitClients");
sletz's avatar
sletz committed
41
    for (i = 0; i < CLIENT_NUM; i++) {
42
        InitRefNum(i);
sletz's avatar
sletz committed
43
44
45
46
47
48
49
50
51
52
53
54
    }
}

JackConnectionManager::~JackConnectionManager()
{}

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

bool JackConnectionManager::IsLoopPathAux(int ref1, int ref2) const
{
sletz's avatar
sletz committed
55
    jack_log("JackConnectionManager::IsLoopPathAux ref1 = %ld ref2 = %ld", ref1, ref2);
sletz's avatar
sletz committed
56
57
58
59
60

    if (ref1 == AUDIO_DRIVER_REFNUM // Driver is reached
            || ref2 == AUDIO_DRIVER_REFNUM
            || ref1 == FREEWHEEL_DRIVER_REFNUM
            || ref2 == FREEWHEEL_DRIVER_REFNUM
sletz's avatar
sletz committed
61
62
            || ref1 == MIDI_DRIVER_REFNUM
            || ref2 == MIDI_DRIVER_REFNUM) {
sletz's avatar
sletz committed
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
        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)
{
sletz's avatar
sletz committed
91
    jack_log("JackConnectionManager::Connect port_src = %ld port_dst = %ld", port_src, port_dst);
sletz's avatar
sletz committed
92
93
94
95
96
97
98
99
100
101
102
103
104
105

    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)
{
sletz's avatar
sletz committed
106
    jack_log("JackConnectionManager::Disconnect port_src = %ld port_dst = %ld", port_src, port_dst);
sletz's avatar
sletz committed
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

    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)) {
sletz's avatar
sletz committed
142
        jack_log("JackConnectionManager::AddInputPort ref = %ld port = %ld", refnum, port_index);
sletz's avatar
sletz committed
143
144
145
146
147
148
149
150
151
152
153
154
155
        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)) {
sletz's avatar
sletz committed
156
        jack_log("JackConnectionManager::AddOutputPort ref = %ld port = %ld", refnum, port_index);
sletz's avatar
sletz committed
157
158
159
160
161
162
163
164
165
166
167
168
        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)
{
sletz's avatar
sletz committed
169
    jack_log("JackConnectionManager::RemoveInputPort ref = %ld port_index = %ld ", refnum, port_index);
sletz's avatar
sletz committed
170
171
172
173
174
175
176
177
178
179
180
181
182
183

    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)
{
sletz's avatar
sletz committed
184
    jack_log("JackConnectionManager::RemoveOutputPort ref = %ld port_index = %ld ", refnum, port_index);
sletz's avatar
sletz committed
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

    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();
}

/*!
211
\brief Init the refnum.
sletz's avatar
sletz committed
212
*/
213
214
215
216
217
218
219
220
void JackConnectionManager::InitRefNum(int refnum)
{
    fInputPort[refnum].Init();
    fOutputPort[refnum].Init();
    fConnectionRef.Init(refnum);
    fInputCounter[refnum].SetValue(0);
}

sletz's avatar
sletz committed
221
222
223
224
225
226
227
228
/*!
\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();
sletz's avatar
sletz committed
229
        timing[i].fStatus = NotTriggered;
sletz's avatar
sletz committed
230
231
232
233
234
235
    }
}

/*!
\brief Wait on the input synchro.
*/
236
int JackConnectionManager::SuspendRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing, long time_out_usec)
sletz's avatar
sletz committed
237
{
sletz's avatar
sletz committed
238
    bool res;
239
    if ((res = table[control->fRefNum].TimedWait(time_out_usec))) {
sletz's avatar
sletz committed
240
241
        timing[control->fRefNum].fStatus = Running;
        timing[control->fRefNum].fAwakeAt = GetMicroSeconds();
sletz's avatar
sletz committed
242
243
244
245
246
247
248
    }
    return (res) ? 0 : -1;
}

/*!
\brief Signal clients connected to the given client.
*/
249
int JackConnectionManager::ResumeRefNum(JackClientControl* control, JackSynchro* table, JackClientTiming* timing)
sletz's avatar
sletz committed
250
{
sletz's avatar
sletz committed
251
    jack_time_t current_date = GetMicroSeconds();
sletz's avatar
sletz committed
252
    const jack_int_t* outputRef = fConnectionRef.GetItems(control->fRefNum);
sletz's avatar
sletz committed
253
    int res = 0;
sletz's avatar
sletz committed
254
255

    // Update state and timestamp of current client
sletz's avatar
sletz committed
256
257
    timing[control->fRefNum].fStatus = Finished;
    timing[control->fRefNum].fFinishedAt = current_date;
sletz's avatar
sletz committed
258
259

    for (int i = 0; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
260

sletz's avatar
sletz committed
261
262
        // Signal connected clients or drivers
        if (outputRef[i] > 0) {
sletz's avatar
sletz committed
263
264
265
266

            // Update state and timestamp of destination clients
            timing[i].fStatus = Triggered;
            timing[i].fSignaledAt = current_date;
sletz's avatar
sletz committed
267

268
            if (!fInputCounter[i].Signal(table + i, control)) {
sletz's avatar
sletz committed
269
                jack_log("JackConnectionManager::ResumeRefNum error: ref = %ld output = %ld ", control->fRefNum, i);
sletz's avatar
sletz committed
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
                res = -1;
            }
        }
    }

    return res;
}

/*!
\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);
sletz's avatar
sletz committed
289
    jack_log("JackConnectionManager::IncConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2);
sletz's avatar
sletz committed
290
291
292
293
294
295
296
297
298
299
300
301
302
}

/*!
\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);
sletz's avatar
sletz committed
303
    jack_log("JackConnectionManager::DecConnectionRef: ref1 = %ld ref2 = %ld", ref1, ref2);
sletz's avatar
sletz committed
304
305
306
307
308
309
310
311
312
313
}

/*!
\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
sletz's avatar
sletz committed
314
        jack_log("JackConnectionManager::DirectConnect first: ref1 = %ld ref2 = %ld", ref1, ref2);
sletz's avatar
sletz committed
315
316
317
318
319
320
321
322
323
324
325
326
        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
sletz's avatar
sletz committed
327
        jack_log("JackConnectionManager::DirectDisconnect last: ref1 = %ld ref2 = %ld", ref1, ref2);
sletz's avatar
sletz committed
328
329
330
331
332
333
334
335
336
337
        fInputCounter[ref2].DecValue();
    }
}

/*!
\brief Returns the connections state between 2 refnum.
*/
bool JackConnectionManager::IsDirectConnection(int ref1, int ref2) const
{
    assert(ref1 >= 0 && ref2 >= 0);
sletz's avatar
sletz committed
338
    return (fConnectionRef.GetItemCount(ref1, ref2) > 0);
sletz's avatar
sletz committed
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
}

/*!
\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 ouput 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
sletz's avatar
sletz committed
386
    jack_log("JackConnectionManager::IncFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2);
sletz's avatar
sletz committed
387
388
389
390
391
392
393
394
395
396
397
398
399
400
    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
sletz's avatar
sletz committed
401
    jack_log("JackConnectionManager::DecFeedbackConnection ref1 = %ld ref2 = %ld", ref1, ref2);
sletz's avatar
sletz committed
402
403
404
405
406
407
408
409
410
411
412
    assert(ref1 >= 0 && ref2 >= 0);

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

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

} // end of namespace