Update OSX install script.
[jack2.git] / common / JackClient.cpp
blob75beefbc41a564298aba955b0f1975dff9aaa17e
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 "JackClient.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():fThread(this)
45 JackClient::JackClient(JackSynchro* table):fThread(this)
47 fSynchroTable = table;
48 fProcess = NULL;
49 fGraphOrder = NULL;
50 fXrun = NULL;
51 fShutdown = NULL;
52 fInit = NULL;
53 fBufferSize = NULL;
54 fClientRegistration = NULL;
55 fFreewheel = NULL;
56 fPortRegistration = NULL;
57 fPortConnect = NULL;
58 fPortRename = NULL;
59 fTimebase = NULL;
60 fSync = NULL;
61 fThreadFun = NULL;
62 fProcessArg = NULL;
63 fGraphOrderArg = NULL;
64 fXrunArg = NULL;
65 fShutdownArg = NULL;
66 fInitArg = NULL;
67 fBufferSizeArg = NULL;
68 fFreewheelArg = NULL;
69 fClientRegistrationArg = NULL;
70 fPortRegistrationArg = NULL;
71 fPortConnectArg = NULL;
72 fPortRenameArg = NULL;
73 fSyncArg = NULL;
74 fTimebaseArg = NULL;
75 fThreadFunArg = NULL;
78 JackClient::~JackClient()
81 int JackClient::Close()
83 jack_log("JackClient::Close ref = %ld", GetClientControl()->fRefNum);
84 int result = 0;
86 Deactivate();
87 fChannel->Stop(); // Channels is stopped first to avoid receiving notifications while closing
89 // Request close only if server is still running
90 if (JackGlobals::fServerRunning) {
91 fChannel->ClientClose(GetClientControl()->fRefNum, &result);
92 } else {
93 jack_log("JackClient::Close server is shutdown");
96 fChannel->Close();
97 fSynchroTable[GetClientControl()->fRefNum].Disconnect();
98 JackGlobals::fClientTable[GetClientControl()->fRefNum] = NULL;
99 return result;
102 bool JackClient::IsActive()
104 return (GetClientControl()) ? GetClientControl()->fActive : false;
107 pthread_t JackClient::GetThreadID()
109 return fThread.GetThreadID();
113 In "async" mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations.
114 The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations.
115 Drivers synchro are setup in "flush" mode if server is "async" and NOT freewheel.
117 void JackClient::SetupDriverSync(bool freewheel)
119 if (!freewheel && !GetEngineControl()->fSyncMode) {
120 jack_log("JackClient::SetupDriverSync driver sem in flush mode");
121 for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
122 fSynchroTable[i].SetFlush(true);
124 } else {
125 jack_log("JackClient::SetupDriverSync driver sem in normal mode");
126 for (int i = 0; i < GetEngineControl()->fDriverNum; i++)
127 fSynchroTable[i].SetFlush(false);
132 \brief Notification received from the server.
135 int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, int value1, int value2)
137 return 0;
140 int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, int value1, int value2)
142 int res = 0;
144 // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
145 switch (notify) {
147 case kAddClient:
148 res = ClientNotifyImp(refnum, name, notify, sync, value1, value2);
149 break;
151 case kRemoveClient:
152 res = ClientNotifyImp(refnum, name, notify, sync, value1, value2);
153 break;
155 case kActivateClient:
156 jack_log("JackClient::kActivateClient name = %s ref = %ld ", name, refnum);
157 Init();
158 break;
162 The current semantic is that notifications can only be received when the client has been activated,
163 although is this implementation, one could imagine calling notifications as soon as the client has be opened.
165 if (IsActive()) {
167 switch (notify) {
169 case kAddClient:
170 jack_log("JackClient::kAddClient fName = %s name = %s", GetClientControl()->fName, name);
171 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) // Don't call the callback for the registering client itself
172 fClientRegistration(name, 1, fClientRegistrationArg);
173 break;
175 case kRemoveClient:
176 jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name);
177 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) // Don't call the callback for the registering client itself
178 fClientRegistration(name, 0, fClientRegistrationArg);
179 break;
181 case kBufferSizeCallback:
182 jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1);
183 if (fBufferSize)
184 res = fBufferSize(value1, fBufferSizeArg);
185 break;
187 case kSampleRateCallback:
188 jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1);
189 if (fSampleRate)
190 res = fSampleRate(value1, fSampleRateArg);
191 break;
193 case kGraphOrderCallback:
194 jack_log("JackClient::kGraphOrderCallback");
195 if (fGraphOrder)
196 res = fGraphOrder(fGraphOrderArg);
197 break;
199 case kStartFreewheelCallback:
200 jack_log("JackClient::kStartFreewheel");
201 SetupDriverSync(true);
202 fThread.DropRealTime();
203 if (fFreewheel)
204 fFreewheel(1, fFreewheelArg);
205 break;
207 case kStopFreewheelCallback:
208 jack_log("JackClient::kStopFreewheel");
209 SetupDriverSync(false);
210 if (fFreewheel)
211 fFreewheel(0, fFreewheelArg);
212 fThread.AcquireRealTime();
213 break;
215 case kPortRegistrationOnCallback:
216 jack_log("JackClient::kPortRegistrationOn port_index = %ld", value1);
217 if (fPortRegistration)
218 fPortRegistration(value1, 1, fPortRegistrationArg);
219 break;
221 case kPortRegistrationOffCallback:
222 jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1);
223 if (fPortRegistration)
224 fPortRegistration(value1, 0, fPortRegistrationArg);
225 break;
227 case kPortConnectCallback:
228 jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2);
229 if (fPortConnect)
230 fPortConnect(value1, value2, 1, fPortConnectArg);
231 break;
233 case kPortDisconnectCallback:
234 jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2);
235 if (fPortConnect)
236 fPortConnect(value1, value2, 0, fPortConnectArg);
237 break;
239 case kPortRenameCallback:
240 jack_log("JackClient::kPortRenameCallback port = %ld");
241 if (fPortRename)
242 fPortRename(value1, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg);
243 break;
245 case kXRunCallback:
246 jack_log("JackClient::kXRunCallback");
247 if (fXrun)
248 res = fXrun(fXrunArg);
249 break;
253 return res;
257 \brief We need to start thread before activating in the server, otherwise the FW driver
258 connected to the client may not be activated.
260 int JackClient::Activate()
262 jack_log("JackClient::Activate");
263 if (IsActive())
264 return 0;
266 // RT thread is started only when needed...
267 if (IsRealTime()) {
268 if (StartThread() < 0)
269 return -1;
273 Insertion of client in the graph will cause a kGraphOrderCallback notification
274 to be delivered by the server, the client wants to receive it.
276 GetClientControl()->fActive = true;
278 // Transport related callback become "active"
279 GetClientControl()->fTransportSync = true;
280 GetClientControl()->fTransportTimebase = true;
282 int result = -1;
283 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
284 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
285 return result;
289 \brief Need to stop thread after deactivating in the server.
291 int JackClient::Deactivate()
293 jack_log("JackClient::Deactivate");
294 if (!IsActive())
295 return 0;
297 GetClientControl()->fActive = false;
299 // Transport related callback become "unactive"
300 GetClientControl()->fTransportSync = false;
301 GetClientControl()->fTransportTimebase = false;
303 // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
304 int result = -1;
305 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
306 jack_log("JackClient::Deactivate res = %ld", result);
308 // RT thread is stopped only when needed...
309 if (IsRealTime())
310 fThread.Kill();
311 return result;
314 //----------------------
315 // RT thread management
316 //----------------------
319 \brief Called once when the thread starts.
321 bool JackClient::Init()
323 if (fInit) {
324 jack_log("JackClient::Init calling client thread init callback");
325 fInit(fInitArg);
327 return true;
330 int JackClient::StartThread()
332 jack_log("JackClient::StartThread : period = %ld computation = %ld constraint = %ld",
333 long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
334 long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
335 long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));
337 // Will do "something" on OSX only...
338 fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
340 if (fThread.StartSync() < 0) {
341 jack_error("Start thread error");
342 return -1;
345 if (GetEngineControl()->fRealTime) {
346 if (fThread.AcquireRealTime(GetEngineControl()->fClientPriority) < 0) {
347 jack_error("AcquireRealTime error");
351 return 0;
355 \brief RT thread.
358 bool JackClient::Execute()
360 if (!jack_tls_set(JackGlobals::fRealTime, this))
361 jack_error("failed to set thread realtime key");
363 if (GetEngineControl()->fRealTime)
364 set_threaded_log_function();
366 // Execute a dummy cycle to be sure thread has the correct properties
367 DummyCycle();
369 if (fThreadFun) {
370 fThreadFun(fThreadFunArg);
371 } else {
372 ExecuteThread();
374 return false;
377 void JackClient::DummyCycle()
379 WaitSync();
380 SignalSync();
383 inline void JackClient::ExecuteThread()
385 while (true) {
386 CycleWaitAux();
387 CycleSignalAux(CallProcessCallback());
391 inline jack_nframes_t JackClient::CycleWaitAux()
393 if (!WaitSync())
394 Error(); // Terminates the thread
395 CallSyncCallbackAux();
396 return GetEngineControl()->fBufferSize;
399 inline void JackClient::CycleSignalAux(int status)
401 if (status == 0)
402 CallTimebaseCallbackAux();
403 SignalSync();
404 if (status != 0)
405 End(); // Terminates the thread
408 jack_nframes_t JackClient::CycleWait()
410 return CycleWaitAux();
413 void JackClient::CycleSignal(int status)
415 CycleSignalAux(status);
418 inline int JackClient::CallProcessCallback()
420 return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0;
423 inline bool JackClient::WaitSync()
425 // Suspend itself: wait on the input synchro
426 if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
427 jack_error("SuspendRefNum error");
428 return false;
429 } else {
430 return true;
434 inline void JackClient::SignalSync()
436 // Resume: signal output clients connected to the running client
437 if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
438 jack_error("ResumeRefNum error");
442 inline void JackClient::End()
444 jack_log("JackClient::Execute end name = %s", GetClientControl()->fName);
445 // Hum... not sure about this, the following "close" code is called in the RT thread...
446 int result;
447 fThread.DropRealTime();
448 GetClientControl()->fActive = false;
449 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
450 fThread.Terminate();
453 inline void JackClient::Error()
455 jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
456 // Hum... not sure about this, the following "close" code is called in the RT thread...
457 int result;
458 fThread.DropRealTime();
459 GetClientControl()->fActive = false;
460 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
461 ShutDown();
462 fThread.Terminate();
465 //-----------------
466 // Port management
467 //-----------------
469 int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
471 // Check if port name is empty
472 string port_name_str = string(port_name);
473 if (port_name_str.size() == 0) {
474 jack_error("port_name is empty");
475 return 0; // Means failure here...
478 // Check port name length
479 string name = string(GetClientControl()->fName) + string(":") + port_name_str;
480 if (name.size() >= JACK_PORT_NAME_SIZE) {
481 jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
482 "Please use %lu characters or less",
483 GetClientControl()->fName,
484 port_name,
485 JACK_PORT_NAME_SIZE - 1);
486 return 0; // Means failure here...
489 int result = -1;
490 jack_port_id_t port_index = NO_PORT;
491 fChannel->PortRegister(GetClientControl()->fRefNum, name.c_str(), port_type, flags, buffer_size, &port_index, &result);
493 if (result == 0) {
494 jack_log("JackClient::PortRegister ref = %ld name = %s type = %s port_index = %ld", GetClientControl()->fRefNum, name.c_str(), port_type, port_index);
495 fPortList.push_back(port_index);
496 return port_index;
497 } else {
498 return 0;
502 int JackClient::PortUnRegister(jack_port_id_t port_index)
504 jack_log("JackClient::PortUnRegister port_index = %ld", port_index);
505 list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
507 if (it != fPortList.end()) {
508 fPortList.erase(it);
509 int result = -1;
510 fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
511 return result;
512 } else {
513 jack_error("unregistering a port %ld that is not own by the client", port_index);
514 return -1;
518 int JackClient::PortConnect(const char* src, const char* dst)
520 jack_log("JackClient::Connect src = %s dst = %s", src, dst);
521 int result = -1;
522 fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
523 return result;
526 int JackClient::PortDisconnect(const char* src, const char* dst)
528 jack_log("JackClient::Disconnect src = %s dst = %s", src, dst);
529 int result = -1;
530 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
531 return result;
534 int JackClient::PortDisconnect(jack_port_id_t src)
536 jack_log("JackClient::PortDisconnect src = %ld", src);
537 int result = -1;
538 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
539 return result;
542 int JackClient::PortIsMine(jack_port_id_t port_index)
544 JackPort* port = GetGraphManager()->GetPort(port_index);
545 return GetClientControl()->fRefNum == port->GetRefNum();
548 int JackClient::PortRename(jack_port_id_t port_index, const char* name)
550 int result = -1;
551 fChannel->PortRename(GetClientControl()->fRefNum, port_index, name, &result);
552 return result;
555 //--------------------
556 // Context management
557 //--------------------
559 int JackClient::SetBufferSize(jack_nframes_t buffer_size)
561 int result = -1;
562 fChannel->SetBufferSize(buffer_size, &result);
563 return result;
566 int JackClient::SetFreeWheel(int onoff)
568 int result = -1;
569 fChannel->SetFreewheel(onoff, &result);
570 return result;
574 ShutDown is called:
575 - from the RT thread when Execute method fails
576 - possibly from a "closed" notification channel
577 (Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown))
580 void JackClient::ShutDown()
582 jack_log("ShutDown");
583 JackGlobals::fServerRunning = false;
585 if (fShutdown) {
586 fShutdown(fShutdownArg);
587 fShutdown = NULL;
591 //----------------------
592 // Transport management
593 //----------------------
595 inline int JackClient::ActivateAux()
597 // If activated without RT thread...
598 if (IsActive() && fThread.GetStatus() != JackThread::kRunning) {
600 jack_log("ActivateAux");
602 // RT thread is started
603 if (StartThread() < 0)
604 return -1;
606 int result = -1;
607 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
608 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
609 return result;
611 } else {
612 return 0;
616 int JackClient::ReleaseTimebase()
618 int result = -1;
619 fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
620 if (result == 0) {
621 GetClientControl()->fTransportTimebase = false;
622 fTimebase = NULL;
623 fTimebaseArg = NULL;
625 return result;
628 /* Call the server if the client is active, otherwise keeps the arguments */
629 int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
631 GetClientControl()->fTransportSync = (fSync != NULL);
632 fSyncArg = arg;
633 fSync = sync_callback;
634 return ActivateAux();
637 int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
639 int result = -1;
640 fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
642 if (result == 0) {
643 GetClientControl()->fTransportTimebase = true;
644 fTimebase = timebase_callback;
645 fTimebaseArg = arg;
646 return ActivateAux();
647 } else {
648 fTimebase = NULL;
649 fTimebaseArg = NULL;
650 return -1;
654 int JackClient::SetSyncTimeout(jack_time_t timeout)
656 GetEngineControl()->fTransport.SetSyncTimeout(timeout);
657 return 0;
660 // Must be RT safe
662 void JackClient::TransportLocate(jack_nframes_t frame)
664 jack_position_t pos;
665 pos.frame = frame;
666 pos.valid = (jack_position_bits_t)0;
667 jack_log("TransportLocate pos = %ld", pos.frame);
668 GetEngineControl()->fTransport.RequestNewPos(&pos);
671 int JackClient::TransportReposition(jack_position_t* pos)
673 jack_position_t tmp = *pos;
674 jack_log("TransportReposition pos = %ld", pos->frame);
675 if (tmp.valid & ~JACK_POSITION_MASK) {
676 return EINVAL;
677 } else {
678 GetEngineControl()->fTransport.RequestNewPos(pos);
679 return 0;
683 jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
685 return GetEngineControl()->fTransport.Query(pos);
688 jack_nframes_t JackClient::GetCurrentTransportFrame()
690 return GetEngineControl()->fTransport.GetCurrentFrame();
693 // Must be RT safe: directly write in the transport shared mem
694 void JackClient::TransportStart()
696 GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
699 // Must be RT safe: directly write in the transport shared mem
700 void JackClient::TransportStop()
702 GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
705 // Never called concurently with the server
706 // TODO check concurrency with SetSyncCallback
708 void JackClient::CallSyncCallback()
710 CallSyncCallbackAux();
713 inline void JackClient::CallSyncCallbackAux()
715 if (GetClientControl()->fTransportSync) {
717 JackTransportEngine& transport = GetEngineControl()->fTransport;
718 jack_position_t* cur_pos = transport.ReadCurrentState();
719 jack_transport_state_t transport_state = transport.GetState();
721 if (fSync != NULL) {
722 if (fSync(transport_state, cur_pos, fSyncArg)) {
723 GetClientControl()->fTransportState = JackTransportRolling;
724 GetClientControl()->fTransportSync = false;
726 } else {
727 GetClientControl()->fTransportState = JackTransportRolling;
728 GetClientControl()->fTransportSync = false;
733 void JackClient::CallTimebaseCallback()
735 CallTimebaseCallbackAux();
738 inline void JackClient::CallTimebaseCallbackAux()
740 JackTransportEngine& transport = GetEngineControl()->fTransport;
741 int master;
742 bool unused;
744 transport.GetTimebaseMaster(master, unused);
746 if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase...
748 jack_transport_state_t transport_state = transport.GetState();
749 jack_position_t* cur_pos = transport.WriteNextStateStart(1);
751 if (GetClientControl()->fTransportTimebase) {
752 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
753 GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true
754 } else if (transport_state == JackTransportRolling) {
755 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
758 transport.WriteNextStateStop(1);
762 //---------------------
763 // Callback management
764 //---------------------
766 void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
768 if (IsActive()) {
769 jack_error("You cannot set callbacks on an active client");
770 } else {
771 fShutdownArg = arg;
772 fShutdown = callback;
776 int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
778 if (IsActive()) {
779 jack_error("You cannot set callbacks on an active client");
780 return -1;
781 } else if (fThreadFun) {
782 jack_error ("A thread callback has already been setup, both models cannot be used at the same time!");
783 return -1;
784 } else {
785 fProcessArg = arg;
786 fProcess = callback;
787 return 0;
791 int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
793 if (IsActive()) {
794 jack_error("You cannot set callbacks on an active client");
795 return -1;
796 } else {
797 GetClientControl()->fCallback[kXRunCallback] = (callback != NULL);
798 fXrunArg = arg;
799 fXrun = callback;
800 return 0;
804 int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
806 if (IsActive()) {
807 jack_error("You cannot set callbacks on an active client");
808 return -1;
809 } else {
810 fInitArg = arg;
811 fInit = callback;
812 return 0;
816 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
818 jack_log("SetGraphOrderCallback ");
820 if (IsActive()) {
821 jack_error("You cannot set callbacks on an active client");
822 return -1;
823 } else {
824 GetClientControl()->fCallback[kGraphOrderCallback] = (callback != NULL);
825 fGraphOrder = callback;
826 fGraphOrderArg = arg;
827 return 0;
831 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
833 if (IsActive()) {
834 jack_error("You cannot set callbacks on an active client");
835 return -1;
836 } else {
837 GetClientControl()->fCallback[kBufferSizeCallback] = (callback != NULL);
838 fBufferSizeArg = arg;
839 fBufferSize = callback;
840 return 0;
844 int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg)
846 if (IsActive()) {
847 jack_error("You cannot set callbacks on an active client");
848 return -1;
849 } else {
850 GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL);
851 fSampleRateArg = arg;
852 fSampleRate = callback;
853 return 0;
857 int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg)
859 if (IsActive()) {
860 jack_error("You cannot set callbacks on an active client");
861 return -1;
862 } else {
863 // kAddClient and kRemoveClient notifications must be delivered by the server in any case
864 fClientRegistrationArg = arg;
865 fClientRegistration = callback;
866 return 0;
870 int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
872 if (IsActive()) {
873 jack_error("You cannot set callbacks on an active client");
874 return -1;
875 } else {
876 GetClientControl()->fCallback[kStartFreewheelCallback] = (callback != NULL);
877 GetClientControl()->fCallback[kStopFreewheelCallback] = (callback != NULL);
878 fFreewheelArg = arg;
879 fFreewheel = callback;
880 return 0;
884 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
886 if (IsActive()) {
887 jack_error("You cannot set callbacks on an active client");
888 return -1;
889 } else {
890 GetClientControl()->fCallback[kPortRegistrationOnCallback] = (callback != NULL);
891 GetClientControl()->fCallback[kPortRegistrationOffCallback] = (callback != NULL);
892 fPortRegistrationArg = arg;
893 fPortRegistration = callback;
894 return 0;
898 int JackClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg)
900 if (IsActive()) {
901 jack_error("You cannot set callbacks on an active client");
902 return -1;
903 } else {
904 GetClientControl()->fCallback[kPortConnectCallback] = (callback != NULL);
905 GetClientControl()->fCallback[kPortDisconnectCallback] = (callback != NULL);
906 fPortConnectArg = arg;
907 fPortConnect = callback;
908 return 0;
912 int JackClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg)
914 if (IsActive()) {
915 jack_error("You cannot set callbacks on an active client");
916 return -1;
917 } else {
918 GetClientControl()->fCallback[kPortRenameCallback] = (callback != NULL);
919 fPortRenameArg = arg;
920 fPortRename = callback;
921 return 0;
925 int JackClient::SetProcessThread(JackThreadCallback fun, void *arg)
927 if (IsActive()) {
928 jack_error("You cannot set callbacks on an active client");
929 return -1;
930 } else if (fProcess) {
931 jack_error ("A process callback has already been setup, both models cannot be used at the same time!");
932 return -1;
933 } else {
934 fThreadFun = fun;
935 fThreadFunArg = arg;
936 return 0;
940 //------------------
941 // Internal clients
942 //------------------
944 char* JackClient::GetInternalClientName(int ref)
946 char name_res[JACK_CLIENT_NAME_SIZE + 1];
947 int result = -1;
948 fChannel->GetInternalClientName(GetClientControl()->fRefNum, ref, name_res, &result);
950 if (result < 0) {
951 return NULL;
952 } else {
953 char* name = (char*)malloc(strlen(name_res));
954 strcpy(name, name_res);
955 return name;
959 int JackClient::InternalClientHandle(const char* client_name, jack_status_t* status)
961 int int_ref, result = -1;
962 fChannel->InternalClientHandle(GetClientControl()->fRefNum, client_name, (int*)status, &int_ref, &result);
963 return int_ref;
966 int JackClient::InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va)
968 if (strlen(client_name) >= JACK_CLIENT_NAME_SIZE) {
969 jack_error ("\"%s\" is too long for a JACK client name.\n"
970 "Please use %lu characters or less.",
971 client_name, JACK_CLIENT_NAME_SIZE);
972 return 0;
975 if (va->load_name && (strlen(va->load_name) >= JACK_PATH_MAX)) {
976 jack_error("\"%s\" is too long for a shared object name.\n"
977 "Please use %lu characters or less.",
978 va->load_name, PATH_MAX);
979 int my_status1 = *status | (JackFailure | JackInvalidOption);
980 *status = (jack_status_t)my_status1;
981 return 0;
984 if (va->load_init && (strlen(va->load_init) >= JACK_LOAD_INIT_LIMIT)) {
985 jack_error ("\"%s\" is too long for internal client init "
986 "string.\nPlease use %lu characters or less.",
987 va->load_init, JACK_LOAD_INIT_LIMIT);
988 int my_status1 = *status | (JackFailure | JackInvalidOption);
989 *status = (jack_status_t)my_status1;
990 return 0;
993 int int_ref, result = -1;
994 fChannel->InternalClientLoad(GetClientControl()->fRefNum, client_name, va->load_name, va->load_init, options, (int*)status, &int_ref, &result);
995 return int_ref;
998 void JackClient::InternalClientUnload(int ref, jack_status_t* status)
1000 int result = -1;
1001 fChannel->InternalClientUnload(GetClientControl()->fRefNum, ref, (int*)status, &result);
1005 } // end of namespace