Merge branch 'master' into develop
[jack2.git] / common / JackClient.cpp
blobb03dbc25d2a43d7b37dd49db47f3fe11b03f96e5
1 /*
2 Copyright (C) 2001 Paul Davis
3 Copyright (C) 2004-2008 Grame
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.
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.
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.
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"
31 #include <math.h>
32 #include <string>
33 #include <algorithm>
35 using namespace std;
37 namespace Jack
40 #define IsRealTime() ((fProcess != NULL) | (fThreadFun != NULL) | (fSync != NULL) | (fTimebase != NULL))
42 JackClient::JackClient(JackSynchro* table):fThread(this)
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 fPropertyChange = NULL;
64 fProcessArg = NULL;
65 fGraphOrderArg = NULL;
66 fXrunArg = NULL;
67 fShutdownArg = NULL;
68 fInfoShutdownArg = NULL;
69 fInitArg = NULL;
70 fBufferSizeArg = NULL;
71 fFreewheelArg = NULL;
72 fClientRegistrationArg = NULL;
73 fPortRegistrationArg = NULL;
74 fPortConnectArg = NULL;
75 fPortRenameArg = NULL;
76 fSyncArg = NULL;
77 fTimebaseArg = NULL;
78 fThreadFunArg = NULL;
79 fSessionArg = NULL;
80 fLatencyArg = NULL;
81 fPropertyChangeArg = NULL;
83 fSessionReply = kPendingSessionReply;
86 JackClient::~JackClient()
89 void JackClient::ShutDown(jack_status_t code, const char* message)
91 jack_log("JackClient::ShutDown");
93 // If "fInfoShutdown" callback, then call it
94 if (fInfoShutdown) {
95 fInfoShutdown(code, message, fInfoShutdownArg);
96 fInfoShutdown = NULL;
97 // Otherwise possibly call the normal "fShutdown"
98 } else if (fShutdown) {
99 fShutdown(fShutdownArg);
100 fShutdown = NULL;
104 int JackClient::Close()
106 jack_log("JackClient::Close ref = %ld", GetClientControl()->fRefNum);
107 int result = 0;
109 Deactivate();
111 // Channels is stopped first to avoid receiving notifications while closing
112 fChannel->Stop();
113 // Then close client
114 fChannel->ClientClose(GetClientControl()->fRefNum, &result);
116 fChannel->Close();
117 assert(JackGlobals::fSynchroMutex);
118 JackGlobals::fSynchroMutex->Lock();
119 fSynchroTable[GetClientControl()->fRefNum].Disconnect();
120 JackGlobals::fSynchroMutex->Unlock();
121 JackGlobals::fClientTable[GetClientControl()->fRefNum] = NULL;
122 return result;
125 bool JackClient::IsActive()
127 return (GetClientControl()) ? GetClientControl()->fActive : false;
130 jack_native_thread_t JackClient::GetThreadID()
132 return fThread.GetThreadID();
136 In "async" mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations.
137 The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations.
138 Drivers synchro are setup in "flush" mode if server is "async" and NOT freewheel.
140 void JackClient::SetupDriverSync(bool freewheel)
142 if (!freewheel && !GetEngineControl()->fSyncMode) {
143 jack_log("JackClient::SetupDriverSync driver sem in flush mode");
144 for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
145 fSynchroTable[i].SetFlush(true);
147 } else {
148 jack_log("JackClient::SetupDriverSync driver sem in normal mode");
149 for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
150 fSynchroTable[i].SetFlush(false);
156 \brief Notification received from the server.
159 int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
161 return 0;
164 int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
166 int res = 0;
168 jack_log("JackClient::ClientNotify ref = %ld name = %s notify = %ld", refnum, name, notify);
170 // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
171 switch (notify) {
173 case kAddClient:
174 res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
175 break;
177 case kRemoveClient:
178 res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
179 break;
181 case kActivateClient:
182 jack_log("JackClient::kActivateClient name = %s ref = %ld ", name, refnum);
183 InitAux();
184 break;
188 The current semantic is that notifications can only be received when the client has been activated,
189 although is this implementation, one could imagine calling notifications as soon as the client has be opened.
191 if (IsActive()) {
193 switch (notify) {
195 case kAddClient:
196 jack_log("JackClient::kAddClient fName = %s name = %s", GetClientControl()->fName, name);
197 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
198 fClientRegistration(name, 1, fClientRegistrationArg);
200 break;
202 case kRemoveClient:
203 jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name);
204 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
205 fClientRegistration(name, 0, fClientRegistrationArg);
207 break;
209 case kBufferSizeCallback:
210 jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1);
211 if (fBufferSize) {
212 res = fBufferSize(value1, fBufferSizeArg);
214 break;
216 case kSampleRateCallback:
217 jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1);
218 if (fSampleRate) {
219 res = fSampleRate(value1, fSampleRateArg);
221 break;
223 case kGraphOrderCallback:
224 jack_log("JackClient::kGraphOrderCallback");
225 if (fGraphOrder) {
226 res = fGraphOrder(fGraphOrderArg);
228 break;
230 case kStartFreewheelCallback:
231 jack_log("JackClient::kStartFreewheel");
232 SetupDriverSync(true);
233 // Drop RT only when the RT thread is actually running
234 if (fThread.GetStatus() == JackThread::kRunning) {
235 fThread.DropRealTime();
237 if (fFreewheel) {
238 fFreewheel(1, fFreewheelArg);
240 break;
242 case kStopFreewheelCallback:
243 jack_log("JackClient::kStopFreewheel");
244 SetupDriverSync(false);
245 if (fFreewheel) {
246 fFreewheel(0, fFreewheelArg);
248 // Acquire RT only when the RT thread is actually running
249 if (GetEngineControl()->fRealTime && fThread.GetStatus() == JackThread::kRunning) {
250 if (fThread.AcquireRealTime(GetEngineControl()->fClientPriority) < 0) {
251 jack_error("JackClient::AcquireRealTime error");
254 break;
256 case kPortRegistrationOnCallback:
257 jack_log("JackClient::kPortRegistrationOn port_index = %ld", value1);
258 if (fPortRegistration) {
259 fPortRegistration(value1, 1, fPortRegistrationArg);
261 break;
263 case kPortRegistrationOffCallback:
264 jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1);
265 if (fPortRegistration) {
266 fPortRegistration(value1, 0, fPortRegistrationArg);
268 break;
270 case kPortConnectCallback:
271 jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2);
272 if (fPortConnect) {
273 fPortConnect(value1, value2, 1, fPortConnectArg);
275 break;
277 case kPortDisconnectCallback:
278 jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2);
279 if (fPortConnect) {
280 fPortConnect(value1, value2, 0, fPortConnectArg);
282 break;
284 case kPortRenameCallback:
285 jack_log("JackClient::kPortRenameCallback port = %ld", value1);
286 if (fPortRename) {
287 fPortRename(value1, message, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg);
289 break;
291 case kXRunCallback:
292 jack_log("JackClient::kXRunCallback");
293 if (fXrun) {
294 res = fXrun(fXrunArg);
296 break;
298 case kShutDownCallback:
299 jack_log("JackClient::kShutDownCallback");
300 ShutDown(jack_status_t(value1), message);
301 break;
303 case kSessionCallback:
304 jack_log("JackClient::kSessionCallback");
305 if (fSession) {
306 jack_session_event_t* event = (jack_session_event_t*)malloc( sizeof(jack_session_event_t));
307 char uuid_buf[JACK_UUID_STRING_SIZE];
308 event->type = (jack_session_event_type_t)value1;
309 event->session_dir = strdup(message);
310 event->command_line = NULL;
311 event->flags = (jack_session_flags_t)0;
312 jack_uuid_unparse(GetClientControl()->fSessionID, uuid_buf);
313 event->client_uuid = strdup(uuid_buf);
314 fSessionReply = kPendingSessionReply;
315 // Session callback may change fSessionReply by directly using jack_session_reply
316 fSession(event, fSessionArg);
317 res = fSessionReply;
319 break;
321 case kLatencyCallback:
322 res = HandleLatencyCallback(value1);
323 break;
325 case kPropertyChangeCallback: {
326 jack_uuid_t subject;
327 jack_uuid_parse(name, &subject);
328 const char* key = message;
329 jack_property_change_t change = (jack_property_change_t)value1;
330 jack_log("JackClient::kPropertyChangeCallback subject = %x key = %s change = %x", subject, key, change);
331 if (fPropertyChange)
332 fPropertyChange(subject, key, change, fPropertyChangeArg);
333 break;
338 return res;
341 int JackClient::HandleLatencyCallback(int status)
343 jack_latency_callback_mode_t mode = (status == 0) ? JackCaptureLatency : JackPlaybackLatency;
344 jack_latency_range_t latency = { UINT32_MAX, 0 };
346 /* first setup all latency values of the ports.
347 * this is based on the connections of the ports.
349 list<jack_port_id_t>::iterator it;
351 for (it = fPortList.begin(); it != fPortList.end(); it++) {
352 JackPort* port = GetGraphManager()->GetPort(*it);
353 if ((port->GetFlags() & JackPortIsOutput) && (mode == JackPlaybackLatency)) {
354 GetGraphManager()->RecalculateLatency(*it, mode);
356 if ((port->GetFlags() & JackPortIsInput) && (mode == JackCaptureLatency)) {
357 GetGraphManager()->RecalculateLatency(*it, mode);
361 if (!fLatency) {
363 * default action is to assume all ports depend on each other.
364 * then always take the maximum latency.
367 if (mode == JackPlaybackLatency) {
368 /* iterate over all OutputPorts, to find maximum playback latency
370 for (it = fPortList.begin(); it != fPortList.end(); it++) {
371 JackPort* port = GetGraphManager()->GetPort(*it);
372 if (port->GetFlags() & JackPortIsOutput) {
373 jack_latency_range_t other_latency;
374 port->GetLatencyRange(mode, &other_latency);
375 if (other_latency.max > latency.max) {
376 latency.max = other_latency.max;
378 if (other_latency.min < latency.min) {
379 latency.min = other_latency.min;
384 if (latency.min == UINT32_MAX) {
385 latency.min = 0;
388 /* now set the found latency on all input ports
390 for (it = fPortList.begin(); it != fPortList.end(); it++) {
391 JackPort* port = GetGraphManager()->GetPort(*it);
392 if (port->GetFlags() & JackPortIsInput) {
393 port->SetLatencyRange(mode, &latency);
397 if (mode == JackCaptureLatency) {
398 /* iterate over all InputPorts, to find maximum playback latency
400 for (it = fPortList.begin(); it != fPortList.end(); it++) {
401 JackPort* port = GetGraphManager()->GetPort(*it);
402 if (port->GetFlags() & JackPortIsInput) {
403 jack_latency_range_t other_latency;
404 port->GetLatencyRange(mode, &other_latency);
405 if (other_latency.max > latency.max) {
406 latency.max = other_latency.max;
408 if (other_latency.min < latency.min) {
409 latency.min = other_latency.min;
414 if (latency.min == UINT32_MAX) {
415 latency.min = 0;
418 /* now set the found latency on all output ports
420 for (it = fPortList.begin(); it != fPortList.end(); it++) {
421 JackPort* port = GetGraphManager()->GetPort(*it);
422 if (port->GetFlags() & JackPortIsOutput) {
423 port->SetLatencyRange(mode, &latency);
427 return 0;
430 /* we have a latency callback setup by the client,
431 * lets use it...
433 fLatency(mode, fLatencyArg);
434 return 0;
438 \brief We need to start thread before activating in the server, otherwise the FW driver
439 connected to the client may not be activated.
441 int JackClient::Activate()
443 jack_log("JackClient::Activate");
444 if (IsActive()) {
445 return 0;
448 // RT thread is started only when needed...
449 if (IsRealTime()) {
450 if (StartThread() < 0) {
451 return -1;
456 Insertion of client in the graph will cause a kGraphOrderCallback notification
457 to be delivered by the server, the client wants to receive it.
459 GetClientControl()->fActive = true;
461 // Transport related callback become "active"
462 GetClientControl()->fTransportSync = true;
463 GetClientControl()->fTransportTimebase = true;
465 int result = -1;
466 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
467 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
468 return result;
472 \brief Need to stop thread after deactivating in the server.
474 int JackClient::Deactivate()
476 jack_log("JackClient::Deactivate");
477 if (!IsActive()) {
478 return 0;
481 GetClientControl()->fActive = false;
483 // Transport related callback become "unactive"
484 GetClientControl()->fTransportSync = false;
485 GetClientControl()->fTransportTimebase = false;
487 // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
488 int result = -1;
489 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
490 jack_log("JackClient::Deactivate res = %ld", result);
492 // RT thread is stopped only when needed...
493 if (IsRealTime()) {
494 fThread.Kill();
496 return result;
499 //----------------------
500 // RT thread management
501 //----------------------
503 void JackClient::InitAux()
505 if (fInit) {
506 jack_log("JackClient::Init calling client thread init callback");
507 fInit(fInitArg);
512 \brief Called once when the thread starts.
514 bool JackClient::Init()
517 Execute buffer_size callback.
519 Since StartThread uses fThread.StartSync, we are sure that buffer_size callback
520 is executed before StartThread returns (and then IsActive will be true).
521 So no RT callback can be called at the same time.
523 jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", GetEngineControl()->fBufferSize);
524 if (fBufferSize) {
525 fBufferSize(GetEngineControl()->fBufferSize, fBufferSizeArg);
528 // Init callback
529 InitAux();
531 // Setup context
532 if (!jack_tls_set(JackGlobals::fRealTimeThread, this)) {
533 jack_error("Failed to set thread realtime key");
536 // Setup RT
537 if (GetEngineControl()->fRealTime) {
538 set_threaded_log_function();
539 SetupRealTime();
542 return true;
545 void JackClient::SetupRealTime()
547 jack_log("JackClient::Init : period = %ld computation = %ld constraint = %ld",
548 long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
549 long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
550 long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));
552 // Will do "something" on OSX only...
553 fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
555 if (fThread.AcquireSelfRealTime(GetEngineControl()->fClientPriority) < 0) {
556 jack_error("JackClient::AcquireSelfRealTime error");
560 int JackClient::StartThread()
562 if (fThread.StartSync() < 0) {
563 jack_error("Start thread error");
564 return -1;
567 return 0;
571 \brief RT thread.
574 bool JackClient::Execute()
576 // Execute a dummy cycle to be sure thread has the correct properties
577 DummyCycle();
579 if (fThreadFun) {
580 fThreadFun(fThreadFunArg);
581 } else {
582 ExecuteThread();
584 return false;
587 void JackClient::DummyCycle()
589 WaitSync();
590 SignalSync();
593 inline void JackClient::ExecuteThread()
595 while (true) {
596 CycleWaitAux();
597 CycleSignalAux(CallProcessCallback());
601 inline jack_nframes_t JackClient::CycleWaitAux()
603 if (!WaitSync()) {
604 Error(); // Terminates the thread
606 CallSyncCallbackAux();
607 return GetEngineControl()->fBufferSize;
610 inline void JackClient::CycleSignalAux(int status)
612 if (status == 0) {
613 CallTimebaseCallbackAux();
615 SignalSync();
616 if (status != 0) {
617 End(); // Terminates the thread
621 jack_nframes_t JackClient::CycleWait()
623 return CycleWaitAux();
626 void JackClient::CycleSignal(int status)
628 CycleSignalAux(status);
631 inline int JackClient::CallProcessCallback()
633 return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0;
636 inline bool JackClient::WaitSync()
638 // Suspend itself: wait on the input synchro
639 if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
640 jack_error("SuspendRefNum error");
641 return false;
642 } else {
643 return true;
647 inline void JackClient::SignalSync()
649 // Resume: signal output clients connected to the running client
650 if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
651 jack_error("ResumeRefNum error");
655 inline void JackClient::End()
657 jack_log("JackClient::Execute end name = %s", GetClientControl()->fName);
658 // Hum... not sure about this, the following "close" code is called in the RT thread...
659 int result;
660 fThread.DropSelfRealTime();
661 GetClientControl()->fActive = false;
662 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
663 fThread.Terminate();
666 inline void JackClient::Error()
668 jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
669 // Hum... not sure about this, the following "close" code is called in the RT thread...
670 int result;
671 fThread.DropSelfRealTime();
672 GetClientControl()->fActive = false;
673 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
674 ShutDown(jack_status_t(JackFailure | JackServerError), JACK_SERVER_FAILURE);
675 fThread.Terminate();
678 //-----------------
679 // Port management
680 //-----------------
682 int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
684 // Check if port name is empty
685 string port_short_name_str = string(port_name);
686 if (port_short_name_str.size() == 0) {
687 jack_error("port_name is empty");
688 return 0; // Means failure here...
691 // Check port name length
692 string port_full_name_str = string(GetClientControl()->fName) + string(":") + port_short_name_str;
693 if (port_full_name_str.size() >= REAL_JACK_PORT_NAME_SIZE) {
694 jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
695 "Please use %lu characters or less",
696 GetClientControl()->fName,
697 port_name,
698 JACK_PORT_NAME_SIZE - 1);
699 return 0; // Means failure here...
702 int result = -1;
703 jack_port_id_t port_index = NO_PORT;
704 fChannel->PortRegister(GetClientControl()->fRefNum, port_full_name_str.c_str(), port_type, flags, buffer_size, &port_index, &result);
706 if (result == 0) {
707 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);
708 fPortList.push_back(port_index);
709 return port_index;
710 } else {
711 return 0;
715 int JackClient::PortUnRegister(jack_port_id_t port_index)
717 jack_log("JackClient::PortUnRegister port_index = %ld", port_index);
718 list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
720 if (it != fPortList.end()) {
721 fPortList.erase(it);
722 int result = -1;
723 fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
724 return result;
725 } else {
726 jack_error("unregistering a port %ld that is not own by the client", port_index);
727 return -1;
731 int JackClient::PortConnect(const char* src, const char* dst)
733 jack_log("JackClient::Connect src = %s dst = %s", src, dst);
734 if (strlen(src) >= REAL_JACK_PORT_NAME_SIZE) {
735 jack_error("\"%s\" is too long to be used as a JACK port name.\n", src);
736 return -1;
738 if (strlen(dst) >= REAL_JACK_PORT_NAME_SIZE) {
739 jack_error("\"%s\" is too long to be used as a JACK port name.\n", dst);
740 return -1;
742 int result = -1;
743 fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
744 return result;
747 int JackClient::PortDisconnect(const char* src, const char* dst)
749 jack_log("JackClient::Disconnect src = %s dst = %s", src, dst);
750 if (strlen(src) >= REAL_JACK_PORT_NAME_SIZE) {
751 jack_error("\"%s\" is too long to be used as a JACK port name.\n", src);
752 return -1;
754 if (strlen(dst) >= REAL_JACK_PORT_NAME_SIZE) {
755 jack_error("\"%s\" is too long to be used as a JACK port name.\n", dst);
756 return -1;
758 int result = -1;
759 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
760 return result;
763 int JackClient::PortDisconnect(jack_port_id_t src)
765 jack_log("JackClient::PortDisconnect src = %ld", src);
766 int result = -1;
767 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
768 return result;
771 int JackClient::PortIsMine(jack_port_id_t port_index)
773 JackPort* port = GetGraphManager()->GetPort(port_index);
774 return GetClientControl()->fRefNum == port->GetRefNum();
777 int JackClient::PortRename(jack_port_id_t port_index, const char* name)
779 int result = -1;
780 fChannel->PortRename(GetClientControl()->fRefNum, port_index, name, &result);
781 return result;
784 //--------------------
785 // Context management
786 //--------------------
788 int JackClient::SetBufferSize(jack_nframes_t buffer_size)
790 int result = -1;
791 fChannel->SetBufferSize(buffer_size, &result);
792 return result;
795 int JackClient::SetFreeWheel(int onoff)
797 int result = -1;
798 fChannel->SetFreewheel(onoff, &result);
799 return result;
802 int JackClient::ComputeTotalLatencies()
804 int result = -1;
805 fChannel->ComputeTotalLatencies(&result);
806 return result;
809 //----------------------
810 // Transport management
811 //----------------------
813 inline int JackClient::ActivateAux()
815 // If activated without RT thread...
816 if (IsActive() && fThread.GetStatus() != JackThread::kRunning) {
818 jack_log("JackClient::ActivateAux");
820 // RT thread is started
821 if (StartThread() < 0) {
822 return -1;
825 int result = -1;
826 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
827 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
828 return result;
830 } else {
831 return 0;
835 int JackClient::ReleaseTimebase()
837 int result = -1;
838 fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
839 if (result == 0) {
840 GetClientControl()->fTransportTimebase = false;
841 fTimebase = NULL;
842 fTimebaseArg = NULL;
844 return result;
847 /* Call the server if the client is active, otherwise keeps the arguments */
848 int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
850 GetClientControl()->fTransportSync = (fSync != NULL);
851 fSyncArg = arg;
852 fSync = sync_callback;
853 return ActivateAux();
856 int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
858 int result = -1;
859 fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
861 if (result == 0) {
862 GetClientControl()->fTransportTimebase = true;
863 fTimebase = timebase_callback;
864 fTimebaseArg = arg;
865 return ActivateAux();
866 } else {
867 fTimebase = NULL;
868 fTimebaseArg = NULL;
869 return result;
873 int JackClient::SetSyncTimeout(jack_time_t timeout)
875 GetEngineControl()->fTransport.SetSyncTimeout(timeout);
876 return 0;
879 // Must be RT safe
881 void JackClient::TransportLocate(jack_nframes_t frame)
883 jack_position_t pos;
884 pos.frame = frame;
885 pos.valid = (jack_position_bits_t)0;
886 jack_log("JackClient::TransportLocate pos = %ld", pos.frame);
887 GetEngineControl()->fTransport.RequestNewPos(&pos);
890 int JackClient::TransportReposition(const jack_position_t* pos)
892 jack_position_t tmp = *pos;
893 jack_log("JackClient::TransportReposition pos = %ld", pos->frame);
894 if (tmp.valid & ~JACK_POSITION_MASK) {
895 return EINVAL;
896 } else {
897 GetEngineControl()->fTransport.RequestNewPos(&tmp);
898 return 0;
902 jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
904 return GetEngineControl()->fTransport.Query(pos);
907 jack_nframes_t JackClient::GetCurrentTransportFrame()
909 return GetEngineControl()->fTransport.GetCurrentFrame();
912 // Must be RT safe: directly write in the transport shared mem
913 void JackClient::TransportStart()
915 GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
918 // Must be RT safe: directly write in the transport shared mem
919 void JackClient::TransportStop()
921 GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
924 // Never called concurrently with the server
925 // TODO check concurrency with SetSyncCallback
927 void JackClient::CallSyncCallback()
929 CallSyncCallbackAux();
932 inline void JackClient::CallSyncCallbackAux()
934 if (GetClientControl()->fTransportSync) {
936 JackTransportEngine& transport = GetEngineControl()->fTransport;
937 jack_position_t* cur_pos = transport.ReadCurrentState();
938 jack_transport_state_t transport_state = transport.GetState();
940 if (fSync != NULL) {
941 if (fSync(transport_state, cur_pos, fSyncArg)) {
942 GetClientControl()->fTransportState = JackTransportRolling;
943 GetClientControl()->fTransportSync = false;
945 } else {
946 GetClientControl()->fTransportState = JackTransportRolling;
947 GetClientControl()->fTransportSync = false;
952 void JackClient::CallTimebaseCallback()
954 CallTimebaseCallbackAux();
957 inline void JackClient::CallTimebaseCallbackAux()
959 JackTransportEngine& transport = GetEngineControl()->fTransport;
960 int master;
961 bool unused;
963 transport.GetTimebaseMaster(master, unused);
965 if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase...
967 jack_transport_state_t transport_state = transport.GetState();
968 jack_position_t* cur_pos = transport.WriteNextStateStart(1);
970 if (GetClientControl()->fTransportTimebase) {
971 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
972 GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true
973 } else if (transport_state == JackTransportRolling) {
974 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
977 transport.WriteNextStateStop(1);
981 //---------------------
982 // Callback management
983 //---------------------
985 void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
987 if (IsActive()) {
988 jack_error("You cannot set callbacks on an active client");
989 } else {
990 // Shutdown callback will either be an old API version or the new version (with info)
991 GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
992 fShutdownArg = arg;
993 fShutdown = callback;
997 void JackClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg)
999 if (IsActive()) {
1000 jack_error("You cannot set callbacks on an active client");
1001 } else {
1002 // Shutdown callback will either be an old API version or the new version (with info)
1003 GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
1004 fInfoShutdownArg = arg;
1005 fInfoShutdown = callback;
1009 int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
1011 if (IsActive()) {
1012 jack_error("You cannot set callbacks on an active client");
1013 return -1;
1014 } else if (fThreadFun) {
1015 jack_error ("A thread callback has already been setup, both models cannot be used at the same time!");
1016 return -1;
1017 } else {
1018 fProcessArg = arg;
1019 fProcess = callback;
1020 return 0;
1024 int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
1026 if (IsActive()) {
1027 jack_error("You cannot set callbacks on an active client");
1028 return -1;
1029 } else {
1030 GetClientControl()->fCallback[kXRunCallback] = (callback != NULL);
1031 fXrunArg = arg;
1032 fXrun = callback;
1033 return 0;
1037 int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
1039 if (IsActive()) {
1040 jack_error("You cannot set callbacks on an active client");
1041 return -1;
1042 } else {
1043 fInitArg = arg;
1044 fInit = callback;
1045 /* make sure that the message buffer thread is initialized too */
1046 return JackMessageBuffer::fInstance->SetInitCallback(callback, arg);
1050 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
1052 if (IsActive()) {
1053 jack_error("You cannot set callbacks on an active client");
1054 return -1;
1055 } else {
1056 GetClientControl()->fCallback[kGraphOrderCallback] = (callback != NULL);
1057 fGraphOrder = callback;
1058 fGraphOrderArg = arg;
1059 return 0;
1063 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
1065 if (IsActive()) {
1066 jack_error("You cannot set callbacks on an active client");
1067 return -1;
1068 } else {
1069 GetClientControl()->fCallback[kBufferSizeCallback] = (callback != NULL);
1070 fBufferSizeArg = arg;
1071 fBufferSize = callback;
1072 return 0;
1076 int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg)
1078 if (IsActive()) {
1079 jack_error("You cannot set callbacks on an active client");
1080 return -1;
1081 } else {
1082 GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL);
1083 fSampleRateArg = arg;
1084 fSampleRate = callback;
1085 // Now invoke it
1086 if (callback) {
1087 callback(GetEngineControl()->fSampleRate, arg);
1089 return 0;
1093 int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg)
1095 if (IsActive()) {
1096 jack_error("You cannot set callbacks on an active client");
1097 return -1;
1098 } else {
1099 // kAddClient and kRemoveClient notifications must be delivered by the server in any case
1100 fClientRegistrationArg = arg;
1101 fClientRegistration = callback;
1102 return 0;
1106 int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
1108 if (IsActive()) {
1109 jack_error("You cannot set callbacks on an active client");
1110 return -1;
1111 } else {
1112 GetClientControl()->fCallback[kStartFreewheelCallback] = (callback != NULL);
1113 GetClientControl()->fCallback[kStopFreewheelCallback] = (callback != NULL);
1114 fFreewheelArg = arg;
1115 fFreewheel = callback;
1116 return 0;
1120 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
1122 if (IsActive()) {
1123 jack_error("You cannot set callbacks on an active client");
1124 return -1;
1125 } else {
1126 GetClientControl()->fCallback[kPortRegistrationOnCallback] = (callback != NULL);
1127 GetClientControl()->fCallback[kPortRegistrationOffCallback] = (callback != NULL);
1128 fPortRegistrationArg = arg;
1129 fPortRegistration = callback;
1130 return 0;
1134 int JackClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg)
1136 if (IsActive()) {
1137 jack_error("You cannot set callbacks on an active client");
1138 return -1;
1139 } else {
1140 GetClientControl()->fCallback[kPortConnectCallback] = (callback != NULL);
1141 GetClientControl()->fCallback[kPortDisconnectCallback] = (callback != NULL);
1142 fPortConnectArg = arg;
1143 fPortConnect = callback;
1144 return 0;
1148 int JackClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg)
1150 if (IsActive()) {
1151 jack_error("You cannot set callbacks on an active client");
1152 return -1;
1153 } else {
1154 GetClientControl()->fCallback[kPortRenameCallback] = (callback != NULL);
1155 fPortRenameArg = arg;
1156 fPortRename = callback;
1157 return 0;
1161 int JackClient::SetProcessThread(JackThreadCallback fun, void *arg)
1163 if (IsActive()) {
1164 jack_error("You cannot set callbacks on an active client");
1165 return -1;
1166 } else if (fProcess) {
1167 jack_error("A process callback has already been setup, both models cannot be used at the same time!");
1168 return -1;
1169 } else {
1170 fThreadFun = fun;
1171 fThreadFunArg = arg;
1172 return 0;
1176 int JackClient::SetSessionCallback(JackSessionCallback callback, void *arg)
1178 if (IsActive()) {
1179 jack_error("You cannot set callbacks on an active client");
1180 return -1;
1181 } else {
1182 GetClientControl()->fCallback[kSessionCallback] = (callback != NULL);
1183 fSessionArg = arg;
1184 fSession = callback;
1185 return 0;
1189 int JackClient::SetLatencyCallback(JackLatencyCallback callback, void *arg)
1191 if (IsActive()) {
1192 jack_error("You cannot set callbacks on an active client");
1193 return -1;
1194 } else {
1195 // fCallback[kLatencyCallback] must always be 'true'
1196 fLatencyArg = arg;
1197 fLatency = callback;
1198 return 0;
1202 int JackClient::SetPropertyChangeCallback(JackPropertyChangeCallback callback, void *arg)
1204 if (IsActive()) {
1205 jack_error("You cannot set callbacks on an active client");
1206 return -1;
1207 } else {
1208 fPropertyChangeArg = arg;
1209 fPropertyChange = callback;
1210 return 0;
1214 //------------------
1215 // Internal clients
1216 //------------------
1218 char* JackClient::GetInternalClientName(int ref)
1220 char name_res[JACK_CLIENT_NAME_SIZE+1];
1221 int result = -1;
1222 fChannel->GetInternalClientName(GetClientControl()->fRefNum, ref, name_res, &result);
1223 return (result < 0) ? NULL : strdup(name_res);
1226 int JackClient::InternalClientHandle(const char* client_name, jack_status_t* status)
1228 int int_ref, result = -1;
1229 fChannel->InternalClientHandle(GetClientControl()->fRefNum, client_name, (int*)status, &int_ref, &result);
1230 return int_ref;
1233 int JackClient::InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va)
1235 if (strlen(client_name) >= JACK_CLIENT_NAME_SIZE) {
1236 jack_error ("\"%s\" is too long for a JACK client name.\n"
1237 "Please use %lu characters or less.",
1238 client_name, JACK_CLIENT_NAME_SIZE);
1239 return 0;
1242 if (va->load_name && (strlen(va->load_name) >= JACK_PATH_MAX)) {
1243 jack_error("\"%s\" is too long for a shared object name.\n"
1244 "Please use %lu characters or less.",
1245 va->load_name, JACK_PATH_MAX);
1246 int my_status1 = *status | (JackFailure | JackInvalidOption);
1247 *status = (jack_status_t)my_status1;
1248 return 0;
1251 if (va->load_init && (strlen(va->load_init) >= JACK_LOAD_INIT_LIMIT)) {
1252 jack_error ("\"%s\" is too long for internal client init "
1253 "string.\nPlease use %lu characters or less.",
1254 va->load_init, JACK_LOAD_INIT_LIMIT);
1255 int my_status1 = *status | (JackFailure | JackInvalidOption);
1256 *status = (jack_status_t)my_status1;
1257 return 0;
1260 int int_ref, result = -1;
1261 fChannel->InternalClientLoad(GetClientControl()->fRefNum, client_name, va->load_name, va->load_init, options, (int*)status, &int_ref, -1, &result);
1262 return int_ref;
1265 void JackClient::InternalClientUnload(int ref, jack_status_t* status)
1267 int result = -1;
1268 fChannel->InternalClientUnload(GetClientControl()->fRefNum, ref, (int*)status, &result);
1271 //------------------
1272 // Session API
1273 //------------------
1275 jack_session_command_t* JackClient::SessionNotify(const char* target, jack_session_event_type_t type, const char* path)
1277 jack_session_command_t* res;
1278 fChannel->SessionNotify(GetClientControl()->fRefNum, target, type, path, &res);
1279 return res;
1282 int JackClient::SessionReply(jack_session_event_t* ev)
1284 if (ev->command_line) {
1285 strncpy(GetClientControl()->fSessionCommand, ev->command_line, sizeof(GetClientControl()->fSessionCommand));
1286 } else {
1287 GetClientControl()->fSessionCommand[0] = '\0';
1290 GetClientControl()->fSessionFlags = ev->flags;
1292 jack_log("JackClient::SessionReply... we are here");
1293 if (fChannel->IsChannelThread()) {
1294 jack_log("JackClient::SessionReply... in callback reply");
1295 // OK, immediate reply...
1296 fSessionReply = kImmediateSessionReply;
1297 return 0;
1300 jack_log("JackClient::SessionReply... out of cb");
1302 int result = -1;
1303 fChannel->SessionReply(GetClientControl()->fRefNum, &result);
1304 return result;
1307 char* JackClient::GetUUIDForClientName(const char* client_name)
1309 char uuid_res[JACK_UUID_STRING_SIZE];
1310 int result = -1;
1311 fChannel->GetUUIDForClientName(GetClientControl()->fRefNum, client_name, uuid_res, &result);
1312 return (result) ? NULL : strdup(uuid_res);
1315 char* JackClient::GetClientNameByUUID(const char* uuid)
1317 char name_res[JACK_CLIENT_NAME_SIZE + 1];
1318 int result = -1;
1319 fChannel->GetClientNameForUUID(GetClientControl()->fRefNum, uuid, name_res, &result);
1320 return (result) ? NULL : strdup(name_res);
1323 int JackClient::ReserveClientName(const char* client_name, const char* uuid)
1325 int result = -1;
1326 fChannel->ReserveClientName( GetClientControl()->fRefNum, client_name, uuid, &result);
1327 return result;
1330 int JackClient::ClientHasSessionCallback(const char* client_name)
1332 int result = -1;
1333 fChannel->ClientHasSessionCallback(client_name, &result);
1334 return result;
1337 //------------------
1338 // Metadata API
1339 //------------------
1341 int JackClient::PropertyChangeNotify(jack_uuid_t subject, const char* key, jack_property_change_t change)
1343 int result = -1;
1344 fChannel->PropertyChangeNotify(subject, key, change, &result);
1345 return result;
1349 } // end of namespace