Dmitry Baikov jackmp-time patch: add jack_get_time, jack_time_to_frames, jack_frames_...
[jack2.git] / common / JackClient.cpp
blobbda9c9665d6e4f88da7e8c703a82790ceb4cd8a5
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 case JackNotifyChannelInterface::kRemoveClient:
132 res = ClientNotifyImp(refnum, name, notify, sync, value);
133 break;
135 case JackNotifyChannelInterface::kActivateClient:
136 JackLog("JackClient::kActivateClient name = %s ref = %ld \n", name, refnum);
137 Init();
138 break;
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.
145 if (IsActive()) {
147 switch (notify) {
149 case JackNotifyChannelInterface::kBufferSizeCallback:
150 JackLog("JackClient::kBufferSizeCallback buffer_size = %ld\n", value);
151 if (fBufferSize)
152 res = fBufferSize(value, fBufferSizeArg);
153 break;
155 case JackNotifyChannelInterface::kGraphOrderCallback:
156 JackLog("JackClient::kGraphOrderCallback\n");
157 if (fGraphOrder)
158 res = fGraphOrder(fGraphOrderArg);
159 break;
161 case JackNotifyChannelInterface::kStartFreewheel:
162 JackLog("JackClient::kStartFreewheel\n");
163 SetupDriverSync(true);
164 fThread->DropRealTime();
165 if (fFreewheel)
166 fFreewheel(1, fFreewheelArg);
167 break;
169 case JackNotifyChannelInterface::kStopFreewheel:
170 JackLog("JackClient::kStopFreewheel\n");
171 SetupDriverSync(false);
172 if (fFreewheel)
173 fFreewheel(0, fFreewheelArg);
174 fThread->AcquireRealTime();
175 break;
177 case JackNotifyChannelInterface::kPortRegistrationOn:
178 JackLog("JackClient::kPortRegistrationOn port_index = %ld\n", value);
179 if (fPortRegistration)
180 fPortRegistration(value, 1, fPortRegistrationArg);
181 break;
183 case JackNotifyChannelInterface::kPortRegistrationOff:
184 JackLog("JackClient::kPortRegistrationOff port_index = %ld \n", value);
185 if (fPortRegistration)
186 fPortRegistration(value, 0, fPortRegistrationArg);
187 break;
189 case JackNotifyChannelInterface::kXRunCallback:
190 JackLog("JackClient::kXRunCallback\n");
191 if (fXrun)
192 res = fXrun(fXrunArg);
193 break;
195 case JackNotifyChannelInterface::kZombifyClient:
196 JackLog("JackClient::kZombifyClient name = %s ref = %ld \n", name, refnum);
197 ShutDown();
198 break;
202 return res;
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");
212 if (IsActive())
213 return 0;
215 /* TODO : solve WIN32 thread Kill issue
216 #ifdef WIN32
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);
220 return -1;
222 #endif
225 if (StartThread() < 0)
226 return -1;
228 int result = -1;
229 fChannel->ClientActivate(GetClientControl()->fRefNum, &result);
230 if (result < 0)
231 return 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;
240 return 0;
244 \brief Need to stop thread after deactivating in the server.
246 int JackClient::Deactivate()
248 JackLog("JackClient::Deactivate \n");
249 if (!IsActive())
250 return 0;
252 GetClientControl()->fActive = false;
253 int result = -1;
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
260 #ifdef WIN32
261 fSynchroTable[GetClientControl()->fRefNum]->Disconnect();
262 fThread->Stop();
263 #else
264 fThread->Kill();
265 #endif
267 fThread->Kill();
268 return result;
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()
285 if (fInit) {
286 JackLog("JackClient::Init calling client thread init callback\n");
287 fInit(fInitArg);
289 return true;
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");
304 return -1;
307 if (GetEngineControl()->fRealTime) {
308 if (fThread->AcquireRealTime(GetEngineControl()->fPriority - 1) < 0) {
309 jack_error("AcquireRealTime error");
313 return 0;
317 \brief RT thread.
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");
324 goto error;
327 // Process call
328 if (IsActive()) {
329 CallSyncCallback();
330 bool res = CallProcessCallback();
331 CallTimebaseCallback();
332 if (!res)
333 goto end;
334 } else {
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");
345 return true;
347 end:
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...
356 int result;
357 fThread->DropRealTime();
358 fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
359 Close(); // Not sure...
360 return false;
362 error:
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();
366 ShutDown();
367 return false;
370 //-----------------
371 // Port management
372 //-----------------
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,
388 port_name,
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());
401 int result = -1;
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);
406 if (result == 0) {
407 fPortList.push_back(port_index);
408 return port_index;
409 } else {
410 return 0;
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()) {
420 fPortList.erase(it);
421 int result = -1;
422 fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
423 return result;
424 } else {
425 jack_error("unregistering a port %ld that is not own by the client", port_index);
426 return -1;
430 int JackClient::PortConnect(const char* src, const char* dst)
432 JackLog("JackClient::Connect src = %s dst = %s\n", src, dst);
433 int result = -1;
434 fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
435 return result;
438 int JackClient::PortDisconnect(const char* src, const char* dst)
440 JackLog("JackClient::Disconnect src = %s dst = %s\n", src, dst);
441 int result = -1;
442 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
443 return 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);
449 int result = -1;
450 fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
451 return result;
454 int JackClient::PortDisconnect(jack_port_id_t src)
456 JackLog("JackClient::PortDisconnect src = %ld\n", src);
457 int result = -1;
458 fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
459 return 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)
474 int result = -1;
475 fChannel->SetBufferSize(buffer_size, &result);
476 return result;
479 int JackClient::SetFreeWheel(int onoff)
481 int result = -1;
482 fChannel->SetFreewheel(onoff, &result);
483 return result;
487 ShutDown is called:
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");
496 if (fShutdown) {
497 GetClientControl()->fActive = false;
498 fShutdown(fShutdownArg);
499 fShutdown = NULL;
503 //----------------------
504 // Transport management
505 //----------------------
507 int JackClient::ReleaseTimebase()
509 int result = -1;
510 fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
511 if (result == 0) {
512 fTimebase = NULL;
513 fTimebaseArg = NULL;
515 return result;
518 /* Call the server if the client is active, otherwise keeps the arguments */
519 int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
521 if (IsActive())
522 GetClientControl()->fTransportState = (sync_callback == NULL) ? JackTransportStopped : JackTransportSynching;
523 fSync = sync_callback;
524 fSyncArg = arg;
525 return 0;
528 int JackClient::SetSyncTimeout(jack_time_t timeout)
530 GetEngineControl()->fTransport.SetSyncTimeout(timeout);
531 return 0;
534 /* Call the server if the client is active, otherwise keeps the arguments */
535 int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
537 if (IsActive()) {
538 int result = -1;
539 fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
540 JackLog("SetTimebaseCallback result = %ld\n", result);
541 if (result == 0) {
542 fTimebase = timebase_callback;
543 fTimebaseArg = arg;
544 } else {
545 fTimebase = NULL;
546 fTimebaseArg = NULL;
548 JackLog("SetTimebaseCallback OK result = %ld\n", result);
549 return result;
550 } else {
551 fTimebase = timebase_callback;
552 fTimebaseArg = arg;
553 fConditionnal = conditional;
554 return 0;
558 // Must be RT safe
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);
567 return 0;
570 int JackClient::TransportLocate(jack_nframes_t frame)
572 jack_position_t pos;
573 pos.frame = 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)
588 if (pos)
589 GetEngineControl()->fTransport.ReadCurrentPos(pos);
590 return GetEngineControl()->fTransport.GetState();
593 jack_nframes_t JackClient::GetCurrentTransportFrame()
595 jack_position_t pos;
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;
602 } else {
603 return pos.frame;
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...
631 if (fSync == NULL) {
632 GetClientControl()->fTransportState = JackTransportRolling;
633 } else if (GetClientControl()->fTransportState == JackTransportStarting) {
634 if (fSync(transport_state, cur_pos, fSyncArg))
635 GetClientControl()->fTransportState = JackTransportRolling;
637 break;
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;
644 break;
646 case JackTransportSynching:
647 // New pos when transport engine is stopped...
648 if (fSync != NULL) {
649 fSync(JackTransportStopped, cur_pos, fSyncArg);
650 GetClientControl()->fTransportState = JackTransportStopped;
652 break;
654 default:
655 break;
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);
672 break;
674 case JackTransportSynching:
675 fTimebase(JackTransportStopped, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
676 break;
678 default:
679 break;
682 transport.WriteNextStateStop(1);
686 //---------------------
687 // Callback management
688 //---------------------
690 void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
692 if (IsActive()) {
693 jack_error("You cannot set callbacks on an active client");
694 } else {
695 fShutdownArg = arg;
696 fShutdown = callback;
700 int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
702 if (IsActive()) {
703 jack_error("You cannot set callbacks on an active client");
704 return -1;
705 } else {
706 fProcessArg = arg;
707 fProcess = callback;
708 return 0;
712 int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
714 if (IsActive()) {
715 jack_error("You cannot set callbacks on an active client");
716 return -1;
717 } else {
718 fXrunArg = arg;
719 fXrun = callback;
720 return 0;
724 int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
726 if (IsActive()) {
727 jack_error("You cannot set callbacks on an active client");
728 return -1;
729 } else {
730 fInitArg = arg;
731 fInit = callback;
732 return 0;
736 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
738 JackLog("SetGraphOrderCallback \n");
740 if (IsActive()) {
741 jack_error("You cannot set callbacks on an active client");
742 return -1;
743 } else {
744 fGraphOrder = callback;
745 fGraphOrderArg = arg;
746 return 0;
750 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
752 if (IsActive()) {
753 jack_error("You cannot set callbacks on an active client");
754 return -1;
755 } else {
756 fBufferSizeArg = arg;
757 fBufferSize = callback;
758 return 0;
762 int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
764 if (IsActive()) {
765 jack_error("You cannot set callbacks on an active client");
766 return -1;
767 } else {
768 fFreewheelArg = arg;
769 fFreewheel = callback;
770 return 0;
774 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
776 if (IsActive()) {
777 jack_error("You cannot set callbacks on an active client");
778 return -1;
779 } else {
780 fPortRegistrationArg = arg;
781 fPortRegistration = callback;
782 return 0;
786 } // end of namespace