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
#include "JackEngineControl.h"
#include "JackClientControl.h"
30
#include "JackServerGlobals.h"
sletz's avatar
sletz committed
31
32
#include "JackGlobals.h"
#include "JackChannel.h"
sletz's avatar
sletz committed
33
#include "JackError.h"
sletz's avatar
sletz committed
34
35
36
37

namespace Jack
{

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

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

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

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

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

72
    // Close remaining clients (RT is stopped)
73
    for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
74
75
76
        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
77
78
            // Close does not delete the pointer for internal clients
            fClientTable[i] = NULL;
sletz's avatar
sletz committed
79
80
81
82
            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
83
84
            // Close deletes the pointer for external clients
            fClientTable[i] = NULL;
sletz's avatar
sletz committed
85
86
        }
    }
sletz's avatar
sletz committed
87

sletz's avatar
sletz committed
88
89
    return 0;
}
90
91
92
93
94
    
void JackEngine::NotifyQuit()
{
    fChannel.NotifyQuit();
}
sletz's avatar
sletz committed
95

sletz's avatar
Cleanup    
sletz committed
96
97
98
99
//-----------------------------
// Client ressource management
//-----------------------------

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

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

    if (fEngineControl->fTemporary) {
        int i;
117
        for (i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
118
119
120
121
122
            if (fClientTable[i])
                break;
        }
        if (i == CLIENT_NUM) {
            // last client and temporay case: quit the server
sletz's avatar
sletz committed
123
            jack_log("JackEngine::ReleaseRefnum server quit");
sletz's avatar
sletz committed
124
            fEngineControl->fTemporary = false;
125
            throw JackTemporaryException();
sletz's avatar
sletz committed
126
127
        }
    }
sletz's avatar
sletz committed
128
129
}

sletz's avatar
sletz committed
130
131
132
133
//------------------
// Graph management
//------------------

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

sletz's avatar
sletz committed
142
void JackEngine::ProcessCurrent(jack_time_t cur_cycle_begin)
sletz's avatar
sletz committed
143
{
sletz's avatar
sletz committed
144
145
    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
146
    fGraphManager->RunCurrentGraph();
sletz's avatar
sletz committed
147
148
}

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

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

sletz's avatar
sletz committed
156
    // Graph
sletz's avatar
sletz committed
157
    if (fGraphManager->IsFinishedGraph()) {
sletz's avatar
sletz committed
158
        ProcessNext(cur_cycle_begin);
sletz's avatar
sletz committed
159
        res = true;
sletz's avatar
sletz committed
160
    } else {
sletz's avatar
sletz committed
161
        jack_log("Process: graph not finished!");
sletz's avatar
sletz committed
162
163
164
        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
165
            res = true;
sletz's avatar
sletz committed
166
        } else {
sletz's avatar
sletz committed
167
168
            jack_log("Process: waiting to switch delta = %ld", long(cur_cycle_begin - fLastSwitchUsecs));
            ProcessCurrent(cur_cycle_begin);
sletz's avatar
sletz committed
169
170
            res = false;
        }
sletz's avatar
sletz committed
171
172
    }

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

sletz's avatar
sletz committed
178
179
180
181
182
183
184
/*
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
{
185
    for (int i = fEngineControl->fDriverNum; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
186
187
        JackClientInterface* client = fClientTable[i];
        if (client && client->GetClientControl()->fActive) {
sletz's avatar
sletz committed
188
189
            JackClientTiming* timing = fGraphManager->GetClientTiming(i);
            jack_client_state_t status = timing->fStatus;
sletz's avatar
sletz committed
190
            jack_time_t finished_date = timing->fFinishedAt;
sletz's avatar
sletz committed
191

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

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

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

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

sletz's avatar
sletz committed
213
    // The client may be notified by the RT thread while closing
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
    if (client) {
    
        if (client && client->GetClientControl()->fCallback[event]) {
            /*
                Important for internal clients : unlock before calling the notification callbacks.
            */
            bool res = fMutex.Unlock();
            if (client->ClientNotify(refnum, client->GetClientControl()->fName, event, sync, message, value1, value2) < 0)
                jack_error("NotifyClient fails name = %s event = %ld val1 = %ld val2 = %ld", client->GetClientControl()->fName, event, value1, value2);
            if (res)
                fMutex.Lock();
       
        } else {
            jack_log("JackEngine::NotifyClient: no callback for event = %ld", event);
        }
sletz's avatar
sletz committed
229
230
231
    }
}

232
void JackEngine::NotifyClients(int event, int sync, const char* message, int value1, int value2)
sletz's avatar
sletz committed
233
234
{
    for (int i = 0; i < CLIENT_NUM; i++) {
235
        NotifyClient(i, event, sync, message, value1, value2);
sletz's avatar
sletz committed
236
237
238
239
240
    }
}

int JackEngine::NotifyAddClient(JackClientInterface* new_client, const char* name, int refnum)
{
241
    jack_log("JackEngine::NotifyAddClient: name = %s", name);
sletz's avatar
sletz committed
242
243
244
245
    // 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) {
246
            if (old_client->ClientNotify(refnum, name, kAddClient, true, "", 0, 0) < 0) {
247
                jack_error("NotifyAddClient old_client fails name = %s", old_client->GetClientControl()->fName);
sletz's avatar
sletz committed
248
249
                return -1;
            }
250
            if (new_client->ClientNotify(i, old_client->GetClientControl()->fName, kAddClient, true, "", 0, 0) < 0) {
251
                jack_error("NotifyAddClient new_client fails name = %s", name);
sletz's avatar
sletz committed
252
253
                return -1;
            }
sletz's avatar
sletz committed
254
255
256
257
258
259
260
261
262
263
264
265
        }
    }

    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) {
266
            client->ClientNotify(refnum, name, kRemoveClient, true, "",0, 0);
sletz's avatar
sletz committed
267
268
269
270
271
        }
    }
}

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

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

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

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

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

303
304
305
306
307
void JackEngine::NotifyFailure(int code, const char* reason)
{
    NotifyClients(kShutDownCallback, false, reason, code, 0);
}
    
sletz's avatar
sletz committed
308
309
void JackEngine::NotifyFreewheel(bool onoff)
{
310
311
    if (onoff) {
        // Save RT state
312
        fEngineControl->fSavedRealTime = fEngineControl->fRealTime;
313
314
315
        fEngineControl->fRealTime = false;
    } else {
        // Restore RT state
316
317
        fEngineControl->fRealTime = fEngineControl->fSavedRealTime;
        fEngineControl->fSavedRealTime = false;
318
    }
319
    NotifyClients((onoff ? kStartFreewheelCallback : kStopFreewheelCallback), true, "", 0, 0);
sletz's avatar
sletz committed
320
321
322
323
}

void JackEngine::NotifyPortRegistation(jack_port_id_t port_index, bool onoff)
{
324
    NotifyClients((onoff ? kPortRegistrationOnCallback : kPortRegistrationOffCallback), false, "", port_index, 0);
sletz's avatar
sletz committed
325
326
}

327
void JackEngine::NotifyPortRename(jack_port_id_t port, const char* old_name)
328
{
329
    NotifyClients(kPortRenameCallback, false, old_name, port, 0);
330
331
}

sletz's avatar
sletz committed
332
333
void JackEngine::NotifyPortConnect(jack_port_id_t src, jack_port_id_t dst, bool onoff)
{
334
    NotifyClients((onoff ? kPortConnectCallback : kPortDisconnectCallback), false, "", src, dst);
sletz's avatar
sletz committed
335
336
}

337
338
void JackEngine::NotifyActivate(int refnum)
{
339
    NotifyClient(refnum, kActivateClient, true, "", 0, 0);
340
341
}

342
343
344
345
//----------------------------
// Loadable client management
//----------------------------

346
int JackEngine::GetInternalClientName(int refnum, char* name_res)
347
{
sletz's avatar
sletz committed
348
349
350
351
352
353
354
    JackClientInterface* client = fClientTable[refnum];
    if (client) {
        strncpy(name_res, client->GetClientControl()->fName, JACK_CLIENT_NAME_SIZE);
        return 0;
    } else {
        return -1;
    }
355
356
357
358
}

int JackEngine::InternalClientHandle(const char* client_name, int* status, int* int_ref)
{
sletz's avatar
sletz committed
359
360
361
362
    // Clear status
    *status = 0;

    for (int i = 0; i < CLIENT_NUM; i++) {
363
364
        JackClientInterface* client = fClientTable[i];
        if (client && dynamic_cast<JackLoadableInternalClient*>(client) && (strcmp(client->GetClientControl()->fName, client_name) == 0)) {
sletz's avatar
sletz committed
365
            jack_log("InternalClientHandle found client name = %s ref = %ld",  client_name, i);
sletz's avatar
sletz committed
366
367
368
            *int_ref = i;
            return 0;
        }
369
    }
sletz's avatar
sletz committed
370
371
372

    *status |= (JackNoSuchClient | JackFailure);
    return -1;
373
374
375
376
}

int JackEngine::InternalClientUnload(int refnum, int* status)
{
sletz's avatar
sletz committed
377
378
379
380
381
382
383
384
    JackClientInterface* client = fClientTable[refnum];
    if (client) {
        int res = client->Close();
        delete client;
        *status = 0;
        return res;
    } else {
        *status = (JackNoSuchClient | JackFailure);
385
386
387
388
        return -1;
    }
}

sletz's avatar
sletz committed
389
390
391
392
//-------------------
// Client management
//-------------------

393
int JackEngine::ClientCheck(const char* name, char* name_res, int protocol, int options, int* status)
394
{
sletz's avatar
sletz committed
395
396
397
398
    // Clear status
    *status = 0;
    strcpy(name_res, name);

sletz's avatar
sletz committed
399
    jack_log("Check protocol client %ld server = %ld", protocol, JACK_PROTOCOL_VERSION);
sletz's avatar
sletz committed
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423

    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;
424
425
426
427
}

bool JackEngine::GenerateUniqueName(char* name)
{
sletz's avatar
sletz committed
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
    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;
457
458
}

sletz's avatar
sletz committed
459
460
461
462
463
464
465
466
467
468
469
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;
}

470
471
472
473
474
475
476
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
477

478
479
480
    return 0;
}

sletz's avatar
sletz committed
481
482
483
484
485
486
487
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
488

sletz's avatar
sletz committed
489
490
491
    return -1;
}

sletz's avatar
sletz committed
492
// Used for external clients
493
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
494
{
495
    jack_log("JackEngine::ClientExternalOpen: name = %s ", name);
sletz's avatar
sletz committed
496
497

    int refnum = AllocateRefnum();
sletz's avatar
sletz committed
498
499
500
501
    if (refnum < 0) {
        jack_error("No more refnum available");
        return -1;
    }
sletz's avatar
sletz committed
502
503

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

505
    if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0)) {
506
        jack_error("Cannot allocate synchro");
sletz's avatar
sletz committed
507
        goto error;
sletz's avatar
sletz committed
508
509
    }

510
    if (client->Open(name, pid, refnum, shared_client) < 0) {
sletz's avatar
sletz committed
511
        jack_error("Cannot open client");
512
        goto error;
sletz's avatar
sletz committed
513
514
    }

515
    if (!fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) {
sletz's avatar
sletz committed
516
517
        // Failure if RT thread is not running (problem with the driver...)
        jack_error("Driver is not running");
518
        goto error;
sletz's avatar
sletz committed
519
520
    }

521
522
    fClientTable[refnum] = client;

sletz's avatar
sletz committed
523
524
    if (NotifyAddClient(client, name, refnum) < 0) {
        jack_error("Cannot notify add client");
525
        goto error;
sletz's avatar
sletz committed
526
    }
sletz's avatar
sletz committed
527

sletz's avatar
sletz committed
528
529
    fGraphManager->InitRefNum(refnum);
    fEngineControl->ResetRollingUsecs();
sletz's avatar
sletz committed
530
531
532
    *shared_engine = fEngineControl->GetShmIndex();
    *shared_graph_manager = fGraphManager->GetShmIndex();
    *ref = refnum;
533
534
535
    return 0;

error:
536
    // Cleanup...
537
    fSynchroTable[refnum].Destroy();
538
    fClientTable[refnum] = 0;
sletz's avatar
sletz committed
539
    client->Close();
sletz's avatar
sletz committed
540
    delete client;
sletz's avatar
sletz committed
541
542
543
544
    return -1;
}

// Used for server driver clients
545
int JackEngine::ClientInternalOpen(const char* name, int* ref, JackEngineControl** shared_engine, JackGraphManager** shared_manager, JackClientInterface* client, bool wait)
sletz's avatar
sletz committed
546
{
547
    jack_log("JackEngine::ClientInternalOpen: name = %s", name);
sletz's avatar
sletz committed
548
549
550

    int refnum = AllocateRefnum();
    if (refnum < 0) {
sletz's avatar
sletz committed
551
        jack_error("No more refnum available");
552
        goto error;
sletz's avatar
sletz committed
553
554
    }

555
    if (!fSynchroTable[refnum].Allocate(name, fEngineControl->fServerName, 0)) {
sletz's avatar
sletz committed
556
        jack_error("Cannot allocate synchro");
557
        goto error;
sletz's avatar
sletz committed
558
    }
sletz's avatar
sletz committed
559

560
    if (wait && !fSignal.LockedTimedWait(DRIVER_OPEN_TIMEOUT * 1000000)) {
561
562
        // Failure if RT thread is not running (problem with the driver...)
        jack_error("Driver is not running");
563
        goto error;
564
    }
sletz's avatar
sletz committed
565

566
567
    fClientTable[refnum] = client;

sletz's avatar
sletz committed
568
569
    if (NotifyAddClient(client, name, refnum) < 0) {
        jack_error("Cannot notify add client");
570
        goto error;
sletz's avatar
sletz committed
571
572
    }

sletz's avatar
sletz committed
573
574
    fGraphManager->InitRefNum(refnum);
    fEngineControl->ResetRollingUsecs();
sletz's avatar
sletz committed
575
576
577
578
    *shared_engine = fEngineControl;
    *shared_manager = fGraphManager;
    *ref = refnum;
    return 0;
579
580
581

error:
    // Cleanup...
582
    fSynchroTable[refnum].Destroy();
583
584
    fClientTable[refnum] = 0;
    return -1;
sletz's avatar
sletz committed
585
586
}

587
// Used for external clients
sletz's avatar
sletz committed
588
int JackEngine::ClientExternalClose(int refnum)
sletz's avatar
sletz committed
589
590
{
    JackClientInterface* client = fClientTable[refnum];
sletz's avatar
sletz committed
591

sletz's avatar
sletz committed
592
593
594
595
596
597
598
599
600
601
602
    if (client)	{
        fEngineControl->fTransport.ResetTimebase(refnum);
        int res = ClientCloseAux(refnum, client, true);
        client->Close();
        delete client;
        return res;
    } else {
        return -1;
    }
}

603
604
// 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
605
606
{
    JackClientInterface* client = fClientTable[refnum];
607
    return (client)	? ClientCloseAux(refnum, client, wait) : -1;
sletz's avatar
sletz committed
608
609
610
611
}

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

614
    // Unregister all ports ==> notifications are sent
sletz's avatar
sletz committed
615
616
617
618
619
    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++) {
620
        PortUnRegister(refnum, ports[i]);
sletz's avatar
sletz committed
621
622
623
624
    }

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

628
629
    // Remove the client from the table
    ReleaseRefnum(refnum);
sletz's avatar
sletz committed
630
631

    // Remove all ports
sletz's avatar
Typo    
sletz committed
632
    fGraphManager->RemoveAllPorts(refnum);
sletz's avatar
sletz committed
633
634
635

    // Wait until next cycle to be sure client is not used anymore
    if (wait) {
636
        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
637
            jack_error("JackEngine::ClientCloseAux wait error ref = %ld", refnum);
sletz's avatar
sletz committed
638
        }
sletz's avatar
Typo    
sletz committed
639
    }
sletz's avatar
sletz committed
640

sletz's avatar
Typo    
sletz committed
641
    // Notify running clients
642
    NotifyRemoveClient(client->GetClientControl()->fName, client->GetClientControl()->fRefNum);
sletz's avatar
sletz committed
643
644

    // Cleanup...
645
    fSynchroTable[refnum].Destroy();
sletz's avatar
sletz committed
646
    fEngineControl->ResetRollingUsecs();
sletz's avatar
sletz committed
647
648
649
    return 0;
}

sletz's avatar
sletz committed
650
int JackEngine::ClientActivate(int refnum, bool is_real_time)
sletz's avatar
sletz committed
651
652
{
    JackClientInterface* client = fClientTable[refnum];
653
 
sletz's avatar
sletz committed
654
    jack_log("JackEngine::ClientActivate ref = %ld name = %s", refnum, client->GetClientControl()->fName);
sletz's avatar
sletz committed
655
    if (is_real_time)
656
        fGraphManager->Activate(refnum);
sletz's avatar
sletz committed
657

sletz's avatar
sletz committed
658
    // Wait for graph state change to be effective
659
    if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) {
sletz's avatar
sletz committed
660
661
662
663
664
665
        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
666
667
668
669
670
671
}

// May be called without client
int JackEngine::ClientDeactivate(int refnum)
{
    JackClientInterface* client = fClientTable[refnum];
sletz's avatar
sletz committed
672
673
674
    if (client == NULL)
        return -1;

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

677
678
679
    // Disconnect all ports ==> notifications are sent
    jack_int_t ports[PORT_NUM_FOR_CLIENT];
    int i;
sletz's avatar
sletz committed
680

681
682
683
684
685
686
687
688
689
    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
690

sletz's avatar
sletz committed
691
692
693
694
    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
695
    if (!fSignal.LockedTimedWait(fEngineControl->fTimeOutUsecs * 10)) {
sletz's avatar
sletz committed
696
697
698
699
700
        jack_error("JackEngine::ClientDeactivate wait error ref = %ld name = %s", refnum, client->GetClientControl()->fName);
        return -1;
    } else {
        return 0;
    }
sletz's avatar
sletz committed
701
702
703
704
705
706
}

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

707
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
708
{
sletz's avatar
sletz committed
709
    jack_log("JackEngine::PortRegister ref = %ld name = %s type = %s flags = %d buffer_size = %d", refnum, name, type, flags, buffer_size);
710
 
711
    // Check if port name already exists
sletz's avatar
sletz committed
712
    if (fGraphManager->GetPort(name) != NO_PORT) {
713
        jack_error("port_name \"%s\" already exists", name);
sletz's avatar
sletz committed
714
        return -1;
715
    }
sletz's avatar
sletz committed
716

717
    *port_index = fGraphManager->AllocatePort(refnum, name, type, (JackPortFlags)flags, fEngineControl->fBufferSize);
sletz's avatar
sletz committed
718
719
720
721
722
723
724
725
726
727
    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)
{
728
    JackLock lock(this);
sletz's avatar
sletz committed
729
    jack_log("JackEngine::PortUnRegister ref = %ld port_index = %ld", refnum, port_index);
730
 
731
732
    // Disconnect port ==> notification is sent
    PortDisconnect(refnum, port_index, ALL_PORTS);
sletz's avatar
sletz committed
733

734
    if (fGraphManager->ReleasePort(refnum, port_index) == 0) {
sletz's avatar
sletz committed
735
736
737
738
739
740
741
742
743
        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
744
    jack_log("JackEngine::PortConnect src = %s dst = %s", src, dst);
sletz's avatar
sletz committed
745
746
    jack_port_id_t port_src, port_dst;

747
    return (fGraphManager->GetTwoPorts(src, dst, &port_src, &port_dst) < 0)
sletz's avatar
sletz committed
748
749
750
751
752
753
           ? -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
754
    jack_log("JackEngine::PortConnect src = %d dst = %d", src, dst);
sletz's avatar
sletz committed
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
    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
780
781

    int res = fGraphManager->Connect(src, dst);
sletz's avatar
sletz committed
782
    if (res == 0)
sletz's avatar
sletz committed
783
784
        NotifyPortConnect(src, dst, true);
    return res;
sletz's avatar
sletz committed
785
786
}

787
788
int JackEngine::PortDisconnect(int refnum, const char* src, const char* dst)
{
sletz's avatar
sletz committed
789
    jack_log("JackEngine::PortDisconnect src = %s dst = %s", src, dst);
790
    jack_port_id_t port_src, port_dst;
sletz's avatar
sletz committed
791

792
793
794
    return (fGraphManager->GetTwoPorts(src, dst, &port_src, &port_dst) < 0)
           ? -1
           : PortDisconnect(refnum, port_src, port_dst);
795
796
}

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

803
        jack_int_t connections[CONNECTION_NUM_FOR_PORT];
sletz's avatar
sletz committed
804
805
806
        fGraphManager->GetConnections(src, connections);

        JackPort* port = fGraphManager->GetPort(src);
807
        int ret = 0;
sletz's avatar
sletz committed
808
        if (port->GetFlags() & JackPortIsOutput) {
809
            for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) {
810
811
812
                if (PortDisconnect(refnum, src, connections[i]) != 0) {
                    ret = -1;
                }
sletz's avatar
sletz committed
813
814
            }
        } else {
815
            for (int i = 0; (i < CONNECTION_NUM_FOR_PORT) && (connections[i] != EMPTY); i++) {
816
817
818
                if (PortDisconnect(refnum, connections[i], src) != 0) {
                    ret = -1;
                }
sletz's avatar
sletz committed
819
820
821
            }
        }

822
        return ret;
sletz's avatar
sletz committed
823
824
825
826
827
828
    } 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
829
    } else {
sletz's avatar
sletz committed
830
831
        return -1;
    }
sletz's avatar
sletz committed
832
833
}

834
835
int JackEngine::PortRename(int refnum, jack_port_id_t port, const char* name)
{
836
837
    char old_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
    strcpy(old_name, fGraphManager->GetPort(port)->GetName());
sletz's avatar
sletz committed
838
    fGraphManager->GetPort(port)->SetName(name);
839
    NotifyPortRename(port, old_name);
sletz's avatar
sletz committed
840
    return 0;
841
}
sletz's avatar
sletz committed
842
843
844

} // end of namespace