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"
38 JackClient::JackClient()
41 JackClient::JackClient(JackSynchro
** table
)
43 fThread
= JackGlobals::MakeThread(this);
44 fSynchroTable
= table
;
52 fPortRegistration
= NULL
;
55 fGraphOrderArg
= NULL
;
59 fBufferSizeArg
= NULL
;
61 fPortRegistrationArg
= NULL
;
63 fConditionnal
= 0; // Temporary??
66 JackClient::~JackClient()
71 int JackClient::Close()
73 JackLog("JackClient::Close ref = %ld\n", GetClientControl()->fRefNum
);
76 fChannel
->ClientClose(GetClientControl()->fRefNum
, &result
);
79 fSynchroTable
[GetClientControl()->fRefNum
]->Disconnect();
83 bool JackClient::IsActive()
85 return (GetClientControl()) ? GetClientControl()->fActive
: false;
88 pthread_t
JackClient::GetThreadID()
90 return fThread
->GetThreadID();
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);
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
)
123 int JackClient::ClientNotify(int refnum
, const char* name
, int notify
, int sync
, int value
)
127 // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
130 case JackNotifyChannelInterface::kAddClient
:
131 res
= ClientNotifyImp(refnum
, name
, notify
, sync
, value
);
134 case JackNotifyChannelInterface::kRemoveClient
:
135 res
= ClientNotifyImp(refnum
, name
, notify
, sync
, value
);
138 case JackNotifyChannelInterface::kActivateClient
:
139 JackLog("JackClient::kActivateClient name = %s ref = %ld \n", name
, refnum
);
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.
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
);
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
);
164 case JackNotifyChannelInterface::kBufferSizeCallback
:
165 JackLog("JackClient::kBufferSizeCallback buffer_size = %ld\n", value
);
167 res
= fBufferSize(value
, fBufferSizeArg
);
170 case JackNotifyChannelInterface::kGraphOrderCallback
:
171 JackLog("JackClient::kGraphOrderCallback\n");
173 res
= fGraphOrder(fGraphOrderArg
);
176 case JackNotifyChannelInterface::kStartFreewheel
:
177 JackLog("JackClient::kStartFreewheel\n");
178 SetupDriverSync(true);
179 fThread
->DropRealTime();
181 fFreewheel(1, fFreewheelArg
);
184 case JackNotifyChannelInterface::kStopFreewheel
:
185 JackLog("JackClient::kStopFreewheel\n");
186 SetupDriverSync(false);
188 fFreewheel(0, fFreewheelArg
);
189 fThread
->AcquireRealTime();
192 case JackNotifyChannelInterface::kPortRegistrationOn
:
193 JackLog("JackClient::kPortRegistrationOn port_index = %ld\n", value
);
194 if (fPortRegistration
)
195 fPortRegistration(value
, 1, fPortRegistrationArg
);
198 case JackNotifyChannelInterface::kPortRegistrationOff
:
199 JackLog("JackClient::kPortRegistrationOff port_index = %ld \n", value
);
200 if (fPortRegistration
)
201 fPortRegistration(value
, 0, fPortRegistrationArg
);
204 case JackNotifyChannelInterface::kXRunCallback
:
205 JackLog("JackClient::kXRunCallback\n");
207 res
= fXrun(fXrunArg
);
210 case JackNotifyChannelInterface::kZombifyClient
:
211 JackLog("JackClient::kZombifyClient name = %s ref = %ld \n", name
, refnum
);
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");
230 /* TODO : solve WIN32 thread Kill issue
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);
240 if (StartThread() < 0)
244 fChannel
->ClientActivate(GetClientControl()->fRefNum
, &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;
259 \brief Need to stop thread after deactivating in the server.
261 int JackClient::Deactivate()
263 JackLog("JackClient::Deactivate \n");
267 GetClientControl()->fActive
= false;
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
276 fSynchroTable[GetClientControl()->fRefNum]->Disconnect();
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()
301 JackLog("JackClient::Init calling client thread init callback\n");
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");
322 if (GetEngineControl()->fRealTime
) {
323 if (fThread
->AcquireRealTime(GetEngineControl()->fPriority
- 1) < 0) {
324 jack_error("AcquireRealTime error");
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");
345 bool res
= CallProcessCallback();
346 CallTimebaseCallback();
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");
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...
372 fThread
->DropRealTime();
373 fChannel
->ClientDeactivate(GetClientControl()->fRefNum
, &result
);
374 Close(); // Not sure...
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();
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
,
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());
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
);
422 fPortList
.push_back(port_index
);
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()) {
437 fChannel
->PortUnRegister(GetClientControl()->fRefNum
, port_index
, &result
);
440 jack_error("unregistering a port %ld that is not own by the client", port_index
);
445 int JackClient::PortConnect(const char* src
, const char* dst
)
447 JackLog("JackClient::Connect src = %s dst = %s\n", src
, dst
);
449 fChannel
->PortConnect(GetClientControl()->fRefNum
, src
, dst
, &result
);
453 int JackClient::PortDisconnect(const char* src
, const char* dst
)
455 JackLog("JackClient::Disconnect src = %s dst = %s\n", src
, dst
);
457 fChannel
->PortDisconnect(GetClientControl()->fRefNum
, src
, dst
, &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
);
465 fChannel
->PortConnect(GetClientControl()->fRefNum
, src
, dst
, &result
);
469 int JackClient::PortDisconnect(jack_port_id_t src
)
471 JackLog("JackClient::PortDisconnect src = %ld\n", src
);
473 fChannel
->PortDisconnect(GetClientControl()->fRefNum
, src
, ALL_PORTS
, &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
)
490 fChannel
->SetBufferSize(buffer_size
, &result
);
494 int JackClient::SetFreeWheel(int onoff
)
497 fChannel
->SetFreewheel(onoff
, &result
);
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");
512 GetClientControl()->fActive
= false;
513 fShutdown(fShutdownArg
);
518 //----------------------
519 // Transport management
520 //----------------------
522 int JackClient::ReleaseTimebase()
525 fChannel
->ReleaseTimebase(GetClientControl()->fRefNum
, &result
);
533 /* Call the server if the client is active, otherwise keeps the arguments */
534 int JackClient::SetSyncCallback(JackSyncCallback sync_callback
, void* arg
)
537 GetClientControl()->fTransportState
= (sync_callback
== NULL
) ? JackTransportStopped
: JackTransportSynching
;
538 fSync
= sync_callback
;
543 int JackClient::SetSyncTimeout(jack_time_t timeout
)
545 GetEngineControl()->fTransport
.SetSyncTimeout(timeout
);
549 /* Call the server if the client is active, otherwise keeps the arguments */
550 int JackClient::SetTimebaseCallback(int conditional
, JackTimebaseCallback timebase_callback
, void* arg
)
554 fChannel
->SetTimebaseCallback(GetClientControl()->fRefNum
, conditional
, &result
);
555 JackLog("SetTimebaseCallback result = %ld\n", result
);
557 fTimebase
= timebase_callback
;
563 JackLog("SetTimebaseCallback OK result = %ld\n", result
);
566 fTimebase
= timebase_callback
;
568 fConditionnal
= conditional
;
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);
585 int JackClient::TransportLocate(jack_nframes_t 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
)
604 GetEngineControl()->fTransport
.ReadCurrentPos(pos
);
605 return GetEngineControl()->fTransport
.GetState();
608 jack_nframes_t
JackClient::GetCurrentTransportFrame()
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
;
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...
647 GetClientControl()->fTransportState
= JackTransportRolling
;
648 } else if (GetClientControl()->fTransportState
== JackTransportStarting
) {
649 if (fSync(transport_state
, cur_pos
, fSyncArg
))
650 GetClientControl()->fTransportState
= JackTransportRolling
;
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
;
661 case JackTransportSynching
:
662 // New pos when transport engine is stopped...
664 fSync(JackTransportStopped
, cur_pos
, fSyncArg
);
665 GetClientControl()->fTransportState
= JackTransportStopped
;
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
);
689 case JackTransportSynching
:
690 fTimebase(JackTransportStopped
, GetEngineControl()->fBufferSize
, cur_pos
, true, fTimebaseArg
);
697 transport
.WriteNextStateStop(1);
701 //---------------------
702 // Callback management
703 //---------------------
705 void JackClient::OnShutdown(JackShutdownCallback callback
, void *arg
)
708 jack_error("You cannot set callbacks on an active client");
711 fShutdown
= callback
;
715 int JackClient::SetProcessCallback(JackProcessCallback callback
, void *arg
)
718 jack_error("You cannot set callbacks on an active client");
727 int JackClient::SetXRunCallback(JackXRunCallback callback
, void *arg
)
730 jack_error("You cannot set callbacks on an active client");
739 int JackClient::SetInitCallback(JackThreadInitCallback callback
, void *arg
)
742 jack_error("You cannot set callbacks on an active client");
751 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback
, void *arg
)
753 JackLog("SetGraphOrderCallback \n");
756 jack_error("You cannot set callbacks on an active client");
759 fGraphOrder
= callback
;
760 fGraphOrderArg
= arg
;
765 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback
, void *arg
)
768 jack_error("You cannot set callbacks on an active client");
771 fBufferSizeArg
= arg
;
772 fBufferSize
= callback
;
777 int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback
, void* arg
)
780 jack_error("You cannot set callbacks on an active client");
783 fPortRegistrationArg
= arg
;
784 fClientRegistration
= callback
;
789 int JackClient::SetFreewheelCallback(JackFreewheelCallback callback
, void *arg
)
792 jack_error("You cannot set callbacks on an active client");
796 fFreewheel
= callback
;
801 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback
, void *arg
)
804 jack_error("You cannot set callbacks on an active client");
807 fPortRegistrationArg
= arg
;
808 fPortRegistration
= callback
;
813 } // end of namespace