JackEngine.cpp 25.6 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
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <iostream>
#include <fstream>
#include <assert.h>

24
#include "JackSystemDeps.h"
sletz's avatar
sletz committed
25
#include "JackLockedEngine.h"
sletz's avatar
sletz committed
26
#include "JackExternalClient.h"
27
#include "JackInternalClient.h"
sletz's avatar
sletz committed
28
29
30
31
#include "JackEngineControl.h"
#include "JackClientControl.h"
#include "JackGlobals.h"
#include "JackChannel.h"
sletz's avatar
sletz committed
32
#include "JackError.h"
sletz's avatar
sletz committed
33
34
35
36

namespace Jack
{

37
38
#define AssertRefnum(ref) assert(ref >= 0 && ref < CLIENT_NUM);

sletz's avatar
sletz committed
39
JackEngine::JackEngine(JackGraphManager* manager,
40
                       JackSynchro* table,
sletz's avatar
sletz committed
41
                       JackEngineControl* control)
sletz's avatar
sletz committed
42
43
44
45
46
{
    fGraphManager = manager;
    fSynchroTable = table;
    fEngineControl = control;
    for (int i = 0; i < CLIENT_NUM; i++)
47
        fClientTable[i] = NULL;
sletz's avatar
sletz committed
48
49
50
51
}

JackEngine::~JackEngine()
{
sletz's avatar
sletz committed
52
    jack_log("JackEngine::~JackEngine");
sletz's avatar
sletz committed
53
54
55
56
}

int JackEngine::Open()
{
sletz's avatar
sletz committed
57
    jack_log("JackEngine::Open");
sletz's avatar
sletz committed
58
59

    // Open audio thread => request thread communication channel
60
    if (fChannel.Open(fEngineControl->fServerName) < 0) {
sletz's avatar
sletz committed
61
62
63
64
65
66
67
68
69
        jack_error("Cannot connect to server");
        return -1;
    } else {
        return 0;
    }
}

int JackEngine::Close()
{
sletz's avatar
sletz committed
70
    jack_log("JackEngine::Close");
71
    fChannel.Close();
sletz's avatar
sletz committed
72

73
    // Close remaining clients (RT is stopped)
74
    for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
75
76
77
        if (JackLoadableInternalClient* loadable_client = dynamic_cast<JackLoadableInternalClient*>(fClientTable[i])) {
            jack_log("JackEngine::Close loadable client = %s", loadable_client->GetClientControl()->fName);
            loadable_client->Close();
sletz's avatar
sletz committed
78
79
            // Close does not delete the pointer for internal clients
            fClientTable[i] = NULL;
sletz's avatar
sletz committed
80
81
82
83
            delete loadable_client;
        } else if (JackExternalClient* external_client = dynamic_cast<JackExternalClient*>(fClientTable[i])) {
            jack_log("JackEngine::Close external client = %s", external_client->GetClientControl()->fName);
            external_client->Close();
sletz's avatar
sletz committed
84
85
            // Close deletes the pointer for external clients
            fClientTable[i] = NULL;
sletz's avatar
sletz committed
86
87
        }
    }
sletz's avatar
sletz committed
88

sletz's avatar
sletz committed
89
90
    return 0;
}
sletz's avatar
sletz committed
91

sletz's avatar
Cleanup    
sletz committed
92
93
94
95
//-----------------------------
// Client ressource management
//-----------------------------

sletz's avatar
sletz committed
96
int JackEngine::AllocateRefnum()
97
98
99
{
    for (int i = 0; i < CLIENT_NUM; i++) {
        if (!fClientTable[i]) {
sletz's avatar
sletz committed
100
            jack_log("JackEngine::AllocateRefNum ref = %ld", i);
101
102
103
104
105
106
            return i;
        }
    }
    return -1;
}

sletz's avatar
sletz committed
107
108
void JackEngine::ReleaseRefnum(int ref)
{
sletz's avatar
sletz committed
109
110
111
112
113
114
115
116
117
118
    fClientTable[ref] = NULL;

    if (fEngineControl->fTemporary) {
        int i;
        for (i = REAL_REFNUM; i < CLIENT_NUM; i++) {
            if (fClientTable[i])
                break;
        }
        if (i == CLIENT_NUM) {
            // last client and temporay case: quit the server
sletz's avatar
sletz committed
119
            jack_log("JackEngine::ReleaseRefnum server quit");
sletz's avatar
sletz committed
120
121
            fEngineControl->fTemporary = false;
#ifndef WIN32
sletz's avatar
sletz committed
122
 	    exit(0);
sletz's avatar
sletz committed
123
124
125
#endif
        }
    }
sletz's avatar
sletz committed
126
127
}

sletz's avatar
sletz committed
128
129
130
131
//------------------
// Graph management
//------------------

sletz's avatar
sletz committed
132
void JackEngine::ProcessNext(jack_time_t cur_cycle_begin)
sletz's avatar
sletz committed
133
{
sletz's avatar
sletz committed
134
    fLastSwitchUsecs = cur_cycle_begin;
sletz's avatar
sletz committed
135
    if (fGraphManager->RunNextGraph())	// True if the graph actually switched to a new state
136
        fChannel.Notify(ALL_CLIENTS, kGraphOrderCallback, 0);
137
    fSignal.Signal();                   // Signal for threads waiting for next cycle
sletz's avatar
sletz committed
138
139
}

sletz's avatar
sletz committed
140
void JackEngine::ProcessCurrent(jack_time_t cur_cycle_begin)
sletz's avatar
sletz committed
141
{
sletz's avatar
sletz committed
142
143
    if (cur_cycle_begin < fLastSwitchUsecs + 2 * fEngineControl->fPeriodUsecs) // Signal XRun only for the first failing cycle
        CheckXRun(cur_cycle_begin);
sletz's avatar
sletz committed
144
    fGraphManager->RunCurrentGraph();
sletz's avatar
sletz committed
145
146
}

sletz's avatar
sletz committed
147
bool JackEngine::Process(jack_time_t cur_cycle_begin, jack_time_t prev_cycle_end)
sletz's avatar
sletz committed
148
{
sletz's avatar
sletz committed
149
    bool res = true;
sletz's avatar
sletz committed
150

151
    // Cycle  begin
sletz's avatar
sletz committed
152
    fEngineControl->CycleBegin(fClientTable, fGraphManager, cur_cycle_begin, prev_cycle_end);
sletz's avatar
sletz committed
153

sletz's avatar
sletz committed
154
    // Graph
sletz's avatar
sletz committed
155
    if (fGraphManager->IsFinishedGraph()) {
sletz's avatar
sletz committed
156
        ProcessNext(cur_cycle_begin);
sletz's avatar
sletz committed
157
        res = true;
sletz's avatar
sletz committed
158
    } else {
sletz's avatar
sletz committed
159
        jack_log("Process: graph not finished!");
sletz's avatar
sletz committed
160
161
162
        if (cur_cycle_begin > fLastSwitchUsecs + fEngineControl->fTimeOutUsecs) {
            jack_log("Process: switch to next state delta = %ld", long(cur_cycle_begin - fLastSwitchUsecs));
            ProcessNext(cur_cycle_begin);
sletz's avatar
sletz committed
163
            res = true;
sletz's avatar
sletz committed
164
        } else {
sletz's avatar
sletz committed
165
166
            jack_log("Process: waiting to switch delta = %ld", long(cur_cycle_begin - fLastSwitchUsecs));
            ProcessCurrent(cur_cycle_begin);
sletz's avatar
sletz committed
167
168
            res = false;
        }
sletz's avatar
sletz committed
169
170
    }

171
    // Cycle end
sletz's avatar
sletz committed
172
173
    fEngineControl->CycleEnd(fClientTable);
    return res;
sletz's avatar
sletz committed
174
175
}

sletz's avatar
sletz committed
176
177
178
179
180
181
182
/*
Client that finish *after* the callback date are considered late even if their output buffers may have been
correctly mixed in the time window: callbackUsecs <==> Read <==> Write.
*/

void JackEngine::CheckXRun(jack_time_t callback_usecs)  // REVOIR les conditions de fin
{
sletz's avatar
sletz committed
183
    for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
184
185
        JackClientInterface* client = fClientTable[i];
        if (client && client->GetClientControl()->fActive) {
sletz's avatar
sletz committed
186
187
            JackClientTiming* timing = fGraphManager->GetClientTiming(i);
            jack_client_state_t status = timing->fStatus;
sletz's avatar
sletz committed
188
            jack_time_t finished_date = timing->fFinishedAt;
sletz's avatar
sletz committed
189

sletz's avatar
sletz committed
190
            if (status != NotTriggered && status != Finished) {
sletz's avatar
Typo    
sletz committed
191
                jack_error("JackEngine::XRun: client = %s was not run: state = %ld", client->GetClientControl()->fName, status);
192
                fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0);  // Notify all clients
sletz's avatar
sletz committed
193
            }
sletz's avatar
sletz committed
194

sletz's avatar
sletz committed
195
196
            if (status == Finished && (long)(finished_date - callback_usecs) > 0) {
                jack_error("JackEngine::XRun: client %s finished after current callback", client->GetClientControl()->fName);
197
                fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0);  // Notify all clients
sletz's avatar
sletz committed
198
199
200
201
202
203
204
205
206
            }
        }
    }
}

//---------------
// Notifications
//---------------

sletz's avatar
sletz committed
207
void JackEngine::NotifyClient(int refnum, int event, int sync, int value1, int value2)
sletz's avatar
sletz committed
208
209
{
    JackClientInterface* client = fClientTable[refnum];
sletz's avatar
sletz committed
210

sletz's avatar
sletz committed
211
    // The client may be notified by the RT thread while closing
212
    if (!client) {
sletz's avatar
sletz committed
213
        jack_log("JackEngine::NotifyClient: client not available anymore");
sletz's avatar
sletz committed
214
215
    } else if (client->GetClientControl()->fCallback[event]) {
        if (client->ClientNotify(refnum, client->GetClientControl()->fName, event, sync, value1, value2) < 0)
sletz's avatar
sletz committed
216
            jack_error("NotifyClient fails name = %s event = %ld val1 = %ld val2 = %ld", client->GetClientControl()->fName, event, value1, value2);
sletz's avatar
sletz committed
217
    } else {
sletz's avatar
sletz committed
218
        jack_log("JackEngine::NotifyClient: no callback for event = %ld", event);
sletz's avatar
sletz committed
219
220
221
    }
}

sletz's avatar
sletz committed
222
void JackEngine::NotifyClients(int event, int sync, int value1, int value2)
sletz's avatar
sletz committed
223
224
225
{
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
sletz's avatar
sletz committed
226
227
228
        if (client) {
            if (client->GetClientControl()->fCallback[event]) {
                if (client->ClientNotify(i, client->GetClientControl()->fName, event, sync, value1, value2) < 0)
sletz's avatar
sletz committed
229
                    jack_error("NotifyClient fails name = %s event = %ld val1 = %ld val2 = %ld", client->GetClientControl()->fName, event, value1, value2);
sletz's avatar
sletz committed
230
            } else {
sletz's avatar
sletz committed
231
                jack_log("JackEngine::NotifyClients: no callback for event = %ld", event);
sletz's avatar
sletz committed
232
233
            }
        }
sletz's avatar
sletz committed
234
235
236
237
238
239
240
241
242
    }
}

int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum)
{
    // Notify existing clients of the new client and new client of existing clients.
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* old_client = fClientTable[i];
        if (old_client) {
sletz's avatar
sletz committed
243
            if (old_client->ClientNotify(refnum, name, kAddClient, true, 0, 0) < 0) {
244
                jack_error("NotifyAddClient old_client fails name = %s", old_client->GetClientControl()->fName);
sletz's avatar
sletz committed
245
246
247
                return -1;
            }
            if (new_client->ClientNotify(i, old_client->GetClientControl()->fName, kAddClient, true, 0, 0) < 0) {
248
                jack_error("NotifyAddClient new_client fails name = %s", name);
sletz's avatar
sletz committed
249
250
                return -1;
            }
sletz's avatar
sletz committed
251
252
253
254
255
256
257
258
259
260
261
262
        }
    }

    return 0;
}

void JackEngine::NotifyRemoveClient(const char* name, int refnum)
{
    // Notify existing clients (including the one beeing suppressed) of the removed client
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
        if (client) {
sletz's avatar
sletz committed
263
            client->ClientNotify(refnum, name, kRemoveClient, true, 0, 0);
sletz's avatar
sletz committed
264
265
266
267
268
        }
    }
}

// Coming from the driver
269
void JackEngine::NotifyXRun(jack_time_t callback_usecs, float delayed_usecs)
sletz's avatar
sletz committed
270
271
{
    // Use the audio thread => request thread communication channel
sletz's avatar
sletz committed
272
    fEngineControl->ResetFrameTime(callback_usecs);
273
    fEngineControl->NotifyXRun(delayed_usecs);
274
    fChannel.Notify(ALL_CLIENTS, kXRunCallback, 0);
sletz's avatar
sletz committed
275
276
277
278
279
}

void JackEngine::NotifyXRun(int refnum)
{
    if (refnum == ALL_CLIENTS) {
sletz's avatar
sletz committed
280
        NotifyClients(kXRunCallback, false, 0, 0);
sletz's avatar
sletz committed
281
    } else {
sletz's avatar
sletz committed
282
        NotifyClient(refnum, kXRunCallback, false, 0, 0);
sletz's avatar
sletz committed
283
284
285
286
287
    }
}

void JackEngine::NotifyGraphReorder()
{
sletz's avatar
sletz committed
288
    NotifyClients(kGraphOrderCallback, false, 0, 0);
sletz's avatar
sletz committed
289
290
}

291
void JackEngine::NotifyBufferSize(jack_nframes_t buffer_size)
sletz's avatar
sletz committed
292
{
293
294
295
296
297
298
    NotifyClients(kBufferSizeCallback, true, buffer_size, 0);
}

void JackEngine::NotifySampleRate(jack_nframes_t sample_rate)
{
    NotifyClients(kSampleRateCallback, true, sample_rate, 0);
sletz's avatar
sletz committed
299
300
301
302
303
}

void JackEngine::NotifyFreewheel(bool onoff)
{
    fEngineControl->fRealTime = !onoff;
sletz's avatar
sletz committed
304
    NotifyClients((onoff ? kStartFreewheelCallback : kStopFreewheelCallback), true, 0, 0);
sletz's avatar
sletz committed
305
306
307
308
}

void JackEngine::NotifyPortRegistation(jack_port_id_t port_index, bool onoff)
{
sletz's avatar
sletz committed
309
310
311
    NotifyClients((onoff ? kPortRegistrationOnCallback : kPortRegistrationOffCallback), false, port_index, 0);
}

312
313
314
315
316
void JackEngine::NotifyPortRename(jack_port_id_t port)
{
    NotifyClients(kPortRenameCallback, false, port, 0);
}

sletz's avatar
sletz committed
317
318
319
void JackEngine::NotifyPortConnect(jack_port_id_t src, jack_port_id_t dst, bool onoff)
{
    NotifyClients((onoff ? kPortConnectCallback : kPortDisconnectCallback), false, src, dst);
sletz's avatar
sletz committed
320
321
}

322
323
void JackEngine::NotifyActivate(int refnum)
{
sletz's avatar
sletz committed
324
    NotifyClient(refnum, kActivateClient, true, 0, 0);
325
326
}

327
328
329
330
//----------------------------
// Loadable client management
//----------------------------

331
int JackEngine::GetInternalClientName(int refnum, char* name_res)
332
{
333
    AssertRefnum(refnum);
sletz's avatar
sletz committed
334
335
336
337
338
339
340
    JackClientInterface* client = fClientTable[refnum];
    if (client) {
        strncpy(name_res, client->GetClientControl()->fName, JACK_CLIENT_NAME_SIZE);
        return 0;
    } else {
        return -1;
    }
341
342
343
344
}

int JackEngine::InternalClientHandle(const char* client_name, int* status, int* int_ref)
{
sletz's avatar
sletz committed
345
346
347
348
    // Clear status
    *status = 0;

    for (int i = 0; i < CLIENT_NUM; i++) {
349
350
        JackClientInterface* client = fClientTable[i];
        if (client && dynamic_cast<JackLoadableInternalClient*>(client) && (strcmp(client->GetClientControl()->fName, client_name) == 0)) {
sletz's avatar
sletz committed
351
            jack_log("InternalClientHandle found client name = %s ref = %ld",  client_name, i);
sletz's avatar
sletz committed
352
353
354
            *int_ref = i;
            return 0;
        }
355
    }
sletz's avatar
sletz committed
356
357
358

    *status |= (JackNoSuchClient | JackFailure);
    return -1;
359
360
361
362
}

int JackEngine::InternalClientUnload(int refnum, int* status)
{
363
    AssertRefnum(refnum);
sletz's avatar
sletz committed
364
365
366
367
368
369
370
371
    JackClientInterface* client = fClientTable[refnum];
    if (client) {
        int res = client->Close();
        delete client;
        *status = 0;
        return res;
    } else {
        *status = (JackNoSuchClient | JackFailure);
372
373
374
375
        return -1;
    }
}

sletz's avatar
sletz committed
376
377
378
379
//-------------------
// Client management
//-------------------

380
int JackEngine::ClientCheck(const char* name, char* name_res, int protocol, int options, int* status)
381
{
sletz's avatar
sletz committed
382
383
384
385
    // Clear status
    *status = 0;
    strcpy(name_res, name);

sletz's avatar
sletz committed
386
    jack_log("Check protocol client %ld server = %ld", protocol, JACK_PROTOCOL_VERSION);
sletz's avatar
sletz committed
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410

    if (protocol != JACK_PROTOCOL_VERSION) {
        *status |= (JackFailure | JackVersionError);
        jack_error("JACK protocol mismatch (%d vs %d)", protocol, JACK_PROTOCOL_VERSION);
        return -1;
    }

    if (ClientCheckName(name)) {

        *status |= JackNameNotUnique;

        if (options & JackUseExactName) {
            jack_error("cannot create new client; %s already exists", name);
            *status |= JackFailure;
            return -1;
        }

        if (GenerateUniqueName(name_res)) {
            *status |= JackFailure;
            return -1;
        }
    }

    return 0;
411
412
413
414
}

bool JackEngine::GenerateUniqueName(char* name)
{
sletz's avatar
sletz committed
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
    int tens, ones;
    int length = strlen(name);

    if (length > JACK_CLIENT_NAME_SIZE - 4) {
        jack_error("%s exists and is too long to make unique", name);
        return true;		/* failure */
    }

    /*  generate a unique name by appending "-01".."-99" */
    name[length++] = '-';
    tens = length++;
    ones = length++;
    name[tens] = '0';
    name[ones] = '1';
    name[length] = '\0';

    while (ClientCheckName(name)) {
        if (name[ones] == '9') {
            if (name[tens] == '9') {
                jack_error("client %s has 99 extra instances already", name);
                return true; /* give up */
            }
            name[tens]++;
            name[ones] = '0';
        } else {
            name[ones]++;
        }
    }
    return false;
444
445
}

sletz's avatar
sletz committed
446
447
448
449
450
451
452
453
454
455
456
bool JackEngine::ClientCheckName(const char* name)
{
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
        if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
            return true;
    }

    return false;
}

457
458
459
460
461
462
463
int JackEngine::GetClientPID(const char* name)
{
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
        if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
            return client->GetClientControl()->fPID;
    }
sletz's avatar
sletz committed
464

465
466
467
    return 0;
}

sletz's avatar
sletz committed
468
469
470
471
472
473
474
int JackEngine::GetClientRefNum(const char* name)
{
    for (int i = 0; i < CLIENT_NUM; i++) {
        JackClientInterface* client = fClientTable[i];
        if (client && (strcmp(client->GetClientControl()->fName, name) == 0))
            return client->GetClientControl()->fRefNum;
    }
sletz's avatar
sletz committed
475

sletz's avatar
sletz committed
476
477
478
    return -1;
}

sletz's avatar
sletz committed
479
// Used for external clients
480
int JackEngine::ClientExternalOpen(const char* name, int pid, int* ref, int* shared_engine, int* shared_client, int* shared_graph_manager)
sletz's avatar
sletz committed
481
{
sletz's avatar
sletz committed
482
    jack_log("JackEngine::ClientOpen: name = %s ", name);
sletz's avatar
sletz committed
483
484

    int refnum = AllocateRefnum();
sletz's avatar
sletz committed
485
486
487
488
    if (refnum < 0) {
        jack_error("No more refnum available");
        return -1;
    }
sletz's avatar
sletz committed
489
490

    JackExternalClient* client = new JackExternalClient();
sletz's avatar
sletz committed
491

492
    if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0)) {
493
        jack_error("Cannot allocate synchro");
sletz's avatar
sletz committed
494
        goto error;
sletz's avatar
sletz committed
495
496
    }

497
    if (client->Open(name, pid, refnum, shared_client) < 0) {
sletz's avatar
sletz committed
498
        jack_error("Cannot open client");
499
        goto error;
sletz's avatar
sletz committed
500
501
    }

502
    if (!fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) {
sletz's avatar
sletz committed
503
504
        // Failure if RT thread is not running (problem with the driver...)
        jack_error("Driver is not running");
505
        goto error;
sletz's avatar
sletz committed
506
507
    }

508
509
    fClientTable[refnum] = client;

sletz's avatar
sletz committed
510
511
    if (NotifyAddClient(client, name, refnum) < 0) {
        jack_error("Cannot notify add client");
512
        goto error;
sletz's avatar
sletz committed
513
    }
sletz's avatar
sletz committed
514

sletz's avatar
sletz committed
515
516
    fGraphManager->InitRefNum(refnum);
    fEngineControl->ResetRollingUsecs();
sletz's avatar
sletz committed
517
518
519
    *shared_engine = fEngineControl->GetShmIndex();
    *shared_graph_manager = fGraphManager->GetShmIndex();
    *ref = refnum;
520
521
522
    return 0;

error:
523
    // Cleanup...
524
    fSynchroTable[refnum].Destroy();
525
    fClientTable[refnum] = 0;
sletz's avatar
sletz committed
526
    client->Close();
sletz's avatar
sletz committed
527
    delete client;
sletz's avatar
sletz committed
528
529
530
531
    return -1;
}

// Used for server driver clients
532
int JackEngine::ClientInternalOpen(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, bool wait)
sletz's avatar
sletz committed
533
{
sletz's avatar
sletz committed
534
    jack_log("JackEngine::ClientInternalNew: name = %s", name);
sletz's avatar
sletz committed
535
536
537

    int refnum = AllocateRefnum();
    if (refnum < 0) {
sletz's avatar
sletz committed
538
        jack_error("No more refnum available");
539
        goto error;
sletz's avatar
sletz committed
540
541
    }

542
    if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0)) {
sletz's avatar
sletz committed
543
        jack_error("Cannot allocate synchro");
544
        goto error;
sletz's avatar
sletz committed
545
    }
sletz's avatar
sletz committed
546

547
    if (wait && !fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) {
548
549
        // Failure if RT thread is not running (problem with the driver...)
        jack_error("Driver is not running");
550
        goto error;
551
    }
sletz's avatar
sletz committed
552

553
554
    fClientTable[refnum] = client;

sletz's avatar
sletz committed
555
556
    if (NotifyAddClient(client, name, refnum) < 0) {
        jack_error("Cannot notify add client");
557
        goto error;
sletz's avatar
sletz committed
558
559
    }

sletz's avatar
sletz committed
560
561
    fGraphManager->InitRefNum(refnum);
    fEngineControl->ResetRollingUsecs();
sletz's avatar
sletz committed
562
563
564
565
    *shared_engine = fEngineControl;
    *shared_manager = fGraphManager;
    *ref = refnum;
    return 0;
566
567
568

error:
    // Cleanup...
569
    fSynchroTable[refnum].Destroy();
570
571
    fClientTable[refnum] = 0;
    return -1;
sletz's avatar
sletz committed
572
573
}

574
// Used for external clients
sletz's avatar
sletz committed
575
int JackEngine::ClientExternalClose(int refnum)
sletz's avatar
sletz committed
576
{
577
    AssertRefnum(refnum);
sletz's avatar
sletz committed
578
    JackClientInterface* client = fClientTable[refnum];
sletz's avatar
sletz committed
579

sletz's avatar
sletz committed
580
581
582
583
584
585
586
587
588
589
590
    if (client)	{
        fEngineControl->fTransport.ResetTimebase(refnum);
        int res = ClientCloseAux(refnum, client, true);
        client->Close();
        delete client;
        return res;
    } else {
        return -1;
    }
}

591
592
// Used for server internal clients or drivers when the RT thread is stopped
int JackEngine::ClientInternalClose(int refnum, bool wait)
sletz's avatar
sletz committed
593
{
594
    AssertRefnum(refnum);
sletz's avatar
sletz committed
595
    JackClientInterface* client = fClientTable[refnum];
596
    return (client)	? ClientCloseAux(refnum, client, wait) : -1;
sletz's avatar
sletz committed
597
598
599
600
}

int JackEngine::ClientCloseAux(int refnum, JackClientInterface* client, bool wait)
{
601
    jack_log("JackEngine::ClientCloseAux ref = %ld", refnum);
sletz's avatar
sletz committed
602

603
    // Unregister all ports ==> notifications are sent
sletz's avatar
sletz committed
604
605
606
607
608
    jack_int_t ports[PORT_NUM_FOR_CLIENT];
    int i;

    fGraphManager->GetInputPorts(refnum, ports);
    for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY) ; i++) {
609
        PortUnRegister(refnum, ports[i]);
sletz's avatar
sletz committed
610
611
612
613
    }

    fGraphManager->GetOutputPorts(refnum, ports);
    for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY) ; i++) {
614
        PortUnRegister(refnum, ports[i]);
sletz's avatar
sletz committed
615
    }
sletz's avatar
sletz committed
616

617
618
    // Remove the client from the table
    ReleaseRefnum(refnum);
sletz's avatar
sletz committed
619
620

    // Remove all ports
sletz's avatar
Typo    
sletz committed
621
    fGraphManager->RemoveAllPorts(refnum);
sletz's avatar
sletz committed
622
623
624

    // Wait until next cycle to be sure client is not used anymore
    if (wait) {
625
        if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 2)) { // Must wait at least until a switch occurs in Process, even in case of graph end failure
sletz's avatar
sletz committed
626
            jack_error("JackEngine::ClientCloseAux wait error ref = %ld", refnum);
sletz's avatar
sletz committed
627
        }
sletz's avatar
Typo    
sletz committed
628
    }
sletz's avatar
sletz committed
629

sletz's avatar
Typo    
sletz committed
630
    // Notify running clients
631
    NotifyRemoveClient(client->GetClientControl()->fName, client->GetClientControl()->fRefNum);
sletz's avatar
sletz committed
632
633

    // Cleanup...
634
    fSynchroTable[refnum].Destroy();
sletz's avatar
sletz committed
635
    fEngineControl->ResetRollingUsecs();
sletz's avatar
sletz committed
636
637
638
    return 0;
}

639
int JackEngine::ClientActivate(int refnum, bool state)
sletz's avatar
sletz committed
640
{
641
    AssertRefnum(refnum);
sletz's avatar
sletz committed
642
    JackClientInterface* client = fClientTable[refnum];
sletz's avatar
sletz committed
643
    assert(fClientTable[refnum]);
sletz's avatar
sletz committed
644

sletz's avatar
sletz committed
645
    jack_log("JackEngine::ClientActivate ref = %ld name = %s", refnum, client->GetClientControl()->fName);
sletz's avatar
sletz committed
646
    if (state)
647
        fGraphManager->Activate(refnum);
sletz's avatar
sletz committed
648

sletz's avatar
sletz committed
649
    // Wait for graph state change to be effective
650
    if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) {
sletz's avatar
sletz committed
651
652
653
654
655
656
        jack_error("JackEngine::ClientActivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName);
        return -1;
    } else {
        NotifyActivate(refnum);
        return 0;
    }
sletz's avatar
sletz committed
657
658
659
660
661
}

// May be called without client
int JackEngine::ClientDeactivate(int refnum)
{
662
    AssertRefnum(refnum);
sletz's avatar
sletz committed
663
    JackClientInterface* client = fClientTable[refnum];
sletz's avatar
sletz committed
664
665
666
    if (client == NULL)
        return -1;

sletz's avatar
sletz committed
667
    jack_log("JackEngine::ClientDeactivate ref = %ld name = %s", refnum, client->GetClientControl()->fName);
sletz's avatar
sletz committed
668

669
670
671
    // Disconnect all ports ==> notifications are sent
    jack_int_t ports[PORT_NUM_FOR_CLIENT];
    int i;
sletz's avatar
sletz committed
672

673
674
675
676
677
678
679
680
681
    fGraphManager->GetInputPorts(refnum, ports);
    for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY) ; i++) {
        PortDisconnect(refnum, ports[i], ALL_PORTS);
    }

    fGraphManager->GetOutputPorts(refnum, ports);
    for (i = 0; (i < PORT_NUM_FOR_CLIENT) && (ports[i] != EMPTY) ; i++) {
        PortDisconnect(refnum, ports[i], ALL_PORTS);
    }
sletz's avatar
sletz committed
682

sletz's avatar
sletz committed
683
684
685
686
    fGraphManager->Deactivate(refnum);
    fLastSwitchUsecs = 0; // Force switch to occur next cycle, even when called with "dead" clients

    // Wait for graph state change to be effective
687
    if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) {
sletz's avatar
sletz committed
688
689
690
691
692
        jack_error("JackEngine::ClientDeactivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName);
        return -1;
    } else {
        return 0;
    }
sletz's avatar
sletz committed
693
694
695
696
697
698
}

//-----------------
// Port management
//-----------------

699
int JackEngine::PortRegister(int refnum, const char* name, const char *type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index)
sletz's avatar
sletz committed
700
{
sletz's avatar
sletz committed
701
    jack_log("JackEngine::PortRegister ref = %ld name = %s type = %s flags = %d buffer_size = %d", refnum, name, type, flags, buffer_size);
702
    AssertRefnum(refnum);
sletz's avatar
sletz committed
703
    assert(fClientTable[refnum]);
sletz's avatar
sletz committed
704

705
    // Check if port name already exists
sletz's avatar
sletz committed
706
    if (fGraphManager->GetPort(name) != NO_PORT) {
707
        jack_error("port_name \"%s\" already exists", name);
sletz's avatar
sletz committed
708
        return -1;
709
    }
sletz's avatar
sletz committed
710

711
    *port_index = fGraphManager->AllocatePort(refnum, name, type, (JackPortFlags)flags, fEngineControl->fBufferSize);
sletz's avatar
sletz committed
712
713
714
715
716
717
718
719
720
721
    if (*port_index != NO_PORT) {
        NotifyPortRegistation(*port_index, true);
        return 0;
    } else {
        return -1;
    }
}

int JackEngine::PortUnRegister(int refnum, jack_port_id_t port_index)
{
sletz's avatar
sletz committed
722
    jack_log("JackEngine::PortUnRegister ref = %ld port_index = %ld", refnum, port_index);
723
    AssertRefnum(refnum);
sletz's avatar
sletz committed
724
    assert(fClientTable[refnum]);
sletz's avatar
sletz committed
725

726
727
    // Disconnect port ==> notification is sent
    PortDisconnect(refnum, port_index, ALL_PORTS);
sletz's avatar
sletz committed
728

729
    if (fGraphManager->ReleasePort(refnum, port_index) == 0) {
sletz's avatar
sletz committed
730
731
732
733
734
735
736
737
738
        NotifyPortRegistation(port_index, false);
        return 0;
    } else {
        return -1;
    }
}

int JackEngine::PortConnect(int refnum, const char* src, const char* dst)
{
sletz's avatar
sletz committed
739
    jack_log("JackEngine::PortConnect src = %s dst = %s", src, dst);
sletz's avatar
sletz committed
740
741
742
743
744
745
746
747
748
    jack_port_id_t port_src, port_dst;

    return (fGraphManager->CheckPorts(src, dst, &port_src, &port_dst) < 0)
           ? -1
           : PortConnect(refnum, port_src, port_dst);
}

int JackEngine::PortConnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
{
sletz's avatar
sletz committed
749
    jack_log("JackEngine::PortConnect src = %d dst = %d", src, dst);
750
    AssertRefnum(refnum);
sletz's avatar
sletz committed
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
    JackClientInterface* client;
    int ref;

    if (fGraphManager->CheckPorts(src, dst) < 0)
        return -1;

    ref = fGraphManager->GetOutputRefNum(src);
    assert(ref >= 0);
    client = fClientTable[ref];
    assert(client);
    if (!client->GetClientControl()->fActive) {
        jack_error("Cannot connect ports owned by inactive clients:"
                   " \"%s\" is not active", client->GetClientControl()->fName);
        return -1;
    }

    ref = fGraphManager->GetInputRefNum(dst);
    assert(ref >= 0);
    client = fClientTable[ref];
    assert(client);
    if (!client->GetClientControl()->fActive) {
        jack_error("Cannot connect ports owned by inactive clients:"
                   " \"%s\" is not active", client->GetClientControl()->fName);
        return -1;
    }
sletz's avatar
sletz committed
776
777

    int res = fGraphManager->Connect(src, dst);
sletz's avatar
sletz committed
778
    if (res == 0)
sletz's avatar
sletz committed
779
780
        NotifyPortConnect(src, dst, true);
    return res;
sletz's avatar
sletz committed
781
782
}

783
784
int JackEngine::PortDisconnect(int refnum, const char* src, const char* dst)
{
sletz's avatar
sletz committed
785
    jack_log("JackEngine::PortDisconnect src = %s dst = %s", src, dst);
786
    AssertRefnum(refnum);
787
    jack_port_id_t port_src, port_dst;
sletz's avatar
sletz committed
788
789
790
791
792
793
794
795
796

    if (fGraphManager->CheckPorts(src, dst, &port_src, &port_dst) < 0) {
        return -1;
    } else if (fGraphManager->Disconnect(port_src, port_dst) == 0) {
        NotifyPortConnect(port_src, port_dst, false);
        return 0;
    } else {
        return -1;
    }
797
798
}

sletz's avatar
sletz committed
799
800
int JackEngine::PortDisconnect(int refnum, jack_port_id_t src, jack_port_id_t dst)
{
sletz's avatar
sletz committed
801
    jack_log("JackEngine::PortDisconnect src = %d dst = %d", src, dst);
802
    AssertRefnum(refnum);
sletz's avatar
sletz committed
803

sletz's avatar
sletz committed
804
    if (dst == ALL_PORTS) {
sletz's avatar
sletz committed
805

806
        jack_int_t connections[CONNECTION_NUM_FOR_PORT];
sletz's avatar
sletz committed
807
808
809
810
811
        fGraphManager->GetConnections(src, connections);

        // Notifications
        JackPort* port = fGraphManager->GetPort(src);
        if (port->GetFlags() & JackPortIsOutput) {
812
            for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) {
sletz's avatar
sletz committed
813
                jack_log("NotifyPortConnect src = %ld dst = %ld false", src, connections[i]);
sletz's avatar
sletz committed
814
815
816
                NotifyPortConnect(src, connections[i], false);
            }
        } else {
817
            for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) {
sletz's avatar
sletz committed
818
                jack_log("NotifyPortConnect src = %ld dst = %ld false", connections[i], src);
sletz's avatar
sletz committed
819
820
821
822
823
824
825
826
827
828
829
                NotifyPortConnect(connections[i], src, false);
            }
        }

        return fGraphManager->DisconnectAll(src);
    } else if (fGraphManager->CheckPorts(src, dst) < 0) {
        return -1;
    } else if (fGraphManager->Disconnect(src, dst) == 0) {
        // Notifications
        NotifyPortConnect(src, dst, false);
        return 0;
sletz's avatar
sletz committed
830
    } else {
sletz's avatar
sletz committed
831
832
        return -1;
    }
sletz's avatar
sletz committed
833
834
}

835
836
int JackEngine::PortRename(int refnum, jack_port_id_t port, const char* name)
{
sletz's avatar
sletz committed
837
838
839
    fGraphManager->GetPort(port)->SetName(name);
    NotifyPortRename(port);
    return 0;
840
}
sletz's avatar
sletz committed
841
842
843

} // end of namespace