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 case JackNotifyChannelInterface::kRemoveClient
:
132 res
= ClientNotifyImp(refnum
, name
, notify
, sync
, value
);
135 case JackNotifyChannelInterface::kActivateClient
:
136 JackLog("JackClient::kActivateClient name = %s ref = %ld \n", name
, refnum
);
142 The current semantic is that notifications can only be received when the client has been activated,
143 although is this implementation, one could imagine calling notifications as soon as the client has be opened.
149 case JackNotifyChannelInterface::kBufferSizeCallback
:
150 JackLog("JackClient::kBufferSizeCallback buffer_size = %ld\n", value
);
152 res
= fBufferSize(value
, fBufferSizeArg
);
155 case JackNotifyChannelInterface::kGraphOrderCallback
:
156 JackLog("JackClient::kGraphOrderCallback\n");
158 res
= fGraphOrder(fGraphOrderArg
);
161 case JackNotifyChannelInterface::kStartFreewheel
:
162 JackLog("JackClient::kStartFreewheel\n");
163 SetupDriverSync(true);
164 fThread
->DropRealTime();
166 fFreewheel(1, fFreewheelArg
);
169 case JackNotifyChannelInterface::kStopFreewheel
:
170 JackLog("JackClient::kStopFreewheel\n");
171 SetupDriverSync(false);
173 fFreewheel(0, fFreewheelArg
);
174 fThread
->AcquireRealTime();
177 case JackNotifyChannelInterface::kPortRegistrationOn
:
178 JackLog("JackClient::kPortRegistrationOn port_index = %ld\n", value
);
179 if (fPortRegistration
)
180 fPortRegistration(value
, 1, fPortRegistrationArg
);
183 case JackNotifyChannelInterface::kPortRegistrationOff
:
184 JackLog("JackClient::kPortRegistrationOff port_index = %ld \n", value
);
185 if (fPortRegistration
)
186 fPortRegistration(value
, 0, fPortRegistrationArg
);
189 case JackNotifyChannelInterface::kXRunCallback
:
190 JackLog("JackClient::kXRunCallback\n");
192 res
= fXrun(fXrunArg
);
195 case JackNotifyChannelInterface::kZombifyClient
:
196 JackLog("JackClient::kZombifyClient name = %s ref = %ld \n", name
, refnum
);
206 \brief We need to start thread before activating in the server, otherwise the FW driver
207 connected to the client may not be activated.
209 int JackClient::Activate()
211 JackLog("JackClient::Activate \n");
215 /* TODO : solve WIN32 thread Kill issue
217 // Done first so that the RT thread then access an allocated synchro
218 if (!fSynchroTable[GetClientControl()->fRefNum]->Connect(GetClientControl()->fName)) {
219 jack_error("Cannot ConnectSemaphore %s client", GetClientControl()->fName);
225 if (StartThread() < 0)
229 fChannel
->ClientActivate(GetClientControl()->fRefNum
, &result
);
233 if (fSync
!= NULL
) /* If a SyncCallback is pending... */
234 SetSyncCallback(fSync
, fSyncArg
);
236 if (fTimebase
!= NULL
) /* If a TimebaseCallback is pending... */
237 SetTimebaseCallback(fConditionnal
, fTimebase
, fTimebaseArg
);
239 GetClientControl()->fActive
= true;
244 \brief Need to stop thread after deactivating in the server.
246 int JackClient::Deactivate()
248 JackLog("JackClient::Deactivate \n");
252 GetClientControl()->fActive
= false;
254 fChannel
->ClientDeactivate(GetClientControl()->fRefNum
, &result
);
256 JackLog("JackClient::Deactivate res = %ld \n", result
);
257 // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
259 /* TODO : solve WIN32 thread Kill issue
261 fSynchroTable[GetClientControl()->fRefNum]->Disconnect();
271 //----------------------
272 // RT thread management
273 //----------------------
275 bool JackClient::CallProcessCallback()
277 return (fProcess
== NULL
) ? true : (fProcess(GetEngineControl()->fBufferSize
, fProcessArg
) == 0);
281 \brief Called once when the thread starts.
283 bool JackClient::Init()
286 JackLog("JackClient::Init calling client thread init callback\n");
292 int JackClient::StartThread()
294 JackLog("JackClient::StartThread : period = %ld computation = %ld constraint = %ld\n",
295 long(int64_t(GetEngineControl()->fPeriod
) / 1000.0f
),
296 long(int64_t(GetEngineControl()->fComputation
) / 1000.0f
),
297 long(int64_t(GetEngineControl()->fConstraint
) / 1000.0f
));
299 // Will do "something" on OSX only...
300 fThread
->SetParams(GetEngineControl()->fPeriod
, GetEngineControl()->fComputation
, GetEngineControl()->fConstraint
);
302 if (fThread
->Start() < 0) {
303 jack_error("Start thread error");
307 if (GetEngineControl()->fRealTime
) {
308 if (fThread
->AcquireRealTime(GetEngineControl()->fPriority
- 1) < 0) {
309 jack_error("AcquireRealTime error");
319 bool JackClient::Execute()
321 // Suspend itself: wait on the input synchro
322 if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable
, 0x7FFFFFFF) < 0) {
323 jack_error("SuspendRefNum error");
330 bool res
= CallProcessCallback();
331 CallTimebaseCallback();
335 JackLog("Process called for an inactive client\n");
336 // Happens if client is still not activated (connected to the FW)
337 // or still runs while being desactivated by the server
340 // Resume: signal output clients connected to the running client
341 if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable
) < 0) {
342 jack_error("ResumeRefNum error");
348 JackLog("JackClient::Execute end name = %s\n", GetClientControl()->fName
);
350 // Continue graph execution for this cycle
351 if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable
) < 0) {
352 jack_error("ResumeRefNum error");
355 // Hum... not sure about this, the following "close" code is called in the RT thread...
357 fThread
->DropRealTime();
358 fChannel
->ClientDeactivate(GetClientControl()->fRefNum
, &result
);
359 Close(); // Not sure...
363 jack_error("JackClient::Execute error name = %s", GetClientControl()->fName
);
364 // Hum... not sure about this, the following "close" code is called in the RT thread...
365 fThread
->DropRealTime();
374 int JackClient::PortRegister(const char* port_name
, const char* port_type
, unsigned long flags
, unsigned long buffer_size
)
376 // Check port name length
377 string port_name_str
= string(port_name
);
378 if (port_name_str
.size() == 0) {
379 jack_error("port_name is empty.");
380 return 0; // Means failure here...
383 string name
= string(GetClientControl()->fName
) + string(":") + port_name_str
;
384 if (name
.size() >= JACK_PORT_NAME_SIZE
) {
385 jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
386 "Please use %lu characters or less.",
387 GetClientControl()->fName
,
389 JACK_PORT_NAME_SIZE
- 1);
390 return 0; // Means failure here...
393 // Check if port name already exists
394 if (GetGraphManager()->GetPort(name
.c_str()) != NO_PORT
) {
395 jack_error("port_name \"%s\" already exists.", port_name
);
396 return 0; // Means failure here...
399 JackLog("JackClient::PortRegister ref = %ld name = %s \n", GetClientControl()->fRefNum
, name
.c_str());
402 jack_port_id_t port_index
= NO_PORT
;
403 fChannel
->PortRegister(GetClientControl()->fRefNum
, name
.c_str(), flags
, buffer_size
, &port_index
, &result
);
404 JackLog("JackClient::PortRegister port_index = %ld \n", port_index
);
407 fPortList
.push_back(port_index
);
414 int JackClient::PortUnRegister(jack_port_id_t port_index
)
416 JackLog("JackClient::PortUnRegister port_index = %ld\n", port_index
);
417 list
<jack_port_id_t
>::iterator it
= find(fPortList
.begin(), fPortList
.end(), port_index
);
419 if (it
!= fPortList
.end()) {
422 fChannel
->PortUnRegister(GetClientControl()->fRefNum
, port_index
, &result
);
425 jack_error("unregistering a port %ld that is not own by the client", port_index
);
430 int JackClient::PortConnect(const char* src
, const char* dst
)
432 JackLog("JackClient::Connect src = %s dst = %s\n", src
, dst
);
434 fChannel
->PortConnect(GetClientControl()->fRefNum
, src
, dst
, &result
);
438 int JackClient::PortDisconnect(const char* src
, const char* dst
)
440 JackLog("JackClient::Disconnect src = %s dst = %s\n", src
, dst
);
442 fChannel
->PortDisconnect(GetClientControl()->fRefNum
, src
, dst
, &result
);
446 int JackClient::PortConnect(jack_port_id_t src
, jack_port_id_t dst
)
448 JackLog("JackClient::PortConnect src = %ld dst = %ld\n", src
, dst
);
450 fChannel
->PortConnect(GetClientControl()->fRefNum
, src
, dst
, &result
);
454 int JackClient::PortDisconnect(jack_port_id_t src
)
456 JackLog("JackClient::PortDisconnect src = %ld\n", src
);
458 fChannel
->PortDisconnect(GetClientControl()->fRefNum
, src
, ALL_PORTS
, &result
);
462 int JackClient::PortIsMine(jack_port_id_t port_index
)
464 JackPort
* port
= GetGraphManager()->GetPort(port_index
);
465 return GetClientControl()->fRefNum
== port
->GetRefNum();
468 //--------------------
469 // Context management
470 //--------------------
472 int JackClient::SetBufferSize(jack_nframes_t buffer_size
)
475 fChannel
->SetBufferSize(buffer_size
, &result
);
479 int JackClient::SetFreeWheel(int onoff
)
482 fChannel
->SetFreewheel(onoff
, &result
);
488 - from the RT thread when Execute method fails
489 - possibly from a "closed" notification channel
490 (Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown))
493 void JackClient::ShutDown()
495 JackLog("ShutDown\n");
497 GetClientControl()->fActive
= false;
498 fShutdown(fShutdownArg
);
503 //----------------------
504 // Transport management
505 //----------------------
507 int JackClient::ReleaseTimebase()
510 fChannel
->ReleaseTimebase(GetClientControl()->fRefNum
, &result
);
518 /* Call the server if the client is active, otherwise keeps the arguments */
519 int JackClient::SetSyncCallback(JackSyncCallback sync_callback
, void* arg
)
522 GetClientControl()->fTransportState
= (sync_callback
== NULL
) ? JackTransportStopped
: JackTransportSynching
;
523 fSync
= sync_callback
;
528 int JackClient::SetSyncTimeout(jack_time_t timeout
)
530 GetEngineControl()->fTransport
.SetSyncTimeout(timeout
);
534 /* Call the server if the client is active, otherwise keeps the arguments */
535 int JackClient::SetTimebaseCallback(int conditional
, JackTimebaseCallback timebase_callback
, void* arg
)
539 fChannel
->SetTimebaseCallback(GetClientControl()->fRefNum
, conditional
, &result
);
540 JackLog("SetTimebaseCallback result = %ld\n", result
);
542 fTimebase
= timebase_callback
;
548 JackLog("SetTimebaseCallback OK result = %ld\n", result
);
551 fTimebase
= timebase_callback
;
553 fConditionnal
= conditional
;
559 int JackClient::RequestNewPos(jack_position_t
* pos
)
561 JackTransportEngine
& transport
= GetEngineControl()->fTransport
;
562 jack_position_t
* request
= transport
.WriteNextStateStart(2);
563 pos
->unique_1
= pos
->unique_2
= transport
.GenerateUniqueID();
564 JackTransportEngine::TransportCopyPosition(pos
, request
);
565 JackLog("RequestNewPos pos = %ld\n", pos
->frame
);
566 transport
.WriteNextStateStop(2);
570 int JackClient::TransportLocate(jack_nframes_t frame
)
574 pos
.valid
= (jack_position_bits_t
)0;
575 JackLog("TransportLocate pos = %ld\n", pos
.frame
);
576 return RequestNewPos(&pos
);
579 int JackClient::TransportReposition(jack_position_t
* pos
)
581 jack_position_t tmp
= *pos
;
582 JackLog("TransportReposition pos = %ld\n", pos
->frame
);
583 return (tmp
.valid
& ~JACK_POSITION_MASK
) ? EINVAL
: RequestNewPos(&tmp
);
586 jack_transport_state_t
JackClient::TransportQuery(jack_position_t
* pos
)
589 GetEngineControl()->fTransport
.ReadCurrentPos(pos
);
590 return GetEngineControl()->fTransport
.GetState();
593 jack_nframes_t
JackClient::GetCurrentTransportFrame()
596 jack_transport_state_t state
= TransportQuery(&pos
);
598 if (state
== JackTransportRolling
) {
599 float usecs
= GetMicroSeconds() - pos
.usecs
;
600 jack_nframes_t elapsed
= (jack_nframes_t
)floor((((float) pos
.frame_rate
) / 1000000.0f
) * usecs
);
601 return pos
.frame
+ elapsed
;
607 // Must be RT safe: directly write in the transport shared mem
608 void JackClient::TransportStart()
610 GetEngineControl()->fTransport
.SetCommand(TransportCommandStart
);
613 // Must be RT safe: directly write in the transport shared mem
614 void JackClient::TransportStop()
616 GetEngineControl()->fTransport
.SetCommand(TransportCommandStop
);
619 // Never called concurently with the server
620 // TODO check concurency with SetSyncCallback
622 void JackClient::CallSyncCallback()
624 JackTransportEngine
& transport
= GetEngineControl()->fTransport
;
625 jack_position_t
* cur_pos
= transport
.ReadCurrentState();
626 jack_transport_state_t transport_state
= transport
.GetState();
628 switch (transport_state
) {
630 case JackTransportStarting
: // Starting...
632 GetClientControl()->fTransportState
= JackTransportRolling
;
633 } else if (GetClientControl()->fTransportState
== JackTransportStarting
) {
634 if (fSync(transport_state
, cur_pos
, fSyncArg
))
635 GetClientControl()->fTransportState
= JackTransportRolling
;
639 case JackTransportRolling
:
640 if (fSync
!= NULL
&& GetClientControl()->fTransportState
== JackTransportStarting
) { // Client still not ready
641 if (fSync(transport_state
, cur_pos
, fSyncArg
))
642 GetClientControl()->fTransportState
= JackTransportRolling
;
646 case JackTransportSynching
:
647 // New pos when transport engine is stopped...
649 fSync(JackTransportStopped
, cur_pos
, fSyncArg
);
650 GetClientControl()->fTransportState
= JackTransportStopped
;
659 void JackClient::CallTimebaseCallback()
661 JackTransportEngine
& transport
= GetEngineControl()->fTransport
;
663 if (fTimebase
!= NULL
&& fTimebaseArg
!= NULL
&& GetClientControl()->fRefNum
== transport
.GetTimebaseMaster()) {
665 jack_transport_state_t transport_state
= transport
.GetState();
666 jack_position_t
* cur_pos
= transport
.WriteNextStateStart(1);
668 switch (transport_state
) {
670 case JackTransportRolling
:
671 fTimebase(transport_state
, GetEngineControl()->fBufferSize
, cur_pos
, false, fTimebaseArg
);
674 case JackTransportSynching
:
675 fTimebase(JackTransportStopped
, GetEngineControl()->fBufferSize
, cur_pos
, true, fTimebaseArg
);
682 transport
.WriteNextStateStop(1);
686 //---------------------
687 // Callback management
688 //---------------------
690 void JackClient::OnShutdown(JackShutdownCallback callback
, void *arg
)
693 jack_error("You cannot set callbacks on an active client");
696 fShutdown
= callback
;
700 int JackClient::SetProcessCallback(JackProcessCallback callback
, void *arg
)
703 jack_error("You cannot set callbacks on an active client");
712 int JackClient::SetXRunCallback(JackXRunCallback callback
, void *arg
)
715 jack_error("You cannot set callbacks on an active client");
724 int JackClient::SetInitCallback(JackThreadInitCallback callback
, void *arg
)
727 jack_error("You cannot set callbacks on an active client");
736 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback
, void *arg
)
738 JackLog("SetGraphOrderCallback \n");
741 jack_error("You cannot set callbacks on an active client");
744 fGraphOrder
= callback
;
745 fGraphOrderArg
= arg
;
750 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback
, void *arg
)
753 jack_error("You cannot set callbacks on an active client");
756 fBufferSizeArg
= arg
;
757 fBufferSize
= callback
;
762 int JackClient::SetFreewheelCallback(JackFreewheelCallback callback
, void *arg
)
765 jack_error("You cannot set callbacks on an active client");
769 fFreewheel
= callback
;
774 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback
, void *arg
)
777 jack_error("You cannot set callbacks on an active client");
780 fPortRegistrationArg
= arg
;
781 fPortRegistration
= callback
;
786 } // end of namespace