JackClient.cpp 38.1 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
You should have received a copy of the GNU Lesser General Public License
sletz's avatar
sletz committed
16
along with this program; if not, write to the Free Software
sletz's avatar
sletz committed
17
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
sletz's avatar
sletz committed
18
19
20

*/

sletz's avatar
sletz committed
21
#include "JackSystemDeps.h"
sletz's avatar
sletz committed
22
23
24
25
26
#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

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

using namespace std;

namespace Jack
{

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

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

46
JackClient::JackClient(JackSynchro* table):fThread(this)
sletz's avatar
sletz committed
47
48
49
50
51
52
{
    fSynchroTable = table;
    fProcess = NULL;
    fGraphOrder = NULL;
    fXrun = NULL;
    fShutdown = NULL;
sletz's avatar
sletz committed
53
    fInfoShutdown = NULL;
sletz's avatar
sletz committed
54
55
    fInit = NULL;
    fBufferSize = NULL;
sletz's avatar
sletz committed
56
    fClientRegistration = NULL;
sletz's avatar
sletz committed
57
58
    fFreewheel = NULL;
    fPortRegistration = NULL;
sletz's avatar
sletz committed
59
    fPortConnect = NULL;
60
    fPortRename = NULL;
61
    fTimebase = NULL;
sletz's avatar
sletz committed
62
    fSync = NULL;
63
    fThreadFun = NULL;
sletz's avatar
sletz committed
64
65
66
    fSession = NULL;
    fLatency = NULL;

sletz's avatar
sletz committed
67
68
69
70
    fProcessArg = NULL;
    fGraphOrderArg = NULL;
    fXrunArg = NULL;
    fShutdownArg = NULL;
sletz's avatar
sletz committed
71
    fInfoShutdownArg = NULL;
sletz's avatar
sletz committed
72
73
74
    fInitArg = NULL;
    fBufferSizeArg = NULL;
    fFreewheelArg = NULL;
sletz's avatar
sletz committed
75
    fClientRegistrationArg = NULL;
sletz's avatar
sletz committed
76
    fPortRegistrationArg = NULL;
sletz's avatar
sletz committed
77
    fPortConnectArg = NULL;
78
    fPortRenameArg = NULL;
sletz's avatar
sletz committed
79
    fSyncArg = NULL;
80
    fTimebaseArg = NULL;
sletz's avatar
sletz committed
81
    fThreadFunArg = NULL;
sletz's avatar
sletz committed
82
83
    fSessionArg = NULL;
    fLatencyArg = NULL;
sletz's avatar
sletz committed
84
85
86
}

JackClient::~JackClient()
87
{}
sletz's avatar
sletz committed
88
89
90

int JackClient::Close()
{
sletz's avatar
sletz committed
91
    jack_log("JackClient::Close ref = %ld", GetClientControl()->fRefNum);
92
    int result = 0;
sletz's avatar
sletz committed
93

sletz's avatar
sletz committed
94
    Deactivate();
95
    fChannel->Stop();  // Channels is stopped first to avoid receiving notifications while closing
sletz's avatar
sletz committed
96

97
98
    // Request close only if server is still running
    if (JackGlobals::fServerRunning) {
99
100
        fChannel->ClientClose(GetClientControl()->fRefNum, &result);
    } else {
sletz's avatar
sletz committed
101
        jack_log("JackClient::Close server is shutdown");
102
    }
sletz's avatar
sletz committed
103

sletz's avatar
sletz committed
104
    fChannel->Close();
105
    fSynchroTable[GetClientControl()->fRefNum].Disconnect();
106
    JackGlobals::fClientTable[GetClientControl()->fRefNum] = NULL;
sletz's avatar
sletz committed
107
108
109
110
111
112
113
114
    return result;
}

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

sletz's avatar
sletz committed
115
jack_native_thread_t JackClient::GetThreadID()
sletz's avatar
sletz committed
116
{
117
    return fThread.GetThreadID();
sletz's avatar
sletz committed
118
119
120
}

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

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

sletz's avatar
sletz committed
143
int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
sletz's avatar
sletz committed
144
{
sletz's avatar
sletz committed
145
    return 0;
sletz's avatar
sletz committed
146
147
}

sletz's avatar
sletz committed
148
int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
sletz's avatar
sletz committed
149
150
151
152
153
154
{
    int res = 0;

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

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

        case kRemoveClient:
sletz's avatar
sletz committed
160
            res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
sletz's avatar
sletz committed
161
162
163
            break;

        case kActivateClient:
sletz's avatar
sletz committed
164
            jack_log("JackClient::kActivateClient name = %s ref = %ld ", name, refnum);
sletz's avatar
sletz committed
165
            InitAux();
sletz's avatar
sletz committed
166
167
            break;
    }
sletz's avatar
sletz committed
168
169
170
171
172
173
174
175

    /*
    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
176
177

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

            case kRemoveClient:
sletz's avatar
sletz committed
185
                jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name);
sletz's avatar
sletz committed
186
                if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
sletz's avatar
sletz committed
187
                    fClientRegistration(name, 0, fClientRegistrationArg);
sletz's avatar
sletz committed
188
                }
sletz's avatar
sletz committed
189
                break;
sletz's avatar
sletz committed
190

191
            case kBufferSizeCallback:
sletz's avatar
sletz committed
192
                jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1);
sletz's avatar
sletz committed
193
                if (fBufferSize) {
sletz's avatar
sletz committed
194
                    res = fBufferSize(value1, fBufferSizeArg);
sletz's avatar
sletz committed
195
                }
sletz's avatar
sletz committed
196
                break;
sletz's avatar
sletz committed
197

198
199
            case kSampleRateCallback:
                jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1);
sletz's avatar
sletz committed
200
                if (fSampleRate) {
201
                    res = fSampleRate(value1, fSampleRateArg);
sletz's avatar
sletz committed
202
                }
203
                break;
sletz's avatar
sletz committed
204

205
            case kGraphOrderCallback:
sletz's avatar
sletz committed
206
                jack_log("JackClient::kGraphOrderCallback");
sletz's avatar
sletz committed
207
                if (fGraphOrder) {
sletz's avatar
sletz committed
208
                    res = fGraphOrder(fGraphOrderArg);
sletz's avatar
sletz committed
209
                }
sletz's avatar
sletz committed
210
211
                break;

212
            case kStartFreewheelCallback:
sletz's avatar
sletz committed
213
                jack_log("JackClient::kStartFreewheel");
sletz's avatar
sletz committed
214
                SetupDriverSync(true);
sletz's avatar
sletz committed
215
216
                fThread.DropRealTime();     // Always done (JACK server in RT mode or not...)
                if (fFreewheel) {
sletz's avatar
sletz committed
217
                    fFreewheel(1, fFreewheelArg);
sletz's avatar
sletz committed
218
                }
sletz's avatar
sletz committed
219
220
                break;

221
            case kStopFreewheelCallback:
sletz's avatar
sletz committed
222
                jack_log("JackClient::kStopFreewheel");
sletz's avatar
sletz committed
223
                SetupDriverSync(false);
sletz's avatar
sletz committed
224
                if (fFreewheel) {
sletz's avatar
sletz committed
225
                    fFreewheel(0, fFreewheelArg);
sletz's avatar
sletz committed
226
227
                }
                if (GetEngineControl()->fRealTime) {
sletz's avatar
sletz committed
228
229
230
                    if (fThread.AcquireRealTime() < 0) {
                        jack_error("JackClient::AcquireRealTime error");
                    }
sletz's avatar
sletz committed
231
                }
sletz's avatar
sletz committed
232
233
                break;

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

241
            case kPortRegistrationOffCallback:
sletz's avatar
sletz committed
242
                jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1);
sletz's avatar
sletz committed
243
                if (fPortRegistration) {
sletz's avatar
sletz committed
244
                    fPortRegistration(value1, 0, fPortRegistrationArg);
sletz's avatar
sletz committed
245
                }
sletz's avatar
sletz committed
246
                break;
sletz's avatar
sletz committed
247
248

            case kPortConnectCallback:
sletz's avatar
sletz committed
249
                jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2);
sletz's avatar
sletz committed
250
                if (fPortConnect) {
sletz's avatar
sletz committed
251
                    fPortConnect(value1, value2, 1, fPortConnectArg);
sletz's avatar
sletz committed
252
                }
sletz's avatar
sletz committed
253
                break;
sletz's avatar
sletz committed
254
255

            case kPortDisconnectCallback:
sletz's avatar
sletz committed
256
                jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2);
sletz's avatar
sletz committed
257
                if (fPortConnect) {
sletz's avatar
sletz committed
258
                    fPortConnect(value1, value2, 0, fPortConnectArg);
sletz's avatar
sletz committed
259
                }
sletz's avatar
sletz committed
260
                break;
sletz's avatar
sletz committed
261

262
             case kPortRenameCallback:
sletz's avatar
sletz committed
263
                jack_log("JackClient::kPortRenameCallback port = %ld", value1);
sletz's avatar
sletz committed
264
                if (fPortRename) {
sletz's avatar
sletz committed
265
                    fPortRename(value1, message, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg);
sletz's avatar
sletz committed
266
                }
267
                break;
sletz's avatar
sletz committed
268

269
            case kXRunCallback:
sletz's avatar
sletz committed
270
                jack_log("JackClient::kXRunCallback");
sletz's avatar
sletz committed
271
                if (fXrun) {
sletz's avatar
sletz committed
272
                    res = fXrun(fXrunArg);
sletz's avatar
sletz committed
273
                }
sletz's avatar
sletz committed
274
                break;
sletz's avatar
sletz committed
275

sletz's avatar
sletz committed
276
277
278
            case kShutDownCallback:
                jack_log("JackClient::kShutDownCallback");
                if (fInfoShutdown) {
sletz's avatar
sletz committed
279
                    fInfoShutdown((jack_status_t)value1, message, fInfoShutdownArg);
sletz's avatar
sletz committed
280
281
282
                    fInfoShutdown = NULL;
                }
                break;
sletz's avatar
sletz committed
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299

            case kSessionCallback:
                jack_log("JackClient::kSessionCallback");
                if (fSession) {
                    jack_session_event_t *event = (jack_session_event_t *) malloc( sizeof(jack_session_event_t) );
                    char uuid_buf[JACK_UUID_SIZE];
                    event->type = (jack_session_event_type_t) value1;
                    event->session_dir = strdup( message );
                    event->command_line = NULL;
                    event->flags = (jack_session_flags_t) 0;
                    snprintf( uuid_buf, sizeof(uuid_buf), "%d", GetClientControl()->fSessionID );
                    event->client_uuid = strdup( uuid_buf );
                    fImmediateSessionReply = false;
                    fSession(event, fSessionArg);
                    res = (fImmediateSessionReply) ? 1 : 2;
                }
                break;
sletz's avatar
sletz committed
300
301
302
303

            case kLatencyCallback:
                res = HandleLatencyCallback(value1);
                break;
sletz's avatar
sletz committed
304
        }
sletz's avatar
sletz committed
305
306
307
308
309
    }

    return res;
}

sletz's avatar
sletz committed
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
int JackClient::HandleLatencyCallback(int status)
{
    jack_latency_callback_mode_t mode = (status == 0) ? JackCaptureLatency : JackPlaybackLatency;
	jack_latency_range_t latency = { UINT32_MAX, 0 };

	/* first setup all latency values of the ports.
	 * this is based on the connections of the ports.
	 */
    list<jack_port_id_t>::iterator it;

	for (it = fPortList.begin(); it != fPortList.end(); it++) {
	   JackPort* port = GetGraphManager()->GetPort(*it);
        if ((port->GetFlags() & JackPortIsOutput) && (mode == JackPlaybackLatency)) {
            GetGraphManager()->RecalculateLatency(*it, mode);
		}
		if ((port->GetFlags() & JackPortIsInput) && (mode == JackCaptureLatency)) {
            GetGraphManager()->RecalculateLatency(*it, mode);
		}
	}

	if (!fLatency) {
		/*
		 * default action is to assume all ports depend on each other.
		 * then always take the maximum latency.
		 */

		if (mode == JackPlaybackLatency) {
			/* iterate over all OutputPorts, to find maximum playback latency
			 */
			for (it = fPortList.begin(); it != fPortList.end(); it++) {
                JackPort* port = GetGraphManager()->GetPort(*it);
                if (port->GetFlags() & JackPortIsOutput) {
					jack_latency_range_t other_latency;
					port->GetLatencyRange(mode, &other_latency);
					if (other_latency.max > latency.max)
						latency.max = other_latency.max;
					if (other_latency.min < latency.min)
						latency.min = other_latency.min;
				}
			}

			if (latency.min == UINT32_MAX)
				latency.min = 0;

			/* now set the found latency on all input ports
			 */
			for (it = fPortList.begin(); it != fPortList.end(); it++) {
                JackPort* port = GetGraphManager()->GetPort(*it);
                if (port->GetFlags() & JackPortIsInput) {
					port->SetLatencyRange(mode, &latency);
				}
			}
		}
		if (mode == JackCaptureLatency) {
			/* iterate over all InputPorts, to find maximum playback latency
			 */
			for (it = fPortList.begin(); it != fPortList.end(); it++) {
                JackPort* port = GetGraphManager()->GetPort(*it);
				if (port->GetFlags() & JackPortIsInput) {
					jack_latency_range_t other_latency;
                    port->GetLatencyRange(mode, &other_latency);
					if (other_latency.max > latency.max)
						latency.max = other_latency.max;
					if (other_latency.min < latency.min)
						latency.min = other_latency.min;
				}
			}

			if (latency.min == UINT32_MAX)
				latency.min = 0;

			/* now set the found latency on all output ports
			 */
			for (it = fPortList.begin(); it != fPortList.end(); it++) {
                JackPort* port = GetGraphManager()->GetPort(*it);
                if (port->GetFlags() & JackPortIsOutput) {
					port->SetLatencyRange(mode, &latency);
				}
			}
		}
		return 0;
	}

	/* we have a latency callback setup by the client,
	 * lets use it...
	 */
	fLatency(mode, fLatencyArg);
	return 0;
}

sletz's avatar
sletz committed
400
/*!
sletz's avatar
sletz committed
401
\brief We need to start thread before activating in the server, otherwise the FW driver
sletz's avatar
sletz committed
402
connected to the client may not be activated.
sletz's avatar
sletz committed
403
404
405
*/
int JackClient::Activate()
{
406
    jack_log("JackClient::Activate");
sletz's avatar
sletz committed
407
    if (IsActive())
408
409
        return 0;

410
411
412
    // RT thread is started only when needed...
    if (IsRealTime()) {
        if (StartThread() < 0)
sletz's avatar
sletz committed
413
            return -1;
414
    }
sletz's avatar
sletz committed
415

416
    /*
sletz's avatar
sletz committed
417
    Insertion of client in the graph will cause a kGraphOrderCallback notification
418
419
    to be delivered by the server, the client wants to receive it.
    */
sletz's avatar
sletz committed
420
    GetClientControl()->fActive = true;
sletz's avatar
sletz committed
421

sletz's avatar
sletz committed
422
423
424
    // Transport related callback become "active"
    GetClientControl()->fTransportSync = true;
    GetClientControl()->fTransportTimebase = true;
425
426

    int result = -1;
427
428
    GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
    fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
429
    return result;
sletz's avatar
sletz committed
430
431
432
433
434
435
436
}

/*!
\brief Need to stop thread after deactivating in the server.
*/
int JackClient::Deactivate()
{
437
    jack_log("JackClient::Deactivate");
sletz's avatar
sletz committed
438
439
440
441
    if (!IsActive())
        return 0;

    GetClientControl()->fActive = false;
sletz's avatar
sletz committed
442

sletz's avatar
sletz committed
443
444
445
    // Transport related callback become "unactive"
    GetClientControl()->fTransportSync = false;
    GetClientControl()->fTransportTimebase = false;
sletz's avatar
sletz committed
446

447
    // 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
448
449
    int result = -1;
    fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
450
    jack_log("JackClient::Deactivate res = %ld", result);
sletz's avatar
sletz committed
451

452
    // RT thread is stopped only when needed...
sletz's avatar
sletz committed
453
    if (IsRealTime())
454
        fThread.Kill();
sletz's avatar
sletz committed
455
456
457
458
459
460
461
    return result;
}

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

sletz's avatar
sletz committed
462
463
464
465
466
467
468
469
void JackClient::InitAux()
{
    if (fInit) {
        jack_log("JackClient::Init calling client thread init callback");
        fInit(fInitArg);
    }
}

sletz's avatar
sletz committed
470
471
472
473
474
/*!
\brief Called once when the thread starts.
*/
bool JackClient::Init()
{
sletz's avatar
sletz committed
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
    /*
        Execute buffer_size callback.

        Since StartThread uses fThread.StartSync, we are sure that buffer_size callback
        is executed before StartThread returns (and then IsActive will be true).
        So no RT callback can be called at the same time.
    */
    jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", GetEngineControl()->fBufferSize);
    if (fBufferSize) {
        fBufferSize(GetEngineControl()->fBufferSize, fBufferSizeArg);
    }

    // Init callback
    InitAux();

    // Setup context
    if (!jack_tls_set(JackGlobals::fRealTime, this))
        jack_error("failed to set thread realtime key");

    if (GetEngineControl()->fRealTime)
        set_threaded_log_function();

    // Setup RT
    if (GetEngineControl()->fRealTime) {
sletz's avatar
sletz committed
499
500
        if (fThread.AcquireSelfRealTime(GetEngineControl()->fClientPriority) < 0) {
            jack_error("JackClient::AcquireSelfRealTime error");
sletz's avatar
sletz committed
501
        }
sletz's avatar
sletz committed
502
    }
sletz's avatar
sletz committed
503

sletz's avatar
sletz committed
504
505
506
507
508
    return true;
}

int JackClient::StartThread()
{
sletz's avatar
sletz committed
509
    jack_log("JackClient::StartThread : period = %ld computation = %ld constraint = %ld",
510
511
512
             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
513
514

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

sletz's avatar
sletz committed
517
    if (fThread.StartSync() < 0) {
sletz's avatar
sletz committed
518
519
520
521
522
523
524
525
526
527
        jack_error("Start thread error");
        return -1;
    }

    return 0;
}

/*!
\brief RT thread.
*/
sletz's avatar
sletz committed
528

529
530
bool JackClient::Execute()
{
sletz's avatar
sletz committed
531
532
    // Execute a dummy cycle to be sure thread has the correct properties
    DummyCycle();
sletz's avatar
sletz committed
533

sletz's avatar
sletz committed
534
535
536
    if (fThreadFun) {
        fThreadFun(fThreadFunArg);
    } else {
sletz's avatar
sletz committed
537
        ExecuteThread();
sletz's avatar
sletz committed
538
    }
sletz's avatar
sletz committed
539
    return false;
540
541
}

sletz's avatar
sletz committed
542
void JackClient::DummyCycle()
543
{
sletz's avatar
sletz committed
544
545
    WaitSync();
    SignalSync();
546
547
548
549
}

inline void JackClient::ExecuteThread()
{
sletz's avatar
sletz committed
550
    while (true) {
sletz's avatar
sletz committed
551
        CycleWaitAux();
sletz's avatar
sletz committed
552
553
        CycleSignalAux(CallProcessCallback());
    }
554
555
}

sletz's avatar
sletz committed
556
inline jack_nframes_t JackClient::CycleWaitAux()
sletz's avatar
sletz committed
557
{
sletz's avatar
sletz committed
558
    if (!WaitSync())
559
        Error();   // Terminates the thread
sletz's avatar
sletz committed
560
    CallSyncCallbackAux();
sletz's avatar
sletz committed
561
    return GetEngineControl()->fBufferSize;
sletz's avatar
sletz committed
562
563
}

sletz's avatar
sletz committed
564
inline void JackClient::CycleSignalAux(int status)
sletz's avatar
sletz committed
565
{
sletz's avatar
sletz committed
566
    if (status == 0)
sletz's avatar
sletz committed
567
        CallTimebaseCallbackAux();
sletz's avatar
sletz committed
568
    SignalSync();
sletz's avatar
sletz committed
569
    if (status != 0)
570
        End();     // Terminates the thread
sletz's avatar
sletz committed
571
572
}

sletz's avatar
sletz committed
573
574
575
576
577
578
579
580
581
582
jack_nframes_t JackClient::CycleWait()
{
    return CycleWaitAux();
}

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

583
584
inline int JackClient::CallProcessCallback()
{
sletz's avatar
sletz committed
585
    return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0;
586
587
588
589
}

inline bool JackClient::WaitSync()
{
sletz's avatar
sletz committed
590
    // Suspend itself: wait on the input synchro
591
    if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
sletz's avatar
sletz committed
592
593
594
595
596
        jack_error("SuspendRefNum error");
        return false;
    } else {
        return true;
    }
597
598
599
600
}

inline void JackClient::SignalSync()
{
sletz's avatar
sletz committed
601
    // Resume: signal output clients connected to the running client
602
603
604
605
    if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
        jack_error("ResumeRefNum error");
    }
}
sletz's avatar
sletz committed
606

607
inline void JackClient::End()
sletz's avatar
Cleanup    
sletz committed
608
{
sletz's avatar
sletz committed
609
    jack_log("JackClient::Execute end name = %s", GetClientControl()->fName);
sletz's avatar
sletz committed
610
611
    // Hum... not sure about this, the following "close" code is called in the RT thread...
    int result;
sletz's avatar
sletz committed
612
    fThread.DropSelfRealTime();
sletz's avatar
sletz committed
613
614
    GetClientControl()->fActive = false;
    fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
615
    fThread.Terminate();
sletz's avatar
Cleanup    
sletz committed
616
617
}

618
inline void JackClient::Error()
sletz's avatar
Cleanup    
sletz committed
619
{
sletz's avatar
sletz committed
620
621
622
    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;
sletz's avatar
sletz committed
623
    fThread.DropSelfRealTime();
sletz's avatar
sletz committed
624
625
    GetClientControl()->fActive = false;
    fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
sletz's avatar
Cleanup    
sletz committed
626
    ShutDown();
627
    fThread.Terminate();
sletz's avatar
Cleanup    
sletz committed
628
629
}

sletz's avatar
sletz committed
630
631
632
633
634
635
//-----------------
// Port management
//-----------------

int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
{
636
    // Check if port name is empty
sletz's avatar
sletz committed
637
638
    string port_name_str = string(port_name);
    if (port_name_str.size() == 0) {
639
        jack_error("port_name is empty");
sletz's avatar
sletz committed
640
641
642
        return 0; // Means failure here...
    }

643
    // Check port name length
sletz's avatar
sletz committed
644
645
646
    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"
647
                   "Please use %lu characters or less",
sletz's avatar
sletz committed
648
649
650
651
652
653
654
                   GetClientControl()->fName,
                   port_name,
                   JACK_PORT_NAME_SIZE - 1);
        return 0; // Means failure here...
    }

    int result = -1;
655
    jack_port_id_t port_index = NO_PORT;
sletz's avatar
sletz committed
656
    fChannel->PortRegister(GetClientControl()->fRefNum, name.c_str(), port_type, flags, buffer_size, &port_index, &result);
sletz's avatar
sletz committed
657

sletz's avatar
sletz committed
658
    if (result == 0) {
659
        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
660
661
662
663
664
665
666
667
668
        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
669
    jack_log("JackClient::PortUnRegister port_index = %ld", port_index);
sletz's avatar
sletz committed
670
    list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
sletz's avatar
sletz committed
671
672
673
674
675
676
677
678
679
680
681
682
683
684

    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
685
    jack_log("JackClient::Connect src = %s dst = %s", src, dst);
sletz's avatar
sletz committed
686
687
688
689
690
691
692
    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
693
    jack_log("JackClient::Disconnect src = %s dst = %s", src, dst);
sletz's avatar
sletz committed
694
695
696
697
698
699
700
    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
701
    jack_log("JackClient::PortDisconnect src = %ld", src);
sletz's avatar
sletz committed
702
703
704
705
706
707
708
709
710
711
712
    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();
}

713
714
715
716
717
718
719
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
720
721
722
723
//--------------------
// Context management
//--------------------

724
int JackClient::SetBufferSize(jack_nframes_t buffer_size)
sletz's avatar
sletz committed
725
726
{
    int result = -1;
727
    fChannel->SetBufferSize(buffer_size, &result);
sletz's avatar
sletz committed
728
729
730
731
732
733
734
735
736
737
    return result;
}

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

sletz's avatar
sletz committed
738
739
740
741
742
743
744
int JackClient::ComputeTotalLatencies()
{
    int result = -1;
    fChannel->ComputeTotalLatencies(&result);
    return result;
}

sletz's avatar
sletz committed
745
746
747
748
749
750
751
752
753
/*
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
754
    jack_log("JackClient::ShutDown");
755
    JackGlobals::fServerRunning = false;
sletz's avatar
sletz committed
756

sletz's avatar
sletz committed
757
758
759
760
    if (fInfoShutdown) {
        fInfoShutdown(JackFailure, "JACK server has been closed", fInfoShutdownArg);
        fInfoShutdown = NULL;
    } else if (fShutdown) {
sletz's avatar
sletz committed
761
762
763
764
765
766
767
768
769
        fShutdown(fShutdownArg);
        fShutdown = NULL;
    }
}

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

sletz's avatar
sletz committed
770
771
772
773
inline int JackClient::ActivateAux()
{
    // If activated without RT thread...
    if (IsActive() && fThread.GetStatus() != JackThread::kRunning) {
sletz's avatar
sletz committed
774
775
776

        jack_log("JackClient::ActivateAux");

sletz's avatar
sletz committed
777
778
779
        // RT thread is started
        if (StartThread() < 0)
            return -1;
sletz's avatar
sletz committed
780

sletz's avatar
sletz committed
781
782
783
784
        int result = -1;
        GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
        fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
        return result;
sletz's avatar
sletz committed
785

sletz's avatar
sletz committed
786
787
788
789
790
    } else {
        return 0;
    }
}

sletz's avatar
sletz committed
791
792
793
794
795
int JackClient::ReleaseTimebase()
{
    int result = -1;
    fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
    if (result == 0) {
sletz's avatar
sletz committed
796
        GetClientControl()->fTransportTimebase = false;
sletz's avatar
sletz committed
797
798
799
800
801
802
803
804
805
        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
806
    GetClientControl()->fTransportSync = (fSync != NULL);
sletz's avatar
sletz committed
807
    fSyncArg = arg;
sletz's avatar
sletz committed
808
    fSync = sync_callback;
sletz's avatar
sletz committed
809
    return ActivateAux();
sletz's avatar
sletz committed
810
811
}

sletz's avatar
sletz committed
812
813
814
815
int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
{
    int result = -1;
    fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
sletz's avatar
sletz committed
816

sletz's avatar
sletz committed
817
    if (result == 0) {
sletz's avatar
sletz committed
818
        GetClientControl()->fTransportTimebase = true;
sletz's avatar
sletz committed
819
820
        fTimebase = timebase_callback;
        fTimebaseArg = arg;
sletz's avatar
sletz committed
821
        return ActivateAux();
sletz's avatar
sletz committed
822
823
824
    } else {
        fTimebase = NULL;
        fTimebaseArg = NULL;
sletz's avatar
sletz committed
825
        return -1;
sletz's avatar
sletz committed
826
    }
sletz's avatar
sletz committed
827
828
829
830
831
832
}

int JackClient::SetSyncTimeout(jack_time_t timeout)
{
    GetEngineControl()->fTransport.SetSyncTimeout(timeout);
    return 0;
sletz's avatar
sletz committed
833
}
sletz's avatar
sletz committed
834
835
836

// Must be RT safe

837
void JackClient::TransportLocate(jack_nframes_t frame)
sletz's avatar
sletz committed
838
839
840
841
{
    jack_position_t pos;
    pos.frame = frame;
    pos.valid = (jack_position_bits_t)0;
sletz's avatar
sletz committed
842
    jack_log("JackClient::TransportLocate pos = %ld", pos.frame);
843
    GetEngineControl()->fTransport.RequestNewPos(&pos);
sletz's avatar
sletz committed
844
845
846
847
848
}

int JackClient::TransportReposition(jack_position_t* pos)
{
    jack_position_t tmp = *pos;
sletz's avatar
sletz committed
849
    jack_log("JackClient::TransportReposition pos = %ld", pos->frame);
850
851
852
853
854
855
    if (tmp.valid & ~JACK_POSITION_MASK) {
        return EINVAL;
    } else {
        GetEngineControl()->fTransport.RequestNewPos(pos);
        return 0;
    }
sletz's avatar
sletz committed
856
857
858
859
}

jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
{
860
    return GetEngineControl()->fTransport.Query(pos);
sletz's avatar
sletz committed
861
862
863
864
}

jack_nframes_t JackClient::GetCurrentTransportFrame()
{
865
    return GetEngineControl()->fTransport.GetCurrentFrame();
sletz's avatar
sletz committed
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
}

// 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
881
// TODO check concurrency with SetSyncCallback
sletz's avatar
sletz committed
882

sletz's avatar
sletz committed
883
void JackClient::CallSyncCallback()
sletz's avatar
sletz committed
884
885
886
887
888
{
    CallSyncCallbackAux();
}

inline void JackClient::CallSyncCallbackAux()
sletz's avatar
sletz committed
889
{
sletz's avatar
sletz committed
890
    if (GetClientControl()->fTransportSync) {
sletz's avatar
sletz committed
891

sletz's avatar
sletz committed
892
893
894
        JackTransportEngine& transport = GetEngineControl()->fTransport;
        jack_position_t* cur_pos = transport.ReadCurrentState();
        jack_transport_state_t transport_state = transport.GetState();
sletz's avatar
sletz committed
895

sletz's avatar
sletz committed
896
897
        if (fSync != NULL) {
            if (fSync(transport_state, cur_pos, fSyncArg)) {
sletz's avatar
sletz committed
898
                GetClientControl()->fTransportState = JackTransportRolling;
sletz's avatar
sletz committed
899
                GetClientControl()->fTransportSync = false;
sletz's avatar
sletz committed
900
            }
sletz's avatar
sletz committed
901
902
903
904
        } else {
            GetClientControl()->fTransportState = JackTransportRolling;
            GetClientControl()->fTransportSync = false;
        }
sletz's avatar
sletz committed
905
906
907
908
    }
}

void JackClient::CallTimebaseCallback()
sletz's avatar
sletz committed
909
910
911
912
913
{
    CallTimebaseCallbackAux();
}

inline void JackClient::CallTimebaseCallbackAux()
sletz's avatar
sletz committed
914
915
{
    JackTransportEngine& transport = GetEngineControl()->fTransport;
916
917
    int master;
    bool unused;
sletz's avatar
sletz committed
918

919
    transport.GetTimebaseMaster(master, unused);
sletz's avatar
sletz committed
920

921
    if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase...
sletz's avatar
sletz committed
922

sletz's avatar
sletz committed
923
924
        jack_transport_state_t transport_state = transport.GetState();
        jack_position_t* cur_pos = transport.WriteNextStateStart(1);
sletz's avatar
sletz committed
925

sletz's avatar
sletz committed
926
        if (GetClientControl()->fTransportTimebase) {
sletz's avatar
sletz committed
927
928
            fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
            GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true
sletz's avatar
sletz committed
929
        } else if (transport_state == JackTransportRolling) {
sletz's avatar
sletz committed
930
            fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
sletz's avatar
sletz committed
931
932
        }

sletz's avatar
sletz committed
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
        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;
    }
}
sletz's avatar
sletz committed
950

sletz's avatar
sletz committed
951
952
953
954
955
956
957
958
959
960
void JackClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
    } else {
        GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
        fInfoShutdownArg = arg;
        fInfoShutdown = callback;
    }
}
sletz's avatar
sletz committed
961
962
963
964
965
966

int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
{
    if (IsActive()) {
        jack_error("You cannot set callbacks on an active client");
        return -1;
967
    } else if (fThreadFun) {
968
969
970
        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
971
972
973
974
975
976
977
978
979
980
981
982
        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
983
        GetClientControl()->fCallback[kXRunCallback] = (callback != NULL);
sletz's avatar
sletz committed
984
985
986
987
988
989
990
991
992
993
994
995
996
997
        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;
sletz's avatar
sletz committed
998
999
        /* make sure that the message buffer thread is initialized too */
        JackMessageBuffer::fInstance->SetInitCallback(callback, arg);
sletz's avatar
sletz committed
1000
        return 0;
For faster browsing, not all history is shown. View entire blame