Jack2: JackClient.cpp Source File

Jack2 1.9.12

JackClient.cpp
1 /*
2 Copyright (C) 2001 Paul Davis
3 Copyright (C) 2004-2008 Grame
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20
21 #include "JackSystemDeps.h"
22 #include "JackGraphManager.h"
23 #include "JackClientControl.h"
24 #include "JackEngineControl.h"
25 #include "JackGlobals.h"
26 #include "JackChannel.h"
27 #include "JackTransportEngine.h"
28 #include "driver_interface.h"
29 #include "JackLibGlobals.h"
30
31 #include <math.h>
32 #include <string>
33 #include <algorithm>
34
35 using namespace std;
36
37 namespace Jack
38 {
39
40 #define IsRealTime() ((fProcess != NULL) | (fThreadFun != NULL) | (fSync != NULL) | (fTimebase != NULL))
41
42 JackClient::JackClient(JackSynchro* table):fThread(this)
43 {
44 fSynchroTable = table;
45 fProcess = NULL;
46 fGraphOrder = NULL;
47 fXrun = NULL;
48 fShutdown = NULL;
49 fInfoShutdown = NULL;
50 fInit = NULL;
51 fBufferSize = NULL;
52 fClientRegistration = NULL;
53 fFreewheel = NULL;
54 fPortRegistration = NULL;
55 fPortConnect = NULL;
56 fPortRename = NULL;
57 fTimebase = NULL;
58 fSync = NULL;
59 fThreadFun = NULL;
60 fSession = NULL;
61 fLatency = NULL;
62
63 fProcessArg = NULL;
64 fGraphOrderArg = NULL;
65 fXrunArg = NULL;
66 fShutdownArg = NULL;
67 fInfoShutdownArg = NULL;
68 fInitArg = NULL;
69 fBufferSizeArg = NULL;
70 fFreewheelArg = NULL;
71 fClientRegistrationArg = NULL;
72 fPortRegistrationArg = NULL;
73 fPortConnectArg = NULL;
74 fPortRenameArg = NULL;
75 fSyncArg = NULL;
76 fTimebaseArg = NULL;
77 fThreadFunArg = NULL;
78 fSessionArg = NULL;
79 fLatencyArg = NULL;
80
81 fSessionReply = kPendingSessionReply;
82 }
83
84 JackClient::~JackClient()
85 {}
86
87 void JackClient::ShutDown(jack_status_t code, const char* message)
88 {
89 jack_log("JackClient::ShutDown");
90
91 // If "fInfoShutdown" callback, then call it
92 if (fInfoShutdown) {
93 fInfoShutdown(code, message, fInfoShutdownArg);
94 fInfoShutdown = NULL;
95 // Otherwise possibly call the normal "fShutdown"
96 } else if (fShutdown) {
97 fShutdown(fShutdownArg);
98 fShutdown = NULL;
99 }
100 }
101
102 int JackClient::Close()
103 {
104 jack_log("JackClient::Close ref = %ld", GetClientControl()->fRefNum);
105 int result = 0;
106
107 Deactivate();
108
109 // Channels is stopped first to avoid receiving notifications while closing
110 fChannel->Stop();
111 // Then close client
112 fChannel->ClientClose(GetClientControl()->fRefNum, &result);
113
114 fChannel->Close();
115 assert(JackGlobals::fSynchroMutex);
116 JackGlobals::fSynchroMutex->Lock();
117 fSynchroTable[GetClientControl()->fRefNum].Disconnect();
118 JackGlobals::fSynchroMutex->Unlock();
119 JackGlobals::fClientTable[GetClientControl()->fRefNum] = NULL;
120 return result;
121 }
122
123 bool JackClient::IsActive()
124 {
125 return (GetClientControl()) ? GetClientControl()->fActive : false;
126 }
127
128 jack_native_thread_t JackClient::GetThreadID()
129 {
130 return fThread.GetThreadID();
131 }
132
138 void JackClient::SetupDriverSync(bool freewheel)
139 {
140 if (!freewheel && !GetEngineControl()->fSyncMode) {
141 jack_log("JackClient::SetupDriverSync driver sem in flush mode");
142 for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
143 fSynchroTable[i].SetFlush(true);
144 }
145 } else {
146 jack_log("JackClient::SetupDriverSync driver sem in normal mode");
147 for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
148 fSynchroTable[i].SetFlush(false);
149 }
150 }
151 }
152
157 int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
158 {
159 return 0;
160 }
161
162 int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
163 {
164 int res = 0;
165
166 jack_log("JackClient::ClientNotify ref = %ld name = %s notify = %ld", refnum, name, notify);
167
168 // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
169 switch (notify) {
170
171 case kAddClient:
172 res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
173 break;
174
175 case kRemoveClient:
176 res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
177 break;
178
179 case kActivateClient:
180 jack_log("JackClient::kActivateClient name = %s ref = %ld ", name, refnum);
181 InitAux();
182 break;
183 }
184
185 /*
186 The current semantic is that notifications can only be received when the client has been activated,
187 although is this implementation, one could imagine calling notifications as soon as the client has be opened.
188 */
189 if (IsActive()) {
190
191 switch (notify) {
192
193 case kAddClient:
194 jack_log("JackClient::kAddClient fName = %s name = %s", GetClientControl()->fName, name);
195 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
196 fClientRegistration(name, 1, fClientRegistrationArg);
197 }
198 break;
199
200 case kRemoveClient:
201 jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name);
202 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
203 fClientRegistration(name, 0, fClientRegistrationArg);
204 }
205 break;
206
207 case kBufferSizeCallback:
208 jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1);
209 if (fBufferSize) {
210 res = fBufferSize(value1, fBufferSizeArg);
211 }
212 break;
213
214 case kSampleRateCallback:
215 jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1);
216 if (fSampleRate) {
217 res = fSampleRate(value1, fSampleRateArg);
218 }
219 break;
220
221 case kGraphOrderCallback:
222 jack_log("JackClient::kGraphOrderCallback");
223 if (fGraphOrder) {
224 res = fGraphOrder(fGraphOrderArg);
225 }
226 break;
227
228 case kStartFreewheelCallback:
229 jack_log("JackClient::kStartFreewheel");
230 SetupDriverSync(true);
231 // Drop RT only when the RT thread is actually running
232 if (fThread.GetStatus() == JackThread::kRunning) {
233 fThread.DropRealTime();
234 }
235 if (fFreewheel) {
236 fFreewheel(1, fFreewheelArg);
237 }
238 break;
239
240 case kStopFreewheelCallback:
241 jack_log("JackClient::kStopFreewheel");
242 SetupDriverSync(false);
243 if (fFreewheel) {
244 fFreewheel(0, fFreewheelArg);
245 }
246 // Acquire RT only when the RT thread is actually running
247 if (GetEngineControl()->fRealTime && fThread.GetStatus() == JackThread::kRunning) {
248 if (fThread.AcquireRealTime(GetEngineControl()->fClientPriority) < 0) {
249 jack_error("JackClient::AcquireRealTime error");
250 }
251 }
252 break;
253
254 case kPortRegistrationOnCallback:
255 jack_log("JackClient::kPortRegistrationOn port_index = %ld", value1);
256 if (fPortRegistration) {
257 fPortRegistration(value1, 1, fPortRegistrationArg);
258 }
259 break;
260
261 case kPortRegistrationOffCallback:
262 jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1);
263 if (fPortRegistration) {
264 fPortRegistration(value1, 0, fPortRegistrationArg);
265 }
266 break;
267
268 case kPortConnectCallback:
269 jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2);
270 if (fPortConnect) {
271 fPortConnect(value1, value2, 1, fPortConnectArg);
272 }
273 break;
274
275 case kPortDisconnectCallback:
276 jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2);
277 if (fPortConnect) {
278 fPortConnect(value1, value2, 0, fPortConnectArg);
279 }
280 break;
281
282 case kPortRenameCallback:
283 jack_log("JackClient::kPortRenameCallback port = %ld", value1);
284 if (fPortRename) {
285 fPortRename(value1, message, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg);
286 }
287 break;
288
289 case kXRunCallback:
290 jack_log("JackClient::kXRunCallback");
291 if (fXrun) {
292 res = fXrun(fXrunArg);
293 }
294 break;
295
296 case kShutDownCallback:
297 jack_log("JackClient::kShutDownCallback");
298 ShutDown(jack_status_t(value1), message);
299 break;
300
301 case kSessionCallback:
302 jack_log("JackClient::kSessionCallback");
303 if (fSession) {
304 jack_session_event_t* event = (jack_session_event_t*)malloc( sizeof(jack_session_event_t));
305 char uuid_buf[JACK_UUID_SIZE];
306 event->type = (jack_session_event_type_t)value1;
307 event->session_dir = strdup(message);
308 event->command_line = NULL;
309 event->flags = (jack_session_flags_t)0;
310 snprintf(uuid_buf, sizeof(uuid_buf), "%d", GetClientControl()->fSessionID);
311 event->client_uuid = strdup(uuid_buf);
312 fSessionReply = kPendingSessionReply;
313 // Session callback may change fSessionReply by directly using jack_session_reply
314 fSession(event, fSessionArg);
315 res = fSessionReply;
316 }
317 break;
318
319 case kLatencyCallback:
320 res = HandleLatencyCallback(value1);
321 break;
322 }
323 }
324
325 return res;
326 }
327
328 int JackClient::HandleLatencyCallback(int status)
329 {
330 jack_latency_callback_mode_t mode = (status == 0) ? JackCaptureLatency : JackPlaybackLatency;
331 jack_latency_range_t latency = { UINT32_MAX, 0 };
332
333 /* first setup all latency values of the ports.
334 * this is based on the connections of the ports.
335 */
336 list<jack_port_id_t>::iterator it;
337
338 for (it = fPortList.begin(); it != fPortList.end(); it++) {
339 JackPort* port = GetGraphManager()->GetPort(*it);
340 if ((port->GetFlags() & JackPortIsOutput) && (mode == JackPlaybackLatency)) {
341 GetGraphManager()->RecalculateLatency(*it, mode);
342 }
343 if ((port->GetFlags() & JackPortIsInput) && (mode == JackCaptureLatency)) {
344 GetGraphManager()->RecalculateLatency(*it, mode);
345 }
346 }
347
348 if (!fLatency) {
349 /*
350 * default action is to assume all ports depend on each other.
351 * then always take the maximum latency.
352 */
353
354 if (mode == JackPlaybackLatency) {
355 /* iterate over all OutputPorts, to find maximum playback latency
356 */
357 for (it = fPortList.begin(); it != fPortList.end(); it++) {
358 JackPort* port = GetGraphManager()->GetPort(*it);
359 if (port->GetFlags() & JackPortIsOutput) {
360 jack_latency_range_t other_latency;
361 port->GetLatencyRange(mode, &other_latency);
362 if (other_latency.max > latency.max) {
363 latency.max = other_latency.max;
364 }
365 if (other_latency.min < latency.min) {
366 latency.min = other_latency.min;
367 }
368 }
369 }
370
371 if (latency.min == UINT32_MAX) {
372 latency.min = 0;
373 }
374
375 /* now set the found latency on all input ports
376 */
377 for (it = fPortList.begin(); it != fPortList.end(); it++) {
378 JackPort* port = GetGraphManager()->GetPort(*it);
379 if (port->GetFlags() & JackPortIsInput) {
380 port->SetLatencyRange(mode, &latency);
381 }
382 }
383 }
384 if (mode == JackCaptureLatency) {
385 /* iterate over all InputPorts, to find maximum playback latency
386 */
387 for (it = fPortList.begin(); it != fPortList.end(); it++) {
388 JackPort* port = GetGraphManager()->GetPort(*it);
389 if (port->GetFlags() & JackPortIsInput) {
390 jack_latency_range_t other_latency;
391 port->GetLatencyRange(mode, &other_latency);
392 if (other_latency.max > latency.max) {
393 latency.max = other_latency.max;
394 }
395 if (other_latency.min < latency.min) {
396 latency.min = other_latency.min;
397 }
398 }
399 }
400
401 if (latency.min == UINT32_MAX) {
402 latency.min = 0;
403 }
404
405 /* now set the found latency on all output ports
406 */
407 for (it = fPortList.begin(); it != fPortList.end(); it++) {
408 JackPort* port = GetGraphManager()->GetPort(*it);
409 if (port->GetFlags() & JackPortIsOutput) {
410 port->SetLatencyRange(mode, &latency);
411 }
412 }
413 }
414 return 0;
415 }
416
417 /* we have a latency callback setup by the client,
418 * lets use it...
419 */
420 fLatency(mode, fLatencyArg);
421 return 0;
422 }
423
428 int JackClient::Activate()
429 {
430 jack_log("JackClient::Activate");
431 if (IsActive()) {
432 return 0;
433 }
434
435 // RT thread is started only when needed...
436 if (IsRealTime()) {
437 if (StartThread() < 0) {
438 return -1;
439 }
440 }
441
442 /*
443 Insertion of client in the graph will cause a kGraphOrderCallback notification
444 to be delivered by the server, the client wants to receive it.
445 */
446 GetClientControl()->fActive = true;
447
448 // Transport related callback become "active"
449 GetClientControl()->fTransportSync = true;
450 GetClientControl()->fTransportTimebase = true;
451
452 int result = -1;
453 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
454 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
455 return result;
456 }
457
461 int JackClient::Deactivate()
462 {
463 jack_log("JackClient::Deactivate");
464 if (!IsActive()) {
465 return 0;
466 }
467
468 GetClientControl()->fActive = false;
469
470 // Transport related callback become "unactive"
471 GetClientControl()->fTransportSync = false;
472 GetClientControl()->fTransportTimebase = false;
473
474 // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
475 int result = -1;
476 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
477 jack_log("JackClient::Deactivate res = %ld", result);
478
479 // RT thread is stopped only when needed...
480 if (IsRealTime()) {
481 fThread.Kill();
482 }
483 return result;
484 }
485
486 //----------------------
487 // RT thread management
488 //----------------------
489
490 void JackClient::InitAux()
491 {
492 if (fInit) {
493 jack_log("JackClient::Init calling client thread init callback");
494 fInit(fInitArg);
495 }
496 }
497
501 bool JackClient::Init()
502 {
503 /*
504 Execute buffer_size callback.
505
506 Since StartThread uses fThread.StartSync, we are sure that buffer_size callback
507 is executed before StartThread returns (and then IsActive will be true).
508 So no RT callback can be called at the same time.
509 */
510 jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", GetEngineControl()->fBufferSize);
511 if (fBufferSize) {
512 fBufferSize(GetEngineControl()->fBufferSize, fBufferSizeArg);
513 }
514
515 // Init callback
516 InitAux();
517
518 // Setup context
519 if (!jack_tls_set(JackGlobals::fRealTimeThread, this)) {
520 jack_error("Failed to set thread realtime key");
521 }
522
523 // Setup RT
524 if (GetEngineControl()->fRealTime) {
525 set_threaded_log_function();
526 SetupRealTime();
527 }
528
529 return true;
530 }
531
532 void JackClient::SetupRealTime()
533 {
534 jack_log("JackClient::Init : period = %ld computation = %ld constraint = %ld",
535 long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
536 long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
537 long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));
538
539 // Will do "something" on OSX only...
540 fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
541
542 if (fThread.AcquireSelfRealTime(GetEngineControl()->fClientPriority) < 0) {
543 jack_error("JackClient::AcquireSelfRealTime error");
544 }
545 }
546
547 int JackClient::StartThread()
548 {
549 if (fThread.StartSync() < 0) {
550 jack_error("Start thread error");
551 return -1;
552 }
553
554 return 0;
555 }
556
561 bool JackClient::Execute()
562 {
563 // Execute a dummy cycle to be sure thread has the correct properties
564 DummyCycle();
565
566 if (fThreadFun) {
567 fThreadFun(fThreadFunArg);
568 } else {
569 ExecuteThread();
570 }
571 return false;
572 }
573
574 void JackClient::DummyCycle()
575 {
576 WaitSync();
577 SignalSync();
578 }
579
580 inline void JackClient::ExecuteThread()
581 {
582 while (true) {
583 CycleWaitAux();
584 CycleSignalAux(CallProcessCallback());
585 }
586 }
587
588 inline jack_nframes_t JackClient::CycleWaitAux()
589 {
590 if (!WaitSync()) {
591 Error(); // Terminates the thread
592 }
593 CallSyncCallbackAux();
594 return GetEngineControl()->fBufferSize;
595 }
596
597 inline void JackClient::CycleSignalAux(int status)
598 {
599 if (status == 0) {
600 CallTimebaseCallbackAux();
601 }
602 SignalSync();
603 if (status != 0) {
604 End(); // Terminates the thread
605 }
606 }
607
608 jack_nframes_t JackClient::CycleWait()
609 {
610 return CycleWaitAux();
611 }
612
613 void JackClient::CycleSignal(int status)
614 {
615 CycleSignalAux(status);
616 }
617
618 inline int JackClient::CallProcessCallback()
619 {
620 return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0;
621 }
622
623 inline bool JackClient::WaitSync()
624 {
625 // Suspend itself: wait on the input synchro
626 if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
627 jack_error("SuspendRefNum error");
628 return false;
629 } else {
630 return true;
631 }
632 }
633
634 inline void JackClient::SignalSync()
635 {
636 // Resume: signal output clients connected to the running client
637 if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
638 jack_error("ResumeRefNum error");
639 }
640 }
641
642 inline void JackClient::End()
643 {
644 jack_log("JackClient::Execute end name = %s", GetClientControl()->fName);
645 // Hum... not sure about this, the following "close" code is called in the RT thread...
646 int result;
647 fThread.DropSelfRealTime();
648 GetClientControl()->fActive = false;
649 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
650 fThread.Terminate();
651 }
652
653 inline void JackClient::Error()
654 {
655 jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
656 // Hum... not sure about this, the following "close" code is called in the RT thread...
657 int result;
658 fThread.DropSelfRealTime();
659 GetClientControl()->fActive = false;
660 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
661 ShutDown(jack_status_t(JackFailure | JackServerError), JACK_SERVER_FAILURE);
662 fThread.Terminate();
663 }
664
665 //-----------------
666 // Port management
667 //-----------------
668
669 int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
670 {
671 // Check if port name is empty
672 string port_short_name_str = string(port_name);
673 if (port_short_name_str.size() == 0) {
674 jack_error("port_name is empty");
675 return 0; // Means failure here...
676 }
677
678 // Check port name length
679 string port_full_name_str = string(GetClientControl()->fName) + string(":") + port_short_name_str;
680 if (port_full_name_str.size() >= REAL_JACK_PORT_NAME_SIZE) {
681 jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
682 "Please use %lu characters or less",
683 GetClientControl()->fName,
684 port_name,
685 JACK_PORT_NAME_SIZE - 1);
686 return 0; // Means failure here...
687 }
688
689 int result = -1;
690 jack_port_id_t port_index = NO_PORT;
691 fChannel->PortRegister(GetClientControl()->fRefNum, port_full_name_str.c_str(), port_type, flags, buffer_size, &port_index, &result);
692
693 if (result == 0) {
694 jack_log("JackClient::PortRegister ref = %ld name = %s type = %s port_index = %ld", GetClientControl()->fRefNum, port_full_name_str.c_str(), port_type, port_index);
695 fPortList.push_back(port_index);
696 return port_index;
697 } else {
698 return 0;
699 }
700 }
701
702 int JackClient::PortUnRegister(jack_port_id_t port_index)
703 {
704 jack_log("JackClient::PortUnRegister port_index = %ld", port_index);
705 list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
706
707 if (it != fPortList.end()) {
708 fPortList.erase(it);
709 int result = -1;
710 fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
711 return result;
712 } else {
713 jack_error("unregistering a port %ld that is not own by the client", port_index);
714 return -1;
715 }
716 }
717
718 int JackClient::PortConnect(const char* src, const char* dst)
719 {
720 jack_log("JackClient::Connect src = %s dst = %s", src, dst);
721 if (strlen(src) >= REAL_JACK_PORT_NAME_SIZE) {
722 jack_error("\"%s\" is too long to be used as a JACK port name.\n", src);
723 return -1;
724 }
725 if (strlen(dst) >= REAL_JACK_PORT_NAME_SIZE) {
726 jack_error("\"%s\" is too long to be used as a JACK port name.\n", src);
727 return -1;
728 }
729 int result = -1;
730 fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
731 return result;
732 }
733
734 int JackClient::PortDisconnect(const char* src, const char* dst)
735 {
736 jack_log("JackClient::Disconnect src = %s dst = %s", src, dst);
737 if (strlen(src) >= REAL_JACK_PORT_NAME_SIZE) {
738 jack_error("\"%s\" is too long to be used as a JACK port name.\n", src);
739 return -1;
740 }
741 if (strlen(dst) >= REAL_JACK_PORT_NAME_SIZE) {
742 jack_error("\"%s\" is too long to be used as a JACK port name.\n", src);
743 return -1;
744 }
745 int result = -1;
746 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
747 return result;
748 }
749
750 int JackClient::PortDisconnect(jack_port_id_t src)
751 {
752 jack_log("JackClient::PortDisconnect src = %ld", src);
753 int result = -1;
754 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
755 return result;
756 }
757
758 int JackClient::PortIsMine(jack_port_id_t port_index)
759 {
760 JackPort* port = GetGraphManager()->GetPort(port_index);
761 return GetClientControl()->fRefNum == port->GetRefNum();
762 }
763
764 int JackClient::PortRename(jack_port_id_t port_index, const char* name)
765 {
766 int result = -1;
767 fChannel->PortRename(GetClientControl()->fRefNum, port_index, name, &result);
768 return result;
769 }
770
771 //--------------------
772 // Context management
773 //--------------------
774
775 int JackClient::SetBufferSize(jack_nframes_t buffer_size)
776 {
777 int result = -1;
778 fChannel->SetBufferSize(buffer_size, &result);
779 return result;
780 }
781
782 int JackClient::SetFreeWheel(int onoff)
783 {
784 int result = -1;
785 fChannel->SetFreewheel(onoff, &result);
786 return result;
787 }
788
789 int JackClient::ComputeTotalLatencies()
790 {
791 int result = -1;
792 fChannel->ComputeTotalLatencies(&result);
793 return result;
794 }
795
796 //----------------------
797 // Transport management
798 //----------------------
799
800 inline int JackClient::ActivateAux()
801 {
802 // If activated without RT thread...
803 if (IsActive() && fThread.GetStatus() != JackThread::kRunning) {
804
805 jack_log("JackClient::ActivateAux");
806
807 // RT thread is started
808 if (StartThread() < 0) {
809 return -1;
810 }
811
812 int result = -1;
813 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
814 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
815 return result;
816
817 } else {
818 return 0;
819 }
820 }
821
822 int JackClient::ReleaseTimebase()
823 {
824 int result = -1;
825 fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
826 if (result == 0) {
827 GetClientControl()->fTransportTimebase = false;
828 fTimebase = NULL;
829 fTimebaseArg = NULL;
830 }
831 return result;
832 }
833
834 /* Call the server if the client is active, otherwise keeps the arguments */
835 int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
836 {
837 GetClientControl()->fTransportSync = (fSync != NULL);
838 fSyncArg = arg;
839 fSync = sync_callback;
840 return ActivateAux();
841 }
842
843 int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
844 {
845 int result = -1;
846 fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
847
848 if (result == 0) {
849 GetClientControl()->fTransportTimebase = true;
850 fTimebase = timebase_callback;
851 fTimebaseArg = arg;
852 return ActivateAux();
853 } else {
854 fTimebase = NULL;
855 fTimebaseArg = NULL;
856 return result;
857 }
858 }
859
860 int JackClient::SetSyncTimeout(jack_time_t timeout)
861 {
862 GetEngineControl()->fTransport.SetSyncTimeout(timeout);
863 return 0;
864 }
865
866 // Must be RT safe
867
868 void JackClient::TransportLocate(jack_nframes_t frame)
869 {
870 jack_position_t pos;
871 pos.frame = frame;
872 pos.valid = (jack_position_bits_t)0;
873 jack_log("JackClient::TransportLocate pos = %ld", pos.frame);
874 GetEngineControl()->fTransport.RequestNewPos(&pos);
875 }
876
877 int JackClient::TransportReposition(const jack_position_t* pos)
878 {
879 jack_position_t tmp = *pos;
880 jack_log("JackClient::TransportReposition pos = %ld", pos->frame);
881 if (tmp.valid & ~JACK_POSITION_MASK) {
882 return EINVAL;
883 } else {
884 GetEngineControl()->fTransport.RequestNewPos(&tmp);
885 return 0;
886 }
887 }
888
889 jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
890 {
891 return GetEngineControl()->fTransport.Query(pos);
892 }
893
894 jack_nframes_t JackClient::GetCurrentTransportFrame()
895 {
896 return GetEngineControl()->fTransport.GetCurrentFrame();
897 }
898
899 // Must be RT safe: directly write in the transport shared mem
900 void JackClient::TransportStart()
901 {
902 GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
903 }
904
905 // Must be RT safe: directly write in the transport shared mem
906 void JackClient::TransportStop()
907 {
908 GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
909 }
910
911 // Never called concurently with the server
912 // TODO check concurrency with SetSyncCallback
913
914 void JackClient::CallSyncCallback()
915 {
916 CallSyncCallbackAux();
917 }
918
919 inline void JackClient::CallSyncCallbackAux()
920 {
921 if (GetClientControl()->fTransportSync) {
922
923 JackTransportEngine& transport = GetEngineControl()->fTransport;
924 jack_position_t* cur_pos = transport.ReadCurrentState();
925 jack_transport_state_t transport_state = transport.GetState();
926
927 if (fSync != NULL) {
928 if (fSync(transport_state, cur_pos, fSyncArg)) {
929 GetClientControl()->fTransportState = JackTransportRolling;
930 GetClientControl()->fTransportSync = false;
931 }
932 } else {
933 GetClientControl()->fTransportState = JackTransportRolling;
934 GetClientControl()->fTransportSync = false;
935 }
936 }
937 }
938
939 void JackClient::CallTimebaseCallback()
940 {
941 CallTimebaseCallbackAux();
942 }
943
944 inline void JackClient::CallTimebaseCallbackAux()
945 {
946 JackTransportEngine& transport = GetEngineControl()->fTransport;
947 int master;
948 bool unused;
949
950 transport.GetTimebaseMaster(master, unused);
951
952 if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase...
953
954 jack_transport_state_t transport_state = transport.GetState();
955 jack_position_t* cur_pos = transport.WriteNextStateStart(1);
956
957 if (GetClientControl()->fTransportTimebase) {
958 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
959 GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true
960 } else if (transport_state == JackTransportRolling) {
961 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
962 }
963
964 transport.WriteNextStateStop(1);
965 }
966 }
967
968 //---------------------
969 // Callback management
970 //---------------------
971
972 void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
973 {
974 if (IsActive()) {
975 jack_error("You cannot set callbacks on an active client");
976 } else {
977 // Shutdown callback will either be an old API version or the new version (with info)
978 GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
979 fShutdownArg = arg;
980 fShutdown = callback;
981 }
982 }
983
984 void JackClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg)
985 {
986 if (IsActive()) {
987 jack_error("You cannot set callbacks on an active client");
988 } else {
989 // Shutdown callback will either be an old API version or the new version (with info)
990 GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
991 fInfoShutdownArg = arg;
992 fInfoShutdown = callback;
993 }
994 }
995
996 int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
997 {
998 if (IsActive()) {
999 jack_error("You cannot set callbacks on an active client");
1000 return -1;
1001 } else if (fThreadFun) {
1002 jack_error ("A thread callback has already been setup, both models cannot be used at the same time!");
1003 return -1;
1004 } else {
1005 fProcessArg = arg;
1006 fProcess = callback;
1007 return 0;
1008 }
1009 }
1010
1011 int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
1012 {
1013 if (IsActive()) {
1014 jack_error("You cannot set callbacks on an active client");
1015 return -1;
1016 } else {
1017 GetClientControl()->fCallback[kXRunCallback] = (callback != NULL);
1018 fXrunArg = arg;
1019 fXrun = callback;
1020 return 0;
1021 }
1022 }
1023
1024 int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
1025 {
1026 if (IsActive()) {
1027 jack_error("You cannot set callbacks on an active client");
1028 return -1;
1029 } else {
1030 fInitArg = arg;
1031 fInit = callback;
1032 /* make sure that the message buffer thread is initialized too */
1033 return JackMessageBuffer::fInstance->SetInitCallback(callback, arg);
1034 }
1035 }
1036
1037 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
1038 {
1039 if (IsActive()) {
1040 jack_error("You cannot set callbacks on an active client");
1041 return -1;
1042 } else {
1043 GetClientControl()->fCallback[kGraphOrderCallback] = (callback != NULL);
1044 fGraphOrder = callback;
1045 fGraphOrderArg = arg;
1046 return 0;
1047 }
1048 }
1049
1050 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
1051 {
1052 if (IsActive()) {
1053 jack_error("You cannot set callbacks on an active client");
1054 return -1;
1055 } else {
1056 GetClientControl()->fCallback[kBufferSizeCallback] = (callback != NULL);
1057 fBufferSizeArg = arg;
1058 fBufferSize = callback;
1059 return 0;
1060 }
1061 }
1062
1063 int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg)
1064 {
1065 if (IsActive()) {
1066 jack_error("You cannot set callbacks on an active client");
1067 return -1;
1068 } else {
1069 GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL);
1070 fSampleRateArg = arg;
1071 fSampleRate = callback;
1072 // Now invoke it
1073 if (callback) {
1074 callback(GetEngineControl()->fSampleRate, arg);
1075 }
1076 return 0;
1077 }
1078 }
1079
1080 int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg)
1081 {
1082 if (IsActive()) {
1083 jack_error("You cannot set callbacks on an active client");
1084 return -1;
1085 } else {
1086 // kAddClient and kRemoveClient notifications must be delivered by the server in any case
1087 fClientRegistrationArg = arg;
1088 fClientRegistration = callback;
1089 return 0;
1090 }
1091 }
1092
1093 int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
1094 {
1095 if (IsActive()) {
1096 jack_error("You cannot set callbacks on an active client");
1097 return -1;
1098 } else {
1099 GetClientControl()->fCallback[kStartFreewheelCallback] = (callback != NULL);
1100 GetClientControl()->fCallback[kStopFreewheelCallback] = (callback != NULL);
1101 fFreewheelArg = arg;
1102 fFreewheel = callback;
1103 return 0;
1104 }
1105 }
1106
1107 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
1108 {
1109 if (IsActive()) {
1110 jack_error("You cannot set callbacks on an active client");
1111 return -1;
1112 } else {
1113 GetClientControl()->fCallback[kPortRegistrationOnCallback] = (callback != NULL);
1114 GetClientControl()->fCallback[kPortRegistrationOffCallback] = (callback != NULL);
1115 fPortRegistrationArg = arg;
1116 fPortRegistration = callback;
1117 return 0;
1118 }
1119 }
1120
1121 int JackClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg)
1122 {
1123 if (IsActive()) {
1124 jack_error("You cannot set callbacks on an active client");
1125 return -1;
1126 } else {
1127 GetClientControl()->fCallback[kPortConnectCallback] = (callback != NULL);
1128 GetClientControl()->fCallback[kPortDisconnectCallback] = (callback != NULL);
1129 fPortConnectArg = arg;
1130 fPortConnect = callback;
1131 return 0;
1132 }
1133 }
1134
1135 int JackClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg)
1136 {
1137 if (IsActive()) {
1138 jack_error("You cannot set callbacks on an active client");
1139 return -1;
1140 } else {
1141 GetClientControl()->fCallback[kPortRenameCallback] = (callback != NULL);
1142 fPortRenameArg = arg;
1143 fPortRename = callback;
1144 return 0;
1145 }
1146 }
1147
1148 int JackClient::SetProcessThread(JackThreadCallback fun, void *arg)
1149 {
1150 if (IsActive()) {
1151 jack_error("You cannot set callbacks on an active client");
1152 return -1;
1153 } else if (fProcess) {
1154 jack_error("A process callback has already been setup, both models cannot be used at the same time!");
1155 return -1;
1156 } else {
1157 fThreadFun = fun;
1158 fThreadFunArg = arg;
1159 return 0;
1160 }
1161 }
1162
1163 int JackClient::SetSessionCallback(JackSessionCallback callback, void *arg)
1164 {
1165 if (IsActive()) {
1166 jack_error("You cannot set callbacks on an active client");
1167 return -1;
1168 } else {
1169 GetClientControl()->fCallback[kSessionCallback] = (callback != NULL);
1170 fSessionArg = arg;
1171 fSession = callback;
1172 return 0;
1173 }
1174 }
1175
1176 int JackClient::SetLatencyCallback(JackLatencyCallback callback, void *arg)
1177 {
1178 if (IsActive()) {
1179 jack_error("You cannot set callbacks on an active client");
1180 return -1;
1181 } else {
1182 // fCallback[kLatencyCallback] must always be 'true'
1183 fLatencyArg = arg;
1184 fLatency = callback;
1185 return 0;
1186 }
1187 }
1188
1189 //------------------
1190 // Internal clients
1191 //------------------
1192
1193 char* JackClient::GetInternalClientName(int ref)
1194 {
1195 char name_res[JACK_CLIENT_NAME_SIZE+1];
1196 int result = -1;
1197 fChannel->GetInternalClientName(GetClientControl()->fRefNum, ref, name_res, &result);
1198 return (result < 0) ? NULL : strdup(name_res);
1199 }
1200
1201 int JackClient::InternalClientHandle(const char* client_name, jack_status_t* status)
1202 {
1203 int int_ref, result = -1;
1204 fChannel->InternalClientHandle(GetClientControl()->fRefNum, client_name, (int*)status, &int_ref, &result);
1205 return int_ref;
1206 }
1207
1208 int JackClient::InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va)
1209 {
1210 if (strlen(client_name) >= JACK_CLIENT_NAME_SIZE) {
1211 jack_error ("\"%s\" is too long for a JACK client name.\n"
1212 "Please use %lu characters or less.",
1213 client_name, JACK_CLIENT_NAME_SIZE);
1214 return 0;
1215 }
1216
1217 if (va->load_name && (strlen(va->load_name) >= JACK_PATH_MAX)) {
1218 jack_error("\"%s\" is too long for a shared object name.\n"
1219 "Please use %lu characters or less.",
1220 va->load_name, JACK_PATH_MAX);
1221 int my_status1 = *status | (JackFailure | JackInvalidOption);
1222 *status = (jack_status_t)my_status1;
1223 return 0;
1224 }
1225
1226 if (va->load_init && (strlen(va->load_init) >= JACK_LOAD_INIT_LIMIT)) {
1227 jack_error ("\"%s\" is too long for internal client init "
1228 "string.\nPlease use %lu characters or less.",
1229 va->load_init, JACK_LOAD_INIT_LIMIT);
1230 int my_status1 = *status | (JackFailure | JackInvalidOption);
1231 *status = (jack_status_t)my_status1;
1232 return 0;
1233 }
1234
1235 int int_ref, result = -1;
1236 fChannel->InternalClientLoad(GetClientControl()->fRefNum, client_name, va->load_name, va->load_init, options, (int*)status, &int_ref, -1, &result);
1237 return int_ref;
1238 }
1239
1240 void JackClient::InternalClientUnload(int ref, jack_status_t* status)
1241 {
1242 int result = -1;
1243 fChannel->InternalClientUnload(GetClientControl()->fRefNum, ref, (int*)status, &result);
1244 }
1245
1246 //------------------
1247 // Session API
1248 //------------------
1249
1250 jack_session_command_t* JackClient::SessionNotify(const char* target, jack_session_event_type_t type, const char* path)
1251 {
1252 jack_session_command_t* res;
1253 fChannel->SessionNotify(GetClientControl()->fRefNum, target, type, path, &res);
1254 return res;
1255 }
1256
1257 int JackClient::SessionReply(jack_session_event_t* ev)
1258 {
1259 if (ev->command_line) {
1260 strncpy(GetClientControl()->fSessionCommand, ev->command_line, sizeof(GetClientControl()->fSessionCommand));
1261 } else {
1262 GetClientControl()->fSessionCommand[0] = '\0';
1263 }
1264
1265 GetClientControl()->fSessionFlags = ev->flags;
1266
1267 jack_log("JackClient::SessionReply... we are here");
1268 if (fChannel->IsChannelThread()) {
1269 jack_log("JackClient::SessionReply... in callback reply");
1270 // OK, immediate reply...
1271 fSessionReply = kImmediateSessionReply;
1272 return 0;
1273 }
1274
1275 jack_log("JackClient::SessionReply... out of cb");
1276
1277 int result = -1;
1278 fChannel->SessionReply(GetClientControl()->fRefNum, &result);
1279 return result;
1280 }
1281
1282 char* JackClient::GetUUIDForClientName(const char* client_name)
1283 {
1284 char uuid_res[JACK_UUID_SIZE];
1285 int result = -1;
1286 fChannel->GetUUIDForClientName(GetClientControl()->fRefNum, client_name, uuid_res, &result);
1287 return (result) ? NULL : strdup(uuid_res);
1288 }
1289
1290 char* JackClient::GetClientNameByUUID(const char* uuid)
1291 {
1292 char name_res[JACK_CLIENT_NAME_SIZE + 1];
1293 int result = -1;
1294 fChannel->GetClientNameForUUID(GetClientControl()->fRefNum, uuid, name_res, &result);
1295 return (result) ? NULL : strdup(name_res);
1296 }
1297
1298 int JackClient::ReserveClientName(const char* client_name, const char* uuid)
1299 {
1300 int result = -1;
1301 fChannel->ReserveClientName( GetClientControl()->fRefNum, client_name, uuid, &result);
1302 return result;
1303 }
1304
1305 int JackClient::ClientHasSessionCallback(const char* client_name)
1306 {
1307 int result = -1;
1308 fChannel->ClientHasSessionCallback(client_name, &result);
1309 return result;
1310 }
1311
1312 } // end of namespace
1313
_jack_position
Definition: types.h:554
Jack
Definition: JackAC3Encoder.cpp:30
Jack::JackTransportEngine
Definition: JackTransportEngine.h:93
Jack::JackPort
Base class for port.
Definition: JackPort.h:39
jack_varargs_t
Definition: varargs.h:34
_jack_session_event::flags
jack_session_flags_t flags
Definition: JackSession.h:50
_jack_session_event
Definition: JackSession.h:45
Jack::JackClient::Deactivate
virtual int Deactivate()
Need to stop thread after deactivating in the server.
Definition: JackClient.cpp:461
Jack::JackClient::ClientNotifyImp
virtual int ClientNotifyImp(int refnum, const char *name, int notify, int sync, const char *message, int value1, int value)
Notification received from the server.
Definition: JackClient.cpp:157
std
jack_error
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:92
_jack_position::valid
jack_position_bits_t valid
Definition: types.h:562
Jack::JackAtomicArrayState::ReadCurrentState
T * ReadCurrentState()
Returns the current state : only valid in the RT reader thread.
Definition: JackAtomicArrayState.h:172
_jack_latency_range::min
jack_nframes_t min
Definition: types.h:270
_jack_latency_range
Definition: types.h:265
_jack_latency_range::max
jack_nframes_t max
Definition: types.h:274
Jack::JackClient::Activate
virtual int Activate()
We need to start thread before activating in the server, otherwise the FW driver connected to the cli...
Definition: JackClient.cpp:428
Jack::JackClient::SetupDriverSync
void SetupDriverSync(bool freewheel)
Definition: JackClient.cpp:138
jack_session_command_t
Definition: JackSession.h:59
Jack::JackClient::Execute
bool Execute()
RT thread.
Definition: JackClient.cpp:561
Jack::JackClient::Init
bool Init()
Called once when the thread starts.
Definition: JackClient.cpp:501
_jack_position::frame
jack_nframes_t frame
Definition: types.h:560
jack_session_flags_t
enum JackSessionFlags jack_session_flags_t
Definition: session.h:98
Jack::JackAtomicArrayState::WriteNextStateStop
void WriteNextStateStop(int state)
Stop write operation : make the next state ready to be used by the RT thread.
Definition: JackAtomicArrayState.h:247
_jack_session_event::type
jack_session_event_type_t type
Definition: JackSession.h:46
Jack::JackAtomicArrayState::WriteNextStateStart
T * WriteNextStateStart(int state)
Start write operation : setup and returns the next state to update, check for recursive write calls...
Definition: JackAtomicArrayState.h:230
jack_log
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:108
JackSessionCallback
void(* JackSessionCallback)(jack_session_event_t *event, void *arg)
Definition: session.h:162
_jack_session_event::command_line
char * command_line
Definition: JackSession.h:49