Compiles on Windows again.
[jack2.git] / common / JackClient.cpp
blobe6469267ba690c03c3d570d69a89465941894055
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 fInfoShutdown = NULL;
53 fInit = NULL;
54 fBufferSize = NULL;
55 fClientRegistration = NULL;
56 fFreewheel = NULL;
57 fPortRegistration = NULL;
58 fPortConnect = NULL;
59 fPortRename = NULL;
60 fTimebase = NULL;
61 fSync = NULL;
62 fThreadFun = NULL;
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;
80 JackClient::~JackClient()
83 int JackClient::Close()
85 jack_log("JackClient::Close ref = %ld", GetClientControl()->fRefNum);
86 int result = 0;
88 Deactivate();
89 fChannel->Stop(); // Channels is stopped first to avoid receiving notifications while closing
91 // Request close only if server is still running
92 if (JackGlobals::fServerRunning) {
93 fChannel->ClientClose(GetClientControl()->fRefNum, &result);
94 } else {
95 jack_log("JackClient::Close server is shutdown");
98 fChannel->Close();
99 fSynchroTable[GetClientControl()->fRefNum].Disconnect();
100 JackGlobals::fClientTable[GetClientControl()->fRefNum] = NULL;
101 return result;
104 bool JackClient::IsActive()
106 return (GetClientControl()) ? GetClientControl()->fActive : false;
109 pthread_t JackClient::GetThreadID()
111 return fThread.GetThreadID();
115 In "async" mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations.
116 The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations.
117 Drivers synchro are setup in "flush" mode if server is "async" and NOT freewheel.
119 void JackClient::SetupDriverSync(bool freewheel)
121 if (!freewheel && !GetEngineControl()->fSyncMode) {
122 jack_log("JackClient::SetupDriverSync driver sem in flush mode");
123 for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
124 fSynchroTable[i].SetFlush(true);
126 } else {
127 jack_log("JackClient::SetupDriverSync driver sem in normal mode");
128 for (int i = 0; i < GetEngineControl()->fDriverNum; i++)
129 fSynchroTable[i].SetFlush(false);
134 \brief Notification received from the server.
137 int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
139 return 0;
142 int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
144 int res = 0;
146 // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
147 switch (notify) {
149 case kAddClient:
150 res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
151 break;
153 case kRemoveClient:
154 res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
155 break;
157 case kActivateClient:
158 jack_log("JackClient::kActivateClient name = %s ref = %ld ", name, refnum);
159 Init();
160 break;
164 The current semantic is that notifications can only be received when the client has been activated,
165 although is this implementation, one could imagine calling notifications as soon as the client has be opened.
167 if (IsActive()) {
169 switch (notify) {
171 case kAddClient:
172 jack_log("JackClient::kAddClient fName = %s name = %s", GetClientControl()->fName, name);
173 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
174 fClientRegistration(name, 1, fClientRegistrationArg);
176 break;
178 case kRemoveClient:
179 jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name);
180 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
181 fClientRegistration(name, 0, fClientRegistrationArg);
183 break;
185 case kBufferSizeCallback:
186 jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1);
187 if (fBufferSize) {
188 res = fBufferSize(value1, fBufferSizeArg);
190 break;
192 case kSampleRateCallback:
193 jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1);
194 if (fSampleRate) {
195 res = fSampleRate(value1, fSampleRateArg);
197 break;
199 case kGraphOrderCallback:
200 jack_log("JackClient::kGraphOrderCallback");
201 if (fGraphOrder) {
202 res = fGraphOrder(fGraphOrderArg);
204 break;
206 case kStartFreewheelCallback:
207 jack_log("JackClient::kStartFreewheel");
208 SetupDriverSync(true);
209 fThread.DropRealTime(); // Always done (JACK server in RT mode or not...)
210 if (fFreewheel) {
211 fFreewheel(1, fFreewheelArg);
213 break;
215 case kStopFreewheelCallback:
216 jack_log("JackClient::kStopFreewheel");
217 SetupDriverSync(false);
218 if (fFreewheel) {
219 fFreewheel(0, fFreewheelArg);
221 if (GetEngineControl()->fRealTime) {
222 fThread.AcquireRealTime();
224 break;
226 case kPortRegistrationOnCallback:
227 jack_log("JackClient::kPortRegistrationOn port_index = %ld", value1);
228 if (fPortRegistration) {
229 fPortRegistration(value1, 1, fPortRegistrationArg);
231 break;
233 case kPortRegistrationOffCallback:
234 jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1);
235 if (fPortRegistration) {
236 fPortRegistration(value1, 0, fPortRegistrationArg);
238 break;
240 case kPortConnectCallback:
241 jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2);
242 if (fPortConnect) {
243 fPortConnect(value1, value2, 1, fPortConnectArg);
245 break;
247 case kPortDisconnectCallback:
248 jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2);
249 if (fPortConnect) {
250 fPortConnect(value1, value2, 0, fPortConnectArg);
252 break;
254 case kPortRenameCallback:
255 jack_log("JackClient::kPortRenameCallback port = %ld");
256 if (fPortRename) {
257 fPortRename(value1, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg);
259 break;
261 case kXRunCallback:
262 jack_log("JackClient::kXRunCallback");
263 if (fXrun) {
264 res = fXrun(fXrunArg);
266 break;
268 case kShutDownCallback:
269 jack_log("JackClient::kShutDownCallback");
270 if (fInfoShutdown) {
271 fInfoShutdown((jack_status_t)value1, message, fInfoShutdownArg);
272 fInfoShutdown = NULL;
274 break;
278 return res;
282 \brief We need to start thread before activating in the server, otherwise the FW driver
283 connected to the client may not be activated.
285 int JackClient::Activate()
287 jack_log("JackClient::Activate");
288 if (IsActive())
289 return 0;
291 // RT thread is started only when needed...
292 if (IsRealTime()) {
293 if (StartThread() < 0)
294 return -1;
298 Insertion of client in the graph will cause a kGraphOrderCallback notification
299 to be delivered by the server, the client wants to receive it.
301 GetClientControl()->fActive = true;
303 // Transport related callback become "active"
304 GetClientControl()->fTransportSync = true;
305 GetClientControl()->fTransportTimebase = true;
307 int result = -1;
308 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
309 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
310 return result;
314 \brief Need to stop thread after deactivating in the server.
316 int JackClient::Deactivate()
318 jack_log("JackClient::Deactivate");
319 if (!IsActive())
320 return 0;
322 GetClientControl()->fActive = false;
324 // Transport related callback become "unactive"
325 GetClientControl()->fTransportSync = false;
326 GetClientControl()->fTransportTimebase = false;
328 // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
329 int result = -1;
330 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
331 jack_log("JackClient::Deactivate res = %ld", result);
333 // RT thread is stopped only when needed...
334 if (IsRealTime())
335 fThread.Kill();
336 return result;
339 //----------------------
340 // RT thread management
341 //----------------------
344 \brief Called once when the thread starts.
346 bool JackClient::Init()
348 if (fInit) {
349 jack_log("JackClient::Init calling client thread init callback");
350 fInit(fInitArg);
352 return true;
355 int JackClient::StartThread()
357 jack_log("JackClient::StartThread : period = %ld computation = %ld constraint = %ld",
358 long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
359 long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
360 long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));
362 // Will do "something" on OSX only...
363 fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
365 if (fThread.StartSync() < 0) {
366 jack_error("Start thread error");
367 return -1;
370 if (GetEngineControl()->fRealTime) {
371 if (fThread.AcquireRealTime(GetEngineControl()->fClientPriority) < 0) {
372 jack_error("AcquireRealTime error");
376 return 0;
380 \brief RT thread.
383 bool JackClient::Execute()
385 if (!jack_tls_set(JackGlobals::fRealTime, this))
386 jack_error("failed to set thread realtime key");
388 if (GetEngineControl()->fRealTime)
389 set_threaded_log_function();
391 // Execute a dummy cycle to be sure thread has the correct properties
392 DummyCycle();
394 if (fThreadFun) {
395 fThreadFun(fThreadFunArg);
396 } else {
397 ExecuteThread();
399 return false;
402 void JackClient::DummyCycle()
404 WaitSync();
405 SignalSync();
408 inline void JackClient::ExecuteThread()
410 while (true) {
411 CycleWaitAux();
412 CycleSignalAux(CallProcessCallback());
416 inline jack_nframes_t JackClient::CycleWaitAux()
418 if (!WaitSync())
419 Error(); // Terminates the thread
420 CallSyncCallbackAux();
421 return GetEngineControl()->fBufferSize;
424 inline void JackClient::CycleSignalAux(int status)
426 if (status == 0)
427 CallTimebaseCallbackAux();
428 SignalSync();
429 if (status != 0)
430 End(); // Terminates the thread
433 jack_nframes_t JackClient::CycleWait()
435 return CycleWaitAux();
438 void JackClient::CycleSignal(int status)
440 CycleSignalAux(status);
443 inline int JackClient::CallProcessCallback()
445 return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0;
448 inline bool JackClient::WaitSync()
450 // Suspend itself: wait on the input synchro
451 if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
452 jack_error("SuspendRefNum error");
453 return false;
454 } else {
455 return true;
459 inline void JackClient::SignalSync()
461 // Resume: signal output clients connected to the running client
462 if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
463 jack_error("ResumeRefNum error");
467 inline void JackClient::End()
469 jack_log("JackClient::Execute end name = %s", GetClientControl()->fName);
470 // Hum... not sure about this, the following "close" code is called in the RT thread...
471 int result;
472 fThread.DropSelfRealTime();
473 GetClientControl()->fActive = false;
474 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
475 fThread.Terminate();
478 inline void JackClient::Error()
480 jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
481 // Hum... not sure about this, the following "close" code is called in the RT thread...
482 int result;
483 fThread.DropSelfRealTime();
484 GetClientControl()->fActive = false;
485 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
486 ShutDown();
487 fThread.Terminate();
490 //-----------------
491 // Port management
492 //-----------------
494 int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
496 // Check if port name is empty
497 string port_name_str = string(port_name);
498 if (port_name_str.size() == 0) {
499 jack_error("port_name is empty");
500 return 0; // Means failure here...
503 // Check port name length
504 string name = string(GetClientControl()->fName) + string(":") + port_name_str;
505 if (name.size() >= JACK_PORT_NAME_SIZE) {
506 jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
507 "Please use %lu characters or less",
508 GetClientControl()->fName,
509 port_name,
510 JACK_PORT_NAME_SIZE - 1);
511 return 0; // Means failure here...
514 int result = -1;
515 jack_port_id_t port_index = NO_PORT;
516 fChannel->PortRegister(GetClientControl()->fRefNum, name.c_str(), port_type, flags, buffer_size, &port_index, &result);
518 if (result == 0) {
519 jack_log("JackClient::PortRegister ref = %ld name = %s type = %s port_index = %ld", GetClientControl()->fRefNum, name.c_str(), port_type, port_index);
520 fPortList.push_back(port_index);
521 return port_index;
522 } else {
523 return 0;
527 int JackClient::PortUnRegister(jack_port_id_t port_index)
529 jack_log("JackClient::PortUnRegister port_index = %ld", port_index);
530 list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
532 if (it != fPortList.end()) {
533 fPortList.erase(it);
534 int result = -1;
535 fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
536 return result;
537 } else {
538 jack_error("unregistering a port %ld that is not own by the client", port_index);
539 return -1;
543 int JackClient::PortConnect(const char* src, const char* dst)
545 jack_log("JackClient::Connect src = %s dst = %s", src, dst);
546 int result = -1;
547 fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
548 return result;
551 int JackClient::PortDisconnect(const char* src, const char* dst)
553 jack_log("JackClient::Disconnect src = %s dst = %s", src, dst);
554 int result = -1;
555 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
556 return result;
559 int JackClient::PortDisconnect(jack_port_id_t src)
561 jack_log("JackClient::PortDisconnect src = %ld", src);
562 int result = -1;
563 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
564 return result;
567 int JackClient::PortIsMine(jack_port_id_t port_index)
569 JackPort* port = GetGraphManager()->GetPort(port_index);
570 return GetClientControl()->fRefNum == port->GetRefNum();
573 int JackClient::PortRename(jack_port_id_t port_index, const char* name)
575 int result = -1;
576 fChannel->PortRename(GetClientControl()->fRefNum, port_index, name, &result);
577 return result;
580 //--------------------
581 // Context management
582 //--------------------
584 int JackClient::SetBufferSize(jack_nframes_t buffer_size)
586 int result = -1;
587 fChannel->SetBufferSize(buffer_size, &result);
588 return result;
591 int JackClient::SetFreeWheel(int onoff)
593 int result = -1;
594 fChannel->SetFreewheel(onoff, &result);
595 return result;
599 ShutDown is called:
600 - from the RT thread when Execute method fails
601 - possibly from a "closed" notification channel
602 (Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown))
605 void JackClient::ShutDown()
607 jack_log("ShutDown");
608 JackGlobals::fServerRunning = false;
610 if (fInfoShutdown) {
611 fInfoShutdown(JackFailure, "JACK server has been closed", fInfoShutdownArg);
612 fInfoShutdown = NULL;
613 } else if (fShutdown) {
614 fShutdown(fShutdownArg);
615 fShutdown = NULL;
619 //----------------------
620 // Transport management
621 //----------------------
623 inline int JackClient::ActivateAux()
625 // If activated without RT thread...
626 if (IsActive() && fThread.GetStatus() != JackThread::kRunning) {
628 jack_log("ActivateAux");
630 // RT thread is started
631 if (StartThread() < 0)
632 return -1;
634 int result = -1;
635 GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
636 fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
637 return result;
639 } else {
640 return 0;
644 int JackClient::ReleaseTimebase()
646 int result = -1;
647 fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
648 if (result == 0) {
649 GetClientControl()->fTransportTimebase = false;
650 fTimebase = NULL;
651 fTimebaseArg = NULL;
653 return result;
656 /* Call the server if the client is active, otherwise keeps the arguments */
657 int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
659 GetClientControl()->fTransportSync = (fSync != NULL);
660 fSyncArg = arg;
661 fSync = sync_callback;
662 return ActivateAux();
665 int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
667 int result = -1;
668 fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
670 if (result == 0) {
671 GetClientControl()->fTransportTimebase = true;
672 fTimebase = timebase_callback;
673 fTimebaseArg = arg;
674 return ActivateAux();
675 } else {
676 fTimebase = NULL;
677 fTimebaseArg = NULL;
678 return -1;
682 int JackClient::SetSyncTimeout(jack_time_t timeout)
684 GetEngineControl()->fTransport.SetSyncTimeout(timeout);
685 return 0;
688 // Must be RT safe
690 void JackClient::TransportLocate(jack_nframes_t frame)
692 jack_position_t pos;
693 pos.frame = frame;
694 pos.valid = (jack_position_bits_t)0;
695 jack_log("TransportLocate pos = %ld", pos.frame);
696 GetEngineControl()->fTransport.RequestNewPos(&pos);
699 int JackClient::TransportReposition(jack_position_t* pos)
701 jack_position_t tmp = *pos;
702 jack_log("TransportReposition pos = %ld", pos->frame);
703 if (tmp.valid & ~JACK_POSITION_MASK) {
704 return EINVAL;
705 } else {
706 GetEngineControl()->fTransport.RequestNewPos(pos);
707 return 0;
711 jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
713 return GetEngineControl()->fTransport.Query(pos);
716 jack_nframes_t JackClient::GetCurrentTransportFrame()
718 return GetEngineControl()->fTransport.GetCurrentFrame();
721 // Must be RT safe: directly write in the transport shared mem
722 void JackClient::TransportStart()
724 GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
727 // Must be RT safe: directly write in the transport shared mem
728 void JackClient::TransportStop()
730 GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
733 // Never called concurently with the server
734 // TODO check concurrency with SetSyncCallback
736 void JackClient::CallSyncCallback()
738 CallSyncCallbackAux();
741 inline void JackClient::CallSyncCallbackAux()
743 if (GetClientControl()->fTransportSync) {
745 JackTransportEngine& transport = GetEngineControl()->fTransport;
746 jack_position_t* cur_pos = transport.ReadCurrentState();
747 jack_transport_state_t transport_state = transport.GetState();
749 if (fSync != NULL) {
750 if (fSync(transport_state, cur_pos, fSyncArg)) {
751 GetClientControl()->fTransportState = JackTransportRolling;
752 GetClientControl()->fTransportSync = false;
754 } else {
755 GetClientControl()->fTransportState = JackTransportRolling;
756 GetClientControl()->fTransportSync = false;
761 void JackClient::CallTimebaseCallback()
763 CallTimebaseCallbackAux();
766 inline void JackClient::CallTimebaseCallbackAux()
768 JackTransportEngine& transport = GetEngineControl()->fTransport;
769 int master;
770 bool unused;
772 transport.GetTimebaseMaster(master, unused);
774 if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase...
776 jack_transport_state_t transport_state = transport.GetState();
777 jack_position_t* cur_pos = transport.WriteNextStateStart(1);
779 if (GetClientControl()->fTransportTimebase) {
780 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
781 GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true
782 } else if (transport_state == JackTransportRolling) {
783 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
786 transport.WriteNextStateStop(1);
790 //---------------------
791 // Callback management
792 //---------------------
794 void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
796 if (IsActive()) {
797 jack_error("You cannot set callbacks on an active client");
798 } else {
799 fShutdownArg = arg;
800 fShutdown = callback;
804 void JackClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg)
806 if (IsActive()) {
807 jack_error("You cannot set callbacks on an active client");
808 } else {
809 GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
810 fInfoShutdownArg = arg;
811 fInfoShutdown = callback;
815 int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
817 if (IsActive()) {
818 jack_error("You cannot set callbacks on an active client");
819 return -1;
820 } else if (fThreadFun) {
821 jack_error ("A thread callback has already been setup, both models cannot be used at the same time!");
822 return -1;
823 } else {
824 fProcessArg = arg;
825 fProcess = callback;
826 return 0;
830 int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
832 if (IsActive()) {
833 jack_error("You cannot set callbacks on an active client");
834 return -1;
835 } else {
836 GetClientControl()->fCallback[kXRunCallback] = (callback != NULL);
837 fXrunArg = arg;
838 fXrun = callback;
839 return 0;
843 int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
845 if (IsActive()) {
846 jack_error("You cannot set callbacks on an active client");
847 return -1;
848 } else {
849 fInitArg = arg;
850 fInit = callback;
851 return 0;
855 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
857 jack_log("SetGraphOrderCallback ");
859 if (IsActive()) {
860 jack_error("You cannot set callbacks on an active client");
861 return -1;
862 } else {
863 GetClientControl()->fCallback[kGraphOrderCallback] = (callback != NULL);
864 fGraphOrder = callback;
865 fGraphOrderArg = arg;
866 return 0;
870 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback 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[kBufferSizeCallback] = (callback != NULL);
877 fBufferSizeArg = arg;
878 fBufferSize = callback;
879 return 0;
883 int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg)
885 if (IsActive()) {
886 jack_error("You cannot set callbacks on an active client");
887 return -1;
888 } else {
889 GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL);
890 fSampleRateArg = arg;
891 fSampleRate = callback;
892 // Now invoke it
893 if (callback)
894 callback(GetEngineControl()->fSampleRate, arg);
895 return 0;
899 int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg)
901 if (IsActive()) {
902 jack_error("You cannot set callbacks on an active client");
903 return -1;
904 } else {
905 // kAddClient and kRemoveClient notifications must be delivered by the server in any case
906 fClientRegistrationArg = arg;
907 fClientRegistration = callback;
908 return 0;
912 int JackClient::SetFreewheelCallback(JackFreewheelCallback 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[kStartFreewheelCallback] = (callback != NULL);
919 GetClientControl()->fCallback[kStopFreewheelCallback] = (callback != NULL);
920 fFreewheelArg = arg;
921 fFreewheel = callback;
922 return 0;
926 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
928 if (IsActive()) {
929 jack_error("You cannot set callbacks on an active client");
930 return -1;
931 } else {
932 GetClientControl()->fCallback[kPortRegistrationOnCallback] = (callback != NULL);
933 GetClientControl()->fCallback[kPortRegistrationOffCallback] = (callback != NULL);
934 fPortRegistrationArg = arg;
935 fPortRegistration = callback;
936 return 0;
940 int JackClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg)
942 if (IsActive()) {
943 jack_error("You cannot set callbacks on an active client");
944 return -1;
945 } else {
946 GetClientControl()->fCallback[kPortConnectCallback] = (callback != NULL);
947 GetClientControl()->fCallback[kPortDisconnectCallback] = (callback != NULL);
948 fPortConnectArg = arg;
949 fPortConnect = callback;
950 return 0;
954 int JackClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg)
956 if (IsActive()) {
957 jack_error("You cannot set callbacks on an active client");
958 return -1;
959 } else {
960 GetClientControl()->fCallback[kPortRenameCallback] = (callback != NULL);
961 fPortRenameArg = arg;
962 fPortRename = callback;
963 return 0;
967 int JackClient::SetProcessThread(JackThreadCallback fun, void *arg)
969 if (IsActive()) {
970 jack_error("You cannot set callbacks on an active client");
971 return -1;
972 } else if (fProcess) {
973 jack_error ("A process callback has already been setup, both models cannot be used at the same time!");
974 return -1;
975 } else {
976 fThreadFun = fun;
977 fThreadFunArg = arg;
978 return 0;
982 //------------------
983 // Internal clients
984 //------------------
986 char* JackClient::GetInternalClientName(int ref)
988 char name_res[JACK_CLIENT_NAME_SIZE + 1];
989 int result = -1;
990 fChannel->GetInternalClientName(GetClientControl()->fRefNum, ref, name_res, &result);
991 return (result < 0) ? NULL : strdup(name_res);
994 int JackClient::InternalClientHandle(const char* client_name, jack_status_t* status)
996 int int_ref, result = -1;
997 fChannel->InternalClientHandle(GetClientControl()->fRefNum, client_name, (int*)status, &int_ref, &result);
998 return int_ref;
1001 int JackClient::InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va)
1003 if (strlen(client_name) >= JACK_CLIENT_NAME_SIZE) {
1004 jack_error ("\"%s\" is too long for a JACK client name.\n"
1005 "Please use %lu characters or less.",
1006 client_name, JACK_CLIENT_NAME_SIZE);
1007 return 0;
1010 if (va->load_name && (strlen(va->load_name) >= JACK_PATH_MAX)) {
1011 jack_error("\"%s\" is too long for a shared object name.\n"
1012 "Please use %lu characters or less.",
1013 va->load_name, PATH_MAX);
1014 int my_status1 = *status | (JackFailure | JackInvalidOption);
1015 *status = (jack_status_t)my_status1;
1016 return 0;
1019 if (va->load_init && (strlen(va->load_init) >= JACK_LOAD_INIT_LIMIT)) {
1020 jack_error ("\"%s\" is too long for internal client init "
1021 "string.\nPlease use %lu characters or less.",
1022 va->load_init, JACK_LOAD_INIT_LIMIT);
1023 int my_status1 = *status | (JackFailure | JackInvalidOption);
1024 *status = (jack_status_t)my_status1;
1025 return 0;
1028 int int_ref, result = -1;
1029 fChannel->InternalClientLoad(GetClientControl()->fRefNum, client_name, va->load_name, va->load_init, options, (int*)status, &int_ref, &result);
1030 return int_ref;
1033 void JackClient::InternalClientUnload(int ref, jack_status_t* status)
1035 int result = -1;
1036 fChannel->InternalClientUnload(GetClientControl()->fRefNum, ref, (int*)status, &result);
1040 } // end of namespace