Add jack_set_client_registration_callback API
[jack2.git] / common / JackClient.cpp
blob42942645b8124c3bf78fee8dbdb5970788547fe2
1 /*
2 Copyright (C) 2001 Paul Davis
3 Copyright (C) 2004-2006 Grame
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "JackClient.h"
23 #include "JackGraphManager.h"
24 #include "JackClientControl.h"
25 #include "JackEngineControl.h"
26 #include "JackGlobals.h"
27 #include "JackChannel.h"
28 #include "JackTransportEngine.h"
29 #include <math.h>
30 #include <string>
31 #include <algorithm>
33 using namespace std;
35 namespace Jack
38 JackClient::JackClient()
41 JackClient::JackClient(JackSynchro** table)
43 fThread = JackGlobals::MakeThread(this);
44 fSynchroTable = table;
45 fProcess = NULL;
46 fGraphOrder = NULL;
47 fXrun = NULL;
48 fShutdown = NULL;
49 fInit = NULL;
50 fBufferSize = NULL;
51 fFreewheel = NULL;
52 fPortRegistration = NULL;
53 fSync = NULL;
54 fProcessArg = NULL;
55 fGraphOrderArg = NULL;
56 fXrunArg = NULL;
57 fShutdownArg = NULL;
58 fInitArg = NULL;
59 fBufferSizeArg = NULL;
60 fFreewheelArg = NULL;
61 fPortRegistrationArg = NULL;
62 fSyncArg = NULL;
63 fConditionnal = 0; // Temporary??
66 JackClient::~JackClient()
68 delete fThread;
71 int JackClient::Close()
73 JackLog("JackClient::Close ref = %ld\n", GetClientControl()->fRefNum);
74 Deactivate();
75 int result = -1;
76 fChannel->ClientClose(GetClientControl()->fRefNum, &result);
77 fChannel->Stop();
78 fChannel->Close();
79 fSynchroTable[GetClientControl()->fRefNum]->Disconnect();
80 return result;
83 bool JackClient::IsActive()
85 return (GetClientControl()) ? GetClientControl()->fActive : false;
88 pthread_t JackClient::GetThreadID()
90 return fThread->GetThreadID();
93 /*!
94 \brief
95 In ASYNC mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations.
96 The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations.
97 Drivers synchro are setup in "flush" mode if server is ASYNC and NOT freewheel.
99 void JackClient::SetupDriverSync(bool freewheel)
101 if (!freewheel && !GetEngineControl()->fSyncMode) {
102 JackLog("JackClient::SetupDriverSync driver sem in flush mode\n");
103 fSynchroTable[AUDIO_DRIVER_REFNUM]->SetFlush(true);
104 fSynchroTable[FREEWHEEL_DRIVER_REFNUM]->SetFlush(true);
105 fSynchroTable[LOOPBACK_DRIVER_REFNUM]->SetFlush(true);
106 } else {
107 JackLog("JackClient::SetupDriverSync driver sem in normal mode\n");
108 fSynchroTable[AUDIO_DRIVER_REFNUM]->SetFlush(false);
109 fSynchroTable[FREEWHEEL_DRIVER_REFNUM]->SetFlush(false);
110 fSynchroTable[LOOPBACK_DRIVER_REFNUM]->SetFlush(false);
115 \brief Notification received from the server.
118 int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, int value)
120 return 0;
123 int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, int value)
125 int res = 0;
127 // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
128 switch (notify) {
130 case JackNotifyChannelInterface::kAddClient:
131 res = ClientNotifyImp(refnum, name, notify, sync, value);
132 break;
134 case JackNotifyChannelInterface::kRemoveClient:
135 res = ClientNotifyImp(refnum, name, notify, sync, value);
136 break;
138 case JackNotifyChannelInterface::kActivateClient:
139 JackLog("JackClient::kActivateClient name = %s ref = %ld \n", name, refnum);
140 Init();
141 break;
145 The current semantic is that notifications can only be received when the client has been activated,
146 although is this implementation, one could imagine calling notifications as soon as the client has be opened.
148 if (IsActive()) {
150 switch (notify) {
152 case JackNotifyChannelInterface::kAddClient:
153 printf("ClientNotify fName = %s name = %s\n", GetClientControl()->fName, name);
154 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) // Don't call the callback for the registering client itself
155 fClientRegistration(name, 1, fClientRegistrationArg);
156 break;
158 case JackNotifyChannelInterface::kRemoveClient:
159 printf("ClientNotify fName = %s name = %s\n", GetClientControl()->fName, name);
160 if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) // Don't call the callback for the registering client itself
161 fClientRegistration(name, 0, fClientRegistrationArg);
162 break;
164 case JackNotifyChannelInterface::kBufferSizeCallback:
165 JackLog("JackClient::kBufferSizeCallback buffer_size = %ld\n", value);
166 if (fBufferSize)
167 res = fBufferSize(value, fBufferSizeArg);
168 break;
170 case JackNotifyChannelInterface::kGraphOrderCallback:
171 JackLog("JackClient::kGraphOrderCallback\n");
172 if (fGraphOrder)
173 res = fGraphOrder(fGraphOrderArg);
174 break;
176 case JackNotifyChannelInterface::kStartFreewheel:
177 JackLog("JackClient::kStartFreewheel\n");
178 SetupDriverSync(true);
179 fThread->DropRealTime();
180 if (fFreewheel)
181 fFreewheel(1, fFreewheelArg);
182 break;
184 case JackNotifyChannelInterface::kStopFreewheel:
185 JackLog("JackClient::kStopFreewheel\n");
186 SetupDriverSync(false);
187 if (fFreewheel)
188 fFreewheel(0, fFreewheelArg);
189 fThread->AcquireRealTime();
190 break;
192 case JackNotifyChannelInterface::kPortRegistrationOn:
193 JackLog("JackClient::kPortRegistrationOn port_index = %ld\n", value);
194 if (fPortRegistration)
195 fPortRegistration(value, 1, fPortRegistrationArg);
196 break;
198 case JackNotifyChannelInterface::kPortRegistrationOff:
199 JackLog("JackClient::kPortRegistrationOff port_index = %ld \n", value);
200 if (fPortRegistration)
201 fPortRegistration(value, 0, fPortRegistrationArg);
202 break;
204 case JackNotifyChannelInterface::kXRunCallback:
205 JackLog("JackClient::kXRunCallback\n");
206 if (fXrun)
207 res = fXrun(fXrunArg);
208 break;
210 case JackNotifyChannelInterface::kZombifyClient:
211 JackLog("JackClient::kZombifyClient name = %s ref = %ld \n", name, refnum);
212 ShutDown();
213 break;
217 return res;
221 \brief We need to start thread before activating in the server, otherwise the FW driver
222 connected to the client may not be activated.
224 int JackClient::Activate()
226 JackLog("JackClient::Activate \n");
227 if (IsActive())
228 return 0;
230 /* TODO : solve WIN32 thread Kill issue
231 #ifdef WIN32
232 // Done first so that the RT thread then access an allocated synchro
233 if (!fSynchroTable[GetClientControl()->fRefNum]->Connect(GetClientControl()->fName)) {
234 jack_error("Cannot ConnectSemaphore %s client", GetClientControl()->fName);
235 return -1;
237 #endif
240 if (StartThread() < 0)
241 return -1;
243 int result = -1;
244 fChannel->ClientActivate(GetClientControl()->fRefNum, &result);
245 if (result < 0)
246 return result;
248 if (fSync != NULL) /* If a SyncCallback is pending... */
249 SetSyncCallback(fSync, fSyncArg);
251 if (fTimebase != NULL) /* If a TimebaseCallback is pending... */
252 SetTimebaseCallback(fConditionnal, fTimebase, fTimebaseArg);
254 GetClientControl()->fActive = true;
255 return 0;
259 \brief Need to stop thread after deactivating in the server.
261 int JackClient::Deactivate()
263 JackLog("JackClient::Deactivate \n");
264 if (!IsActive())
265 return 0;
267 GetClientControl()->fActive = false;
268 int result = -1;
269 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
271 JackLog("JackClient::Deactivate res = %ld \n", result);
272 // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
274 /* TODO : solve WIN32 thread Kill issue
275 #ifdef WIN32
276 fSynchroTable[GetClientControl()->fRefNum]->Disconnect();
277 fThread->Stop();
278 #else
279 fThread->Kill();
280 #endif
282 fThread->Kill();
283 return result;
286 //----------------------
287 // RT thread management
288 //----------------------
290 bool JackClient::CallProcessCallback()
292 return (fProcess == NULL) ? true : (fProcess(GetEngineControl()->fBufferSize, fProcessArg) == 0);
296 \brief Called once when the thread starts.
298 bool JackClient::Init()
300 if (fInit) {
301 JackLog("JackClient::Init calling client thread init callback\n");
302 fInit(fInitArg);
304 return true;
307 int JackClient::StartThread()
309 JackLog("JackClient::StartThread : period = %ld computation = %ld constraint = %ld\n",
310 long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
311 long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
312 long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));
314 // Will do "something" on OSX only...
315 fThread->SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
317 if (fThread->Start() < 0) {
318 jack_error("Start thread error");
319 return -1;
322 if (GetEngineControl()->fRealTime) {
323 if (fThread->AcquireRealTime(GetEngineControl()->fPriority - 1) < 0) {
324 jack_error("AcquireRealTime error");
328 return 0;
332 \brief RT thread.
334 bool JackClient::Execute()
336 // Suspend itself: wait on the input synchro
337 if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
338 jack_error("SuspendRefNum error");
339 goto error;
342 // Process call
343 if (IsActive()) {
344 CallSyncCallback();
345 bool res = CallProcessCallback();
346 CallTimebaseCallback();
347 if (!res)
348 goto end;
349 } else {
350 JackLog("Process called for an inactive client\n");
351 // Happens if client is still not activated (connected to the FW)
352 // or still runs while being desactivated by the server
355 // Resume: signal output clients connected to the running client
356 if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
357 jack_error("ResumeRefNum error");
360 return true;
362 end:
363 JackLog("JackClient::Execute end name = %s\n", GetClientControl()->fName);
365 // Continue graph execution for this cycle
366 if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
367 jack_error("ResumeRefNum error");
370 // Hum... not sure about this, the following "close" code is called in the RT thread...
371 int result;
372 fThread->DropRealTime();
373 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
374 Close(); // Not sure...
375 return false;
377 error:
378 jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
379 // Hum... not sure about this, the following "close" code is called in the RT thread...
380 fThread->DropRealTime();
381 ShutDown();
382 return false;
385 //-----------------
386 // Port management
387 //-----------------
389 int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
391 // Check port name length
392 string port_name_str = string(port_name);
393 if (port_name_str.size() == 0) {
394 jack_error("port_name is empty.");
395 return 0; // Means failure here...
398 string name = string(GetClientControl()->fName) + string(":") + port_name_str;
399 if (name.size() >= JACK_PORT_NAME_SIZE) {
400 jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
401 "Please use %lu characters or less.",
402 GetClientControl()->fName,
403 port_name,
404 JACK_PORT_NAME_SIZE - 1);
405 return 0; // Means failure here...
408 // Check if port name already exists
409 if (GetGraphManager()->GetPort(name.c_str()) != NO_PORT) {
410 jack_error("port_name \"%s\" already exists.", port_name);
411 return 0; // Means failure here...
414 JackLog("JackClient::PortRegister ref = %ld name = %s \n", GetClientControl()->fRefNum, name.c_str());
416 int result = -1;
417 jack_port_id_t port_index = NO_PORT;
418 fChannel->PortRegister(GetClientControl()->fRefNum, name.c_str(), flags, buffer_size, &port_index, &result);
419 JackLog("JackClient::PortRegister port_index = %ld \n", port_index);
421 if (result == 0) {
422 fPortList.push_back(port_index);
423 return port_index;
424 } else {
425 return 0;
429 int JackClient::PortUnRegister(jack_port_id_t port_index)
431 JackLog("JackClient::PortUnRegister port_index = %ld\n", port_index);
432 list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
434 if (it != fPortList.end()) {
435 fPortList.erase(it);
436 int result = -1;
437 fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
438 return result;
439 } else {
440 jack_error("unregistering a port %ld that is not own by the client", port_index);
441 return -1;
445 int JackClient::PortConnect(const char* src, const char* dst)
447 JackLog("JackClient::Connect src = %s dst = %s\n", src, dst);
448 int result = -1;
449 fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
450 return result;
453 int JackClient::PortDisconnect(const char* src, const char* dst)
455 JackLog("JackClient::Disconnect src = %s dst = %s\n", src, dst);
456 int result = -1;
457 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
458 return result;
461 int JackClient::PortConnect(jack_port_id_t src, jack_port_id_t dst)
463 JackLog("JackClient::PortConnect src = %ld dst = %ld\n", src, dst);
464 int result = -1;
465 fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
466 return result;
469 int JackClient::PortDisconnect(jack_port_id_t src)
471 JackLog("JackClient::PortDisconnect src = %ld\n", src);
472 int result = -1;
473 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
474 return result;
477 int JackClient::PortIsMine(jack_port_id_t port_index)
479 JackPort* port = GetGraphManager()->GetPort(port_index);
480 return GetClientControl()->fRefNum == port->GetRefNum();
483 //--------------------
484 // Context management
485 //--------------------
487 int JackClient::SetBufferSize(jack_nframes_t buffer_size)
489 int result = -1;
490 fChannel->SetBufferSize(buffer_size, &result);
491 return result;
494 int JackClient::SetFreeWheel(int onoff)
496 int result = -1;
497 fChannel->SetFreewheel(onoff, &result);
498 return result;
502 ShutDown is called:
503 - from the RT thread when Execute method fails
504 - possibly from a "closed" notification channel
505 (Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown))
508 void JackClient::ShutDown()
510 JackLog("ShutDown\n");
511 if (fShutdown) {
512 GetClientControl()->fActive = false;
513 fShutdown(fShutdownArg);
514 fShutdown = NULL;
518 //----------------------
519 // Transport management
520 //----------------------
522 int JackClient::ReleaseTimebase()
524 int result = -1;
525 fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
526 if (result == 0) {
527 fTimebase = NULL;
528 fTimebaseArg = NULL;
530 return result;
533 /* Call the server if the client is active, otherwise keeps the arguments */
534 int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
536 if (IsActive())
537 GetClientControl()->fTransportState = (sync_callback == NULL) ? JackTransportStopped : JackTransportSynching;
538 fSync = sync_callback;
539 fSyncArg = arg;
540 return 0;
543 int JackClient::SetSyncTimeout(jack_time_t timeout)
545 GetEngineControl()->fTransport.SetSyncTimeout(timeout);
546 return 0;
549 /* Call the server if the client is active, otherwise keeps the arguments */
550 int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
552 if (IsActive()) {
553 int result = -1;
554 fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
555 JackLog("SetTimebaseCallback result = %ld\n", result);
556 if (result == 0) {
557 fTimebase = timebase_callback;
558 fTimebaseArg = arg;
559 } else {
560 fTimebase = NULL;
561 fTimebaseArg = NULL;
563 JackLog("SetTimebaseCallback OK result = %ld\n", result);
564 return result;
565 } else {
566 fTimebase = timebase_callback;
567 fTimebaseArg = arg;
568 fConditionnal = conditional;
569 return 0;
573 // Must be RT safe
574 int JackClient::RequestNewPos(jack_position_t* pos)
576 JackTransportEngine& transport = GetEngineControl()->fTransport;
577 jack_position_t* request = transport.WriteNextStateStart(2);
578 pos->unique_1 = pos->unique_2 = transport.GenerateUniqueID();
579 JackTransportEngine::TransportCopyPosition(pos, request);
580 JackLog("RequestNewPos pos = %ld\n", pos->frame);
581 transport.WriteNextStateStop(2);
582 return 0;
585 int JackClient::TransportLocate(jack_nframes_t frame)
587 jack_position_t pos;
588 pos.frame = frame;
589 pos.valid = (jack_position_bits_t)0;
590 JackLog("TransportLocate pos = %ld\n", pos.frame);
591 return RequestNewPos(&pos);
594 int JackClient::TransportReposition(jack_position_t* pos)
596 jack_position_t tmp = *pos;
597 JackLog("TransportReposition pos = %ld\n", pos->frame);
598 return (tmp.valid & ~JACK_POSITION_MASK) ? EINVAL : RequestNewPos(&tmp);
601 jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
603 if (pos)
604 GetEngineControl()->fTransport.ReadCurrentPos(pos);
605 return GetEngineControl()->fTransport.GetState();
608 jack_nframes_t JackClient::GetCurrentTransportFrame()
610 jack_position_t pos;
611 jack_transport_state_t state = TransportQuery(&pos);
613 if (state == JackTransportRolling) {
614 float usecs = GetMicroSeconds() - pos.usecs;
615 jack_nframes_t elapsed = (jack_nframes_t)floor((((float) pos.frame_rate) / 1000000.0f) * usecs);
616 return pos.frame + elapsed;
617 } else {
618 return pos.frame;
622 // Must be RT safe: directly write in the transport shared mem
623 void JackClient::TransportStart()
625 GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
628 // Must be RT safe: directly write in the transport shared mem
629 void JackClient::TransportStop()
631 GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
634 // Never called concurently with the server
635 // TODO check concurency with SetSyncCallback
637 void JackClient::CallSyncCallback()
639 JackTransportEngine& transport = GetEngineControl()->fTransport;
640 jack_position_t* cur_pos = transport.ReadCurrentState();
641 jack_transport_state_t transport_state = transport.GetState();
643 switch (transport_state) {
645 case JackTransportStarting: // Starting...
646 if (fSync == NULL) {
647 GetClientControl()->fTransportState = JackTransportRolling;
648 } else if (GetClientControl()->fTransportState == JackTransportStarting) {
649 if (fSync(transport_state, cur_pos, fSyncArg))
650 GetClientControl()->fTransportState = JackTransportRolling;
652 break;
654 case JackTransportRolling:
655 if (fSync != NULL && GetClientControl()->fTransportState == JackTransportStarting) { // Client still not ready
656 if (fSync(transport_state, cur_pos, fSyncArg))
657 GetClientControl()->fTransportState = JackTransportRolling;
659 break;
661 case JackTransportSynching:
662 // New pos when transport engine is stopped...
663 if (fSync != NULL) {
664 fSync(JackTransportStopped, cur_pos, fSyncArg);
665 GetClientControl()->fTransportState = JackTransportStopped;
667 break;
669 default:
670 break;
674 void JackClient::CallTimebaseCallback()
676 JackTransportEngine& transport = GetEngineControl()->fTransport;
678 if (fTimebase != NULL && fTimebaseArg != NULL && GetClientControl()->fRefNum == transport.GetTimebaseMaster()) {
680 jack_transport_state_t transport_state = transport.GetState();
681 jack_position_t* cur_pos = transport.WriteNextStateStart(1);
683 switch (transport_state) {
685 case JackTransportRolling:
686 fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
687 break;
689 case JackTransportSynching:
690 fTimebase(JackTransportStopped, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
691 break;
693 default:
694 break;
697 transport.WriteNextStateStop(1);
701 //---------------------
702 // Callback management
703 //---------------------
705 void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
707 if (IsActive()) {
708 jack_error("You cannot set callbacks on an active client");
709 } else {
710 fShutdownArg = arg;
711 fShutdown = callback;
715 int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
717 if (IsActive()) {
718 jack_error("You cannot set callbacks on an active client");
719 return -1;
720 } else {
721 fProcessArg = arg;
722 fProcess = callback;
723 return 0;
727 int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
729 if (IsActive()) {
730 jack_error("You cannot set callbacks on an active client");
731 return -1;
732 } else {
733 fXrunArg = arg;
734 fXrun = callback;
735 return 0;
739 int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
741 if (IsActive()) {
742 jack_error("You cannot set callbacks on an active client");
743 return -1;
744 } else {
745 fInitArg = arg;
746 fInit = callback;
747 return 0;
751 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
753 JackLog("SetGraphOrderCallback \n");
755 if (IsActive()) {
756 jack_error("You cannot set callbacks on an active client");
757 return -1;
758 } else {
759 fGraphOrder = callback;
760 fGraphOrderArg = arg;
761 return 0;
765 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
767 if (IsActive()) {
768 jack_error("You cannot set callbacks on an active client");
769 return -1;
770 } else {
771 fBufferSizeArg = arg;
772 fBufferSize = callback;
773 return 0;
777 int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg)
779 if (IsActive()) {
780 jack_error("You cannot set callbacks on an active client");
781 return -1;
782 } else {
783 fPortRegistrationArg = arg;
784 fClientRegistration = callback;
785 return 0;
789 int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
791 if (IsActive()) {
792 jack_error("You cannot set callbacks on an active client");
793 return -1;
794 } else {
795 fFreewheelArg = arg;
796 fFreewheel = callback;
797 return 0;
801 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
803 if (IsActive()) {
804 jack_error("You cannot set callbacks on an active client");
805 return -1;
806 } else {
807 fPortRegistrationArg = arg;
808 fPortRegistration = callback;
809 return 0;
813 } // end of namespace