JackClient.cpp 30.9 KB
Newer Older
sletz's avatar
sletz committed
1
/*
sletz's avatar
sletz committed
2
Copyright (C) 2001 Paul Davis
sletz's avatar
sletz committed
3
Copyright (C) 2004-2008 Grame
sletz's avatar
sletz committed
4
5

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

sletz's avatar
sletz committed
15
16
17
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
18
19
20
21
22
23
24
25
26

*/

#include "JackClient.h"
#include "JackGraphManager.h"
#include "JackClientControl.h"
#include "JackEngineControl.h"
#include "JackGlobals.h"
#include "JackChannel.h"
sletz's avatar
sletz committed
27
#include "JackTransportEngine.h"
sletz's avatar
sletz committed
28
#include "driver_interface.h"
29
30
#include "JackLibGlobals.h"

sletz's avatar
sletz committed
31
32
#include <math.h>
#include <string>
sletz's avatar
sletz committed
33
#include <algorithm>
sletz's avatar
sletz committed
34
35
36
37
38
39

using namespace std;

namespace Jack
{

40
#define IsRealTime() ((fProcess != NULL) | (fThreadFun != NULL) | (fSync != NULL) | (fTimebase != NULL))
41

42
JackClient::JackClient():fThread(this)
sletz's avatar
sletz committed
43
44
{}

45
JackClient::JackClient(JackSynchro* table):fThread(this)
sletz's avatar
sletz committed
46
47
48
49
50
51
{
    fSynchroTable = table;
    fProcess = NULL;
    fGraphOrder = NULL;
    fXrun = NULL;
    fShutdown = NULL;
52
    fInfoShutdown = NULL;
sletz's avatar
sletz committed
53
54
    fInit = NULL;
    fBufferSize = NULL;
sletz's avatar
sletz committed
55
    fClientRegistration = NULL;
sletz's avatar
sletz committed
56
57
    fFreewheel = NULL;
    fPortRegistration = NULL;
sletz's avatar
sletz committed
58
    fPortConnect = NULL;
59
    fPortRename = NULL;
60
    fTimebase = NULL;
sletz's avatar
sletz committed
61
    fSync = NULL;
62
    fThreadFun = NULL;
sletz's avatar
sletz committed
63
64
65
66
    fProcessArg = NULL;
    fGraphOrderArg = NULL;
    fXrunArg = NULL;
    fShutdownArg = NULL;
67
    fInfoShutdownArg = NULL;
sletz's avatar
sletz committed
68
69
70
    fInitArg = NULL;
    fBufferSizeArg = NULL;
    fFreewheelArg = NULL;
sletz's avatar
sletz committed
71
    fClientRegistrationArg = NULL;
sletz's avatar
sletz committed
72
    fPortRegistrationArg = NULL;
sletz's avatar
sletz committed
73
    fPortConnectArg = NULL;
74
    fPortRenameArg = NULL;
sletz's avatar
sletz committed
75
    fSyncArg = NULL;
76
    fTimebaseArg = NULL;
sletz's avatar
sletz committed
77
    fThreadFunArg = NULL;
sletz's avatar
sletz committed
78
79
80
}

JackClient::~JackClient()
81
{}
sletz's avatar
sletz committed
82
83
84

int JackClient::Close()
{
sletz's avatar
sletz committed
85
    jack_log("JackClient::Close ref = %ld", GetClientControl()->fRefNum);
86
    int result = 0;
87
    
sletz's avatar
sletz committed
88
    Deactivate();
89
    fChannel->Stop();  // Channels is stopped first to avoid receiving notifications while closing
90
    
91
92
    // Request close only if server is still running
    if (JackGlobals::fServerRunning) {
93
94
95
96
        fChannel->ClientClose(GetClientControl()->fRefNum, &result);
    } else {
        jack_log("JackClient::Close server is shutdown"); 
    }
97
    
sletz's avatar
sletz committed
98
    fChannel->Close();
99
    fSynchroTable[GetClientControl()->fRefNum].Disconnect();
100
    JackGlobals::fClientTable[GetClientControl()->fRefNum] = NULL;
sletz's avatar
sletz committed
101
102
103
104
105
106
107
108
109
110
    return result;
}

bool JackClient::IsActive()
{
    return (GetClientControl()) ? GetClientControl()->fActive : false;
}

pthread_t JackClient::GetThreadID()
{
111
    return fThread.GetThreadID();
sletz's avatar
sletz committed
112
113
114
}

/*!
115
	In "async" mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations.
sletz's avatar
sletz committed
116
	The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations.
117
	Drivers synchro are setup in "flush" mode if server is "async" and NOT freewheel.
sletz's avatar
sletz committed
118
119
120
121
*/
void JackClient::SetupDriverSync(bool freewheel)
{
    if (!freewheel && !GetEngineControl()->fSyncMode) {
sletz's avatar
sletz committed
122
        jack_log("JackClient::SetupDriverSync driver sem in flush mode");
123
124
125
        for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
            fSynchroTable[i].SetFlush(true);
        }
sletz's avatar
sletz committed
126
    } else {
sletz's avatar
sletz committed
127
        jack_log("JackClient::SetupDriverSync driver sem in normal mode");
128
129
        for (int i = 0; i < GetEngineControl()->fDriverNum; i++)
            fSynchroTable[i].SetFlush(false);
sletz's avatar
sletz committed
130
131
132
133
134
135
136
    }
}

/*!
\brief Notification received from the server.
*/

137
int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
sletz's avatar
sletz committed
138
{
sletz's avatar
sletz committed
139
    return 0;
sletz's avatar
sletz committed
140
141
}

142
int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
sletz's avatar
sletz committed
143
144
145
146
147
148
{
    int res = 0;

    // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
    switch (notify) {

149
        case kAddClient:
150
            res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
sletz's avatar
sletz committed
151
152
153
            break;

        case kRemoveClient:
154
            res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
sletz's avatar
sletz committed
155
156
157
            break;

        case kActivateClient:
sletz's avatar
sletz committed
158
            jack_log("JackClient::kActivateClient name = %s ref = %ld ", name, refnum);
sletz's avatar
sletz committed
159
160
161
            Init();
            break;
    }
sletz's avatar
sletz committed
162
163
164
165
166
167
168
169

    /*
    The current semantic is that notifications can only be received when the client has been activated,
    although is this implementation, one could imagine calling notifications as soon as the client has be opened.
    */
    if (IsActive()) {

        switch (notify) {
sletz's avatar
sletz committed
170
171

            case kAddClient:
sletz's avatar
sletz committed
172
                jack_log("JackClient::kAddClient fName = %s name = %s", GetClientControl()->fName, name);
173
                if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) {	// Don't call the callback for the registering client itself
sletz's avatar
sletz committed
174
                    fClientRegistration(name, 1, fClientRegistrationArg);
175
                }
sletz's avatar
sletz committed
176
177
178
                break;

            case kRemoveClient:
sletz's avatar
sletz committed
179
                jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name);
180
                if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
sletz's avatar
sletz committed
181
                    fClientRegistration(name, 0, fClientRegistrationArg);
182
                }
sletz's avatar
sletz committed
183
                break;
sletz's avatar
sletz committed
184

185
            case kBufferSizeCallback:
sletz's avatar
sletz committed
186
                jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1);
187
                if (fBufferSize) {
sletz's avatar
sletz committed
188
                    res = fBufferSize(value1, fBufferSizeArg);
189
                }
sletz's avatar
sletz committed
190
                break;
191
192
193
                
            case kSampleRateCallback:
                jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1);
194
                if (fSampleRate) {
195
                    res = fSampleRate(value1, fSampleRateArg);
196
                }
197
                break;
sletz's avatar
sletz committed
198

199
            case kGraphOrderCallback:
sletz's avatar
sletz committed
200
                jack_log("JackClient::kGraphOrderCallback");
201
                if (fGraphOrder) {
sletz's avatar
sletz committed
202
                    res = fGraphOrder(fGraphOrderArg);
203
                }
sletz's avatar
sletz committed
204
205
                break;

206
            case kStartFreewheelCallback:
sletz's avatar
sletz committed
207
                jack_log("JackClient::kStartFreewheel");
sletz's avatar
sletz committed
208
                SetupDriverSync(true);
209
210
                fThread.DropRealTime();     // Always done (JACK server in RT mode or not...)
                if (fFreewheel) {
sletz's avatar
sletz committed
211
                    fFreewheel(1, fFreewheelArg);
212
                }
sletz's avatar
sletz committed
213
214
                break;

215
            case kStopFreewheelCallback:
sletz's avatar
sletz committed
216
                jack_log("JackClient::kStopFreewheel");
sletz's avatar
sletz committed
217
                SetupDriverSync(false);
218
                if (fFreewheel) {
sletz's avatar
sletz committed
219
                    fFreewheel(0, fFreewheelArg);
220
                }
sletz's avatar
sletz committed
221
222
223
                if (GetEngineControl()->fRealTime) {
                    fThread.AcquireRealTime();
                }
sletz's avatar
sletz committed
224
225
                break;

226
            case kPortRegistrationOnCallback:
sletz's avatar
sletz committed
227
                jack_log("JackClient::kPortRegistrationOn port_index = %ld", value1);
228
                if (fPortRegistration) {
sletz's avatar
sletz committed
229
                    fPortRegistration(value1, 1, fPortRegistrationArg);
230
                }
sletz's avatar
sletz committed
231
232
                break;

233
            case kPortRegistrationOffCallback:
sletz's avatar
sletz committed
234
                jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1);
235
                if (fPortRegistration) {
sletz's avatar
sletz committed
236
                    fPortRegistration(value1, 0, fPortRegistrationArg);
237
                }
sletz's avatar
sletz committed
238
                break;
sletz's avatar
sletz committed
239
240

            case kPortConnectCallback:
sletz's avatar
sletz committed
241
                jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2);
242
                if (fPortConnect) {
sletz's avatar
sletz committed
243
                    fPortConnect(value1, value2, 1, fPortConnectArg);
244
                }
sletz's avatar
sletz committed
245
                break;
sletz's avatar
sletz committed
246
247

            case kPortDisconnectCallback:
sletz's avatar
sletz committed
248
                jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2);
249
                if (fPortConnect) {
sletz's avatar
sletz committed
250
                    fPortConnect(value1, value2, 0, fPortConnectArg);
251
                }
sletz's avatar
sletz committed
252
                break;
253
254
                
             case kPortRenameCallback:
sletz's avatar
sletz committed
255
                jack_log("JackClient::kPortRenameCallback port = %ld", value1);
256
                if (fPortRename) {
257
                    fPortRename(value1, message, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg);
258
                }
259
                break;
sletz's avatar
sletz committed
260

261
            case kXRunCallback:
sletz's avatar
sletz committed
262
                jack_log("JackClient::kXRunCallback");
263
                if (fXrun) {
sletz's avatar
sletz committed
264
                    res = fXrun(fXrunArg);
265
                }
sletz's avatar
sletz committed
266
                break;
267
268
269
                
            case kShutDownCallback:
                jack_log("JackClient::kShutDownCallback");
270
                if (fInfoShutdown) {
271
                    fInfoShutdown((jack_status_t)value1, message, fInfoShutdownArg);
272
273
                    fInfoShutdown = NULL;
                }
274
                break;
sletz's avatar
sletz committed
275
        }
sletz's avatar
sletz committed
276
277
278
279
280
281
    }

    return res;
}

/*!
sletz's avatar
sletz committed
282
\brief We need to start thread before activating in the server, otherwise the FW driver
sletz's avatar
sletz committed
283
284
285
286
	   connected to the client may not be activated.
*/
int JackClient::Activate()
{
287
    jack_log("JackClient::Activate");
sletz's avatar
sletz committed
288
    if (IsActive())
289
290
        return 0;

291
292
293
    // RT thread is started only when needed...
    if (IsRealTime()) {
        if (StartThread() < 0)
sletz's avatar
sletz committed
294
            return -1;
295
    }
296
297
298
299
300
    
    /*
    Insertion of client in the graph will cause a kGraphOrderCallback notification 
    to be delivered by the server, the client wants to receive it.
    */
sletz's avatar
sletz committed
301
    GetClientControl()->fActive = true;
sletz's avatar
sletz committed
302
303
304
305
    
    // Transport related callback become "active"
    GetClientControl()->fTransportSync = true;
    GetClientControl()->fTransportTimebase = true;
306
307

    int result = -1;
308
309
    GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
    fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
310
    return result;
sletz's avatar
sletz committed
311
312
313
314
315
316
317
}

/*!
\brief Need to stop thread after deactivating in the server.
*/
int JackClient::Deactivate()
{
318
    jack_log("JackClient::Deactivate");
sletz's avatar
sletz committed
319
320
321
322
    if (!IsActive())
        return 0;

    GetClientControl()->fActive = false;
sletz's avatar
sletz committed
323
324
325
326
327
    
    // Transport related callback become "unactive"
    GetClientControl()->fTransportSync = false;
    GetClientControl()->fTransportTimebase = false;
    
328
    // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
sletz's avatar
sletz committed
329
330
    int result = -1;
    fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
331
332
    jack_log("JackClient::Deactivate res = %ld", result);
  
333
    // RT thread is stopped only when needed...
334
    if (IsRealTime()) 
335
        fThread.Kill();
sletz's avatar
sletz committed
336
337
338
339
340
341
342
343
344
345
346
347
348
    return result;
}

//----------------------
// RT thread management
//----------------------

/*!
\brief Called once when the thread starts.
*/
bool JackClient::Init()
{
    if (fInit) {
sletz's avatar
sletz committed
349
        jack_log("JackClient::Init calling client thread init callback");
sletz's avatar
sletz committed
350
        fInit(fInitArg);
sletz's avatar
sletz committed
351
352
353
354
355
356
    }
    return true;
}

int JackClient::StartThread()
{
sletz's avatar
sletz committed
357
    jack_log("JackClient::StartThread : period = %ld computation = %ld constraint = %ld",
358
359
360
             long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
             long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
             long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));
sletz's avatar
sletz committed
361
362

    // Will do "something" on OSX only...
363
    fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
sletz's avatar
sletz committed
364

365
    if (fThread.StartSync() < 0) {
sletz's avatar
sletz committed
366
367
368
369
370
        jack_error("Start thread error");
        return -1;
    }

    if (GetEngineControl()->fRealTime) {
371
        if (fThread.AcquireRealTime(GetEngineControl()->fClientPriority) < 0) {
sletz's avatar
sletz committed
372
373
374
375
376
377
378
379
380
381
            jack_error("AcquireRealTime error");
        }
    }

    return 0;
}

/*!
\brief RT thread.
*/
382

383
384
bool JackClient::Execute()
{
385
    if (!jack_tls_set(JackGlobals::fRealTime, this)) 
386
        jack_error("failed to set thread realtime key");
387
388
389
        
    if (GetEngineControl()->fRealTime) 
        set_threaded_log_function(); 
390
391
392
393
        
    // Execute a dummy cycle to be sure thread has the correct properties
    DummyCycle();
      
sletz's avatar
sletz committed
394
395
396
    if (fThreadFun) {
        fThreadFun(fThreadFunArg);
    } else {
397
        ExecuteThread();
sletz's avatar
sletz committed
398
    }
399
    return false; 
400
401
}

402
void JackClient::DummyCycle()
403
{
404
405
    WaitSync();
    SignalSync();
406
407
408
409
}

inline void JackClient::ExecuteThread()
{
410
411
412
413
    while (true) { 
        CycleWaitAux();
        CycleSignalAux(CallProcessCallback());  
	}
414
415
}

416
inline jack_nframes_t JackClient::CycleWaitAux()
sletz's avatar
sletz committed
417
{
418
419
    if (!WaitSync()) 
        Error();   // Terminates the thread
420
    CallSyncCallbackAux();
sletz's avatar
sletz committed
421
    return GetEngineControl()->fBufferSize;
sletz's avatar
sletz committed
422
423
}

424
inline void JackClient::CycleSignalAux(int status)
sletz's avatar
sletz committed
425
{
sletz's avatar
sletz committed
426
    if (status == 0)
427
        CallTimebaseCallbackAux();
sletz's avatar
sletz committed
428
    SignalSync();
429
430
    if (status != 0) 
        End();     // Terminates the thread
sletz's avatar
sletz committed
431
432
}

433
434
435
436
437
438
439
440
441
442
jack_nframes_t JackClient::CycleWait()
{
    return CycleWaitAux();
}

void JackClient::CycleSignal(int status)
{
    CycleSignalAux(status);
}

443
444
inline int JackClient::CallProcessCallback()
{
sletz's avatar
sletz committed
445
    return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0;
446
447
448
449
}

inline bool JackClient::WaitSync()
{
sletz's avatar
sletz committed
450
    // Suspend itself: wait on the input synchro
451
    if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
sletz's avatar
sletz committed
452
453
454
455
456
        jack_error("SuspendRefNum error");
        return false;
    } else {
        return true;
    }
457
458
459
460
}

inline void JackClient::SignalSync()
{
sletz's avatar
sletz committed
461
    // Resume: signal output clients connected to the running client
462
463
464
465
    if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
        jack_error("ResumeRefNum error");
    }
}
sletz's avatar
sletz committed
466

467
inline void JackClient::End()
sletz's avatar
Cleanup    
sletz committed
468
{
sletz's avatar
sletz committed
469
    jack_log("JackClient::Execute end name = %s", GetClientControl()->fName);
sletz's avatar
sletz committed
470
471
    // Hum... not sure about this, the following "close" code is called in the RT thread...
    int result;
472
    fThread.DropSelfRealTime();
sletz's avatar
sletz committed
473
474
    GetClientControl()->fActive = false;
    fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
475
    fThread.Terminate();
sletz's avatar
Cleanup    
sletz committed
476
477
}

478
inline void JackClient::Error()
sletz's avatar
Cleanup    
sletz committed
479
{
sletz's avatar
sletz committed
480
481
482
    jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
    // Hum... not sure about this, the following "close" code is called in the RT thread...
    int result;
483
    fThread.DropSelfRealTime();
sletz's avatar
sletz committed
484
485
    GetClientControl()->fActive = false;
    fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
sletz's avatar
Cleanup    
sletz committed
486
    ShutDown();
487
    fThread.Terminate();
sletz's avatar
Cleanup    
sletz committed
488
489
}

sletz's avatar
sletz committed
490
491
492
493
494
495
//-----------------
// Port management
//-----------------

int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
{
496
    // Check if port name is empty
sletz's avatar
sletz committed
497
498
    string port_name_str = string(port_name);
    if (port_name_str.size() == 0) {
499
        jack_error("port_name is empty");
sletz's avatar
sletz committed
500
501
502
        return 0; // Means failure here...
    }

503
    // Check port name length
sletz's avatar
sletz committed
504
505
506
    string name = string(GetClientControl()->fName) + string(":") + port_name_str;
    if (name.size() >= JACK_PORT_NAME_SIZE) {
        jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
507
                   "Please use %lu characters or less",
sletz's avatar
sletz committed
508
509
510
511
512
513
514
                   GetClientControl()->fName,
                   port_name,
                   JACK_PORT_NAME_SIZE - 1);
        return 0; // Means failure here...
    }

    int result = -1;
515
    jack_port_id_t port_index = NO_PORT;
sletz's avatar
sletz committed
516
    fChannel->PortRegister(GetClientControl()->fRefNum, name.c_str(), port_type, flags, buffer_size, &port_index, &result);
517
  
sletz's avatar
sletz committed
518
    if (result == 0) {
519
        jack_log("JackClient::PortRegister ref = %ld name = %s type = %s port_index = %ld", GetClientControl()->fRefNum, name.c_str(), port_type, port_index);
sletz's avatar
sletz committed
520
521
522
523
524
525
526
527
528
        fPortList.push_back(port_index);
        return port_index;
    } else {
        return 0;
    }
}

int JackClient::PortUnRegister(jack_port_id_t port_index)
{
sletz's avatar
sletz committed
529
    jack_log("JackClient::PortUnRegister port_index = %ld", port_index);
sletz's avatar
sletz committed
530
    list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
sletz's avatar
sletz committed
531
532
533
534
535
536
537
538
539
540
541
542
543
544

    if (it != fPortList.end()) {
        fPortList.erase(it);
        int result = -1;
        fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
        return result;
    } else {
        jack_error("unregistering a port %ld that is not own by the client", port_index);
        return -1;
    }
}

int JackClient::PortConnect(const char* src, const char* dst)
{
sletz's avatar
sletz committed
545
    jack_log("JackClient::Connect src = %s dst = %s", src, dst);
sletz's avatar
sletz committed
546
547
548
549
550
551
552
    int result = -1;
    fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
    return result;
}

int JackClient::PortDisconnect(const char* src, const char* dst)
{
sletz's avatar
sletz committed
553
    jack_log("JackClient::Disconnect src = %s dst = %s", src, dst);
sletz's avatar
sletz committed
554
555
556
557
558
559
560
    int result = -1;
    fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
    return result;
}

int JackClient::PortDisconnect(jack_port_id_t src)
{
sletz's avatar
sletz committed
561
    jack_log("JackClient::PortDisconnect src = %ld", src);
sletz's avatar
sletz committed
562
563
564
565
566
567
568
569
570
571
572
    int result = -1;
    fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
    return result;
}

int JackClient::PortIsMine(jack_port_id_t port_index)
{
    JackPort* port = GetGraphManager()->GetPort(port_index);
    return GetClientControl()->fRefNum == port->GetRefNum();
}

573
574
575
576
577
578
579
int JackClient::PortRename(jack_port_id_t port_index, const char* name)
{
    int result = -1;
    fChannel->PortRename(GetClientControl()->fRefNum, port_index, name, &result);
    return result;
}

sletz's avatar
sletz committed
580
581
582
583
//--------------------
// Context management
//--------------------

584
int JackClient::SetBufferSize(jack_nframes_t buffer_size)
sletz's avatar
sletz committed
585
586
{
    int result = -1;
587
    fChannel->SetBufferSize(buffer_size, &result);
sletz's avatar
sletz committed
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
    return result;
}

int JackClient::SetFreeWheel(int onoff)
{
    int result = -1;
    fChannel->SetFreewheel(onoff, &result);
    return result;
}

/*
ShutDown is called:
- from the RT thread when Execute method fails
- possibly from a "closed" notification channel
(Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown))
*/

void JackClient::ShutDown()
{
sletz's avatar
sletz committed
607
    jack_log("ShutDown");
608
    JackGlobals::fServerRunning = false;
609
    
610
    if (fInfoShutdown) {
611
        fInfoShutdown(JackFailure, "JACK server has been closed", fInfoShutdownArg);
612
613
        fInfoShutdown = NULL;
    } else if (fShutdown) {
sletz's avatar
sletz committed
614
615
616
617
618
619
620
621
622
        fShutdown(fShutdownArg);
        fShutdown = NULL;
    }
}

//----------------------
// Transport management
//----------------------

623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
inline int JackClient::ActivateAux()
{
    // If activated without RT thread...
    if (IsActive() && fThread.GetStatus() != JackThread::kRunning) {
    
        jack_log("ActivateAux");
    
        // RT thread is started
        if (StartThread() < 0)
            return -1;
        
        int result = -1;
        GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
        fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
        return result;
        
    } else {
        return 0;
    }
}

sletz's avatar
sletz committed
644
645
646
647
648
int JackClient::ReleaseTimebase()
{
    int result = -1;
    fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
    if (result == 0) {
sletz's avatar
sletz committed
649
        GetClientControl()->fTransportTimebase = false;
sletz's avatar
sletz committed
650
651
652
653
654
655
656
657
658
        fTimebase = NULL;
        fTimebaseArg = NULL;
    }
    return result;
}

/* Call the server if the client is active, otherwise keeps the arguments */
int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
{
sletz's avatar
sletz committed
659
    GetClientControl()->fTransportSync = (fSync != NULL);
sletz's avatar
sletz committed
660
    fSyncArg = arg;
sletz's avatar
sletz committed
661
    fSync = sync_callback;
662
    return ActivateAux();
sletz's avatar
sletz committed
663
664
}

sletz's avatar
sletz committed
665
666
667
668
int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
{
    int result = -1;
    fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
669
    
sletz's avatar
sletz committed
670
    if (result == 0) {
sletz's avatar
sletz committed
671
        GetClientControl()->fTransportTimebase = true;
sletz's avatar
sletz committed
672
673
        fTimebase = timebase_callback;
        fTimebaseArg = arg;
674
        return ActivateAux();
sletz's avatar
sletz committed
675
676
677
    } else {
        fTimebase = NULL;
        fTimebaseArg = NULL;
678
        return -1;
sletz's avatar
sletz committed
679
    }
680
681
682
683
684
685
}

int JackClient::SetSyncTimeout(jack_time_t timeout)
{
    GetEngineControl()->fTransport.SetSyncTimeout(timeout);
    return 0;
sletz's avatar
sletz committed
686
}
sletz's avatar
sletz committed
687
688
689

// Must be RT safe

690
void JackClient::TransportLocate(jack_nframes_t frame)
sletz's avatar
sletz committed
691
692
693
694
{
    jack_position_t pos;
    pos.frame = frame;
    pos.valid = (jack_position_bits_t)0;
sletz's avatar
sletz committed
695
    jack_log("TransportLocate pos = %ld", pos.frame);
696
    GetEngineControl()->fTransport.RequestNewPos(&pos);
sletz's avatar
sletz committed
697
698
699
700
701
}

int JackClient::TransportReposition(jack_position_t* pos)
{
    jack_position_t tmp = *pos;
sletz's avatar
sletz committed
702
    jack_log("TransportReposition pos = %ld", pos->frame);
703
704
705
706
707
708
    if (tmp.valid & ~JACK_POSITION_MASK) {
        return EINVAL;
    } else {
        GetEngineControl()->fTransport.RequestNewPos(pos);
        return 0;
    }
sletz's avatar
sletz committed
709
710
711
712
}

jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
{
713
    return GetEngineControl()->fTransport.Query(pos);
sletz's avatar
sletz committed
714
715
716
717
}

jack_nframes_t JackClient::GetCurrentTransportFrame()
{
718
    return GetEngineControl()->fTransport.GetCurrentFrame();
sletz's avatar
sletz committed
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
}

// Must be RT safe: directly write in the transport shared mem
void JackClient::TransportStart()
{
    GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
}

// Must be RT safe: directly write in the transport shared mem
void JackClient::TransportStop()
{
    GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
}

// Never called concurently with the server
sletz's avatar
sletz committed
734
// TODO check concurrency with SetSyncCallback
735

sletz's avatar
sletz committed
736
void JackClient::CallSyncCallback()
737
738
739
740
741
{
    CallSyncCallbackAux();
}

inline void JackClient::CallSyncCallbackAux()
sletz's avatar
sletz committed
742
{
sletz's avatar
sletz committed
743
744
745
746
747
748
749
750
    if (GetClientControl()->fTransportSync) {
    
        JackTransportEngine& transport = GetEngineControl()->fTransport;
        jack_position_t* cur_pos = transport.ReadCurrentState();
        jack_transport_state_t transport_state = transport.GetState();
    
        if (fSync != NULL) {
            if (fSync(transport_state, cur_pos, fSyncArg)) {
sletz's avatar
sletz committed
751
                GetClientControl()->fTransportState = JackTransportRolling;
sletz's avatar
sletz committed
752
                GetClientControl()->fTransportSync = false;
sletz's avatar
sletz committed
753
            }
sletz's avatar
sletz committed
754
755
756
757
        } else {
            GetClientControl()->fTransportState = JackTransportRolling;
            GetClientControl()->fTransportSync = false;
        }
sletz's avatar
sletz committed
758
759
760
761
    }
}

void JackClient::CallTimebaseCallback()
762
763
764
765
766
{
    CallTimebaseCallbackAux();
}

inline void JackClient::CallTimebaseCallbackAux()
sletz's avatar
sletz committed
767
768
{
    JackTransportEngine& transport = GetEngineControl()->fTransport;
769
770
    int master;
    bool unused;
sletz's avatar
sletz committed
771
    
772
773
774
    transport.GetTimebaseMaster(master, unused);
    
    if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase...
sletz's avatar
sletz committed
775
    
sletz's avatar
sletz committed
776
777
        jack_transport_state_t transport_state = transport.GetState();
        jack_position_t* cur_pos = transport.WriteNextStateStart(1);
sletz's avatar
sletz committed
778
        
sletz's avatar
sletz committed
779
        if (GetClientControl()->fTransportTimebase) {
sletz's avatar
sletz committed
780
            fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg); 
sletz's avatar
sletz committed
781
            GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true 
sletz's avatar
sletz committed
782
        } else if (transport_state == JackTransportRolling) {
sletz's avatar
sletz committed
783
            fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
sletz's avatar
sletz committed
784
        } 
sletz's avatar
sletz committed
785
        
sletz's avatar
sletz committed
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
        transport.WriteNextStateStop(1);
    }
}

//---------------------
// Callback management
//---------------------

void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
    } else {
        fShutdownArg = arg;
        fShutdown = callback;
    }
}
803
804
805
806
807
808
    
void JackClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
    } else {
809
        GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
810
811
812
813
        fInfoShutdownArg = arg;
        fInfoShutdown = callback;
    }
}
sletz's avatar
sletz committed
814
815
816
817
818
819

int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
820
    } else if (fThreadFun) {
821
822
823
        jack_error ("A thread callback has already been setup, both models cannot be used at the same time!");
        return -1;
    } else {
sletz's avatar
sletz committed
824
825
826
827
828
829
830
831
832
833
834
835
        fProcessArg = arg;
        fProcess = callback;
        return 0;
    }
}

int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
    } else {
sletz's avatar
sletz committed
836
        GetClientControl()->fCallback[kXRunCallback] = (callback != NULL);
sletz's avatar
sletz committed
837
838
839
840
841
842
843
844
845
846
847
848
849
850
        fXrunArg = arg;
        fXrun = callback;
        return 0;
    }
}

int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
    } else {
        fInitArg = arg;
        fInit = callback;
851
852
        /* make sure that the message buffer thread is initialized too */
        JackMessageBuffer::fInstance->SetInitCallback(callback, arg);
sletz's avatar
sletz committed
853
854
855
856
857
858
        return 0;
    }
}

int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
{
sletz's avatar
sletz committed
859
    jack_log("SetGraphOrderCallback ");
sletz's avatar
sletz committed
860
861
862
863
864

    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
    } else {
sletz's avatar
sletz committed
865
        GetClientControl()->fCallback[kGraphOrderCallback] = (callback != NULL);
sletz's avatar
sletz committed
866
867
868
869
870
871
872
873
874
875
876
877
        fGraphOrder = callback;
        fGraphOrderArg = arg;
        return 0;
    }
}

int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
    } else {
sletz's avatar
sletz committed
878
        GetClientControl()->fCallback[kBufferSizeCallback] = (callback != NULL);
sletz's avatar
sletz committed
879
880
881
882
883
884
        fBufferSizeArg = arg;
        fBufferSize = callback;
        return 0;
    }
}

885
886
887
888
889
890
891
892
893
int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
    } else {
        GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL);
        fSampleRateArg = arg;
        fSampleRate = callback;
894
895
896
        // Now invoke it 
        if (callback) 
            callback(GetEngineControl()->fSampleRate, arg);
897
898
899
900
        return 0;
    }
}

901
902
903
904
905
906
int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
    } else {
sletz's avatar
sletz committed
907
908
        // kAddClient and kRemoveClient notifications must be delivered by the server in any case
        fClientRegistrationArg = arg;
909
910
911
912
913
        fClientRegistration = callback;
        return 0;
    }
}

sletz's avatar
sletz committed
914
915
916
917
918
919
int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
    } else {
sletz's avatar
sletz committed
920
921
        GetClientControl()->fCallback[kStartFreewheelCallback] = (callback != NULL);
        GetClientControl()->fCallback[kStopFreewheelCallback] = (callback != NULL);
sletz's avatar
sletz committed
922
923
924
925
926
927
928
929
930
931
932
933
        fFreewheelArg = arg;
        fFreewheel = callback;
        return 0;
    }
}

int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
    } else {
sletz's avatar
sletz committed
934
935
        GetClientControl()->fCallback[kPortRegistrationOnCallback] = (callback != NULL);
        GetClientControl()->fCallback[kPortRegistrationOffCallback] = (callback != NULL);
sletz's avatar
sletz committed
936
937
938
939
940
941
        fPortRegistrationArg = arg;
        fPortRegistration = callback;
        return 0;
    }
}

sletz's avatar
sletz committed
942
943
944
945
946
947
int JackClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
    } else {
sletz's avatar
sletz committed
948
949
        GetClientControl()->fCallback[kPortConnectCallback] = (callback != NULL);
        GetClientControl()->fCallback[kPortDisconnectCallback] = (callback != NULL);
sletz's avatar
sletz committed
950
951
952
953
954
955
        fPortConnectArg = arg;
        fPortConnect = callback;
        return 0;
    }
}

956
957
958
959
960
961
962
963
964
965
966
967
968
int JackClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
    } else {
        GetClientControl()->fCallback[kPortRenameCallback] = (callback != NULL);
        fPortRenameArg = arg;
        fPortRename = callback;
        return 0;
    }
}

969
970
971
972
973
974
int JackClient::SetProcessThread(JackThreadCallback fun, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
    } else if (fProcess) {
975
976
977
        jack_error ("A process callback has already been setup, both models cannot be used at the same time!");
        return -1;
    } else {
978
979
980
981
982
983
        fThreadFun = fun;
        fThreadFunArg = arg;
        return 0;
    }
}

984
985
986
987
988
989
//------------------
// Internal clients
//------------------

char* JackClient::GetInternalClientName(int ref)
{
990
    char name_res[JACK_CLIENT_NAME_SIZE + 1];
sletz's avatar
sletz committed
991
992
    int result = -1;
    fChannel->GetInternalClientName(GetClientControl()->fRefNum, ref, name_res, &result);
993
    return (result < 0) ? NULL : strdup(name_res);
994
995
996
997
}

int JackClient::InternalClientHandle(const char* client_name, jack_status_t* status)
{
sletz's avatar
sletz committed
998
999
1000
    int int_ref, result = -1;
    fChannel->InternalClientHandle(GetClientControl()->fRefNum, client_name, (int*)status, &int_ref, &result);
    return int_ref;