Jack2  1.9.9
JackClient.cpp
1 /*
2 Copyright (C) 2001 Paul Davis
3 Copyright (C) 2004-2008 Grame
4 
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.
9 
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.
14 
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.
18 
19 */
20 
21 #include "JackSystemDeps.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"
30 
31 
32 #include <math.h>
33 #include <string>
34 #include <algorithm>
35 
36 using namespace std;
37 
38 namespace Jack
39 {
40 
41 #define IsRealTime() ((fProcess != NULL) | (fThreadFun != NULL) | (fSync != NULL) | (fTimebase != NULL))
42 
43 JackClient::JackClient():fThread(this)
44 {}
45 
46 JackClient::JackClient(JackSynchro* table):fThread(this)
47 {
48  fSynchroTable = table;
49  fProcess = NULL;
50  fGraphOrder = NULL;
51  fXrun = NULL;
52  fShutdown = NULL;
53  fInfoShutdown = NULL;
54  fInit = NULL;
55  fBufferSize = NULL;
56  fClientRegistration = NULL;
57  fFreewheel = NULL;
58  fPortRegistration = NULL;
59  fPortConnect = NULL;
60  fPortRename = NULL;
61  fTimebase = NULL;
62  fSync = NULL;
63  fThreadFun = NULL;
64  fSession = NULL;
65  fLatency = NULL;
66 
67  fProcessArg = NULL;
68  fGraphOrderArg = NULL;
69  fXrunArg = NULL;
70  fShutdownArg = NULL;
71  fInfoShutdownArg = NULL;
72  fInitArg = NULL;
73  fBufferSizeArg = NULL;
74  fFreewheelArg = NULL;
75  fClientRegistrationArg = NULL;
76  fPortRegistrationArg = NULL;
77  fPortConnectArg = NULL;
78  fPortRenameArg = NULL;
79  fSyncArg = NULL;
80  fTimebaseArg = NULL;
81  fThreadFunArg = NULL;
82  fSessionArg = NULL;
83  fLatencyArg = NULL;
84 
85  fSessionReply = kPendingSessionReply;
86 }
87 
88 JackClient::~JackClient()
89 {}
90 
91 void JackClient::ShutDown()
92 {
93  jack_log("JackClient::ShutDown");
94 
95  if (fInfoShutdown) {
96  fInfoShutdown(JackFailure, "JACK server has been closed", fInfoShutdownArg);
97  fInfoShutdown = NULL;
98  } else if (fShutdown) {
99  fShutdown(fShutdownArg);
100  fShutdown = NULL;
101  }
102 }
103 
104 int JackClient::Close()
105 {
106  jack_log("JackClient::Close ref = %ld", GetClientControl()->fRefNum);
107  int result = 0;
108 
109  Deactivate();
110  fChannel->Stop(); // Channels is stopped first to avoid receiving notifications while closing
111 
112  // Request close only if server is still running
113  if (JackGlobals::fServerRunning) {
114  fChannel->ClientClose(GetClientControl()->fRefNum, &result);
115  } else {
116  jack_log("JackClient::Close server is shutdown");
117  }
118 
119  fChannel->Close();
120  fSynchroTable[GetClientControl()->fRefNum].Disconnect();
121  JackGlobals::fClientTable[GetClientControl()->fRefNum] = NULL;
122  return result;
123 }
124 
125 bool JackClient::IsActive()
126 {
127  return (GetClientControl()) ? GetClientControl()->fActive : false;
128 }
129 
130 jack_native_thread_t JackClient::GetThreadID()
131 {
132  return fThread.GetThreadID();
133 }
134 
140 void JackClient::SetupDriverSync(bool freewheel)
141 {
142  if (!freewheel && !GetEngineControl()->fSyncMode) {
143  jack_log("JackClient::SetupDriverSync driver sem in flush mode");
144  for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
145  fSynchroTable[i].SetFlush(true);
146  }
147  } else {
148  jack_log("JackClient::SetupDriverSync driver sem in normal mode");
149  for (int i = 0; i < GetEngineControl()->fDriverNum; i++)
150  fSynchroTable[i].SetFlush(false);
151  }
152 }
153 
158 int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
159 {
160  return 0;
161 }
162 
163 int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
164 {
165  int res = 0;
166 
167  jack_log("JackClient::ClientNotify ref = %ld name = %s notify = %ld", refnum, name, notify);
168 
169  // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
170  switch (notify) {
171 
172  case kAddClient:
173  res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
174  break;
175 
176  case kRemoveClient:
177  res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
178  break;
179 
180  case kActivateClient:
181  jack_log("JackClient::kActivateClient name = %s ref = %ld ", name, refnum);
182  InitAux();
183  break;
184  }
185 
186  /*
187  The current semantic is that notifications can only be received when the client has been activated,
188  although is this implementation, one could imagine calling notifications as soon as the client has be opened.
189  */
190  if (IsActive()) {
191 
192  switch (notify) {
193 
194  case kAddClient:
195  jack_log("JackClient::kAddClient fName = %s name = %s", GetClientControl()->fName, name);
196  if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
197  fClientRegistration(name, 1, fClientRegistrationArg);
198  }
199  break;
200 
201  case kRemoveClient:
202  jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name);
203  if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
204  fClientRegistration(name, 0, fClientRegistrationArg);
205  }
206  break;
207 
208  case kBufferSizeCallback:
209  jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1);
210  if (fBufferSize) {
211  res = fBufferSize(value1, fBufferSizeArg);
212  }
213  break;
214 
215  case kSampleRateCallback:
216  jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1);
217  if (fSampleRate) {
218  res = fSampleRate(value1, fSampleRateArg);
219  }
220  break;
221 
222  case kGraphOrderCallback:
223  jack_log("JackClient::kGraphOrderCallback");
224  if (fGraphOrder) {
225  res = fGraphOrder(fGraphOrderArg);
226  }
227  break;
228 
229  case kStartFreewheelCallback:
230  jack_log("JackClient::kStartFreewheel");
231  SetupDriverSync(true);
232  fThread.DropRealTime(); // Always done (JACK server in RT mode or not...)
233  if (fFreewheel) {
234  fFreewheel(1, fFreewheelArg);
235  }
236  break;
237 
238  case kStopFreewheelCallback:
239  jack_log("JackClient::kStopFreewheel");
240  SetupDriverSync(false);
241  if (fFreewheel) {
242  fFreewheel(0, fFreewheelArg);
243  }
244  if (GetEngineControl()->fRealTime) {
245  if (fThread.AcquireRealTime() < 0) {
246  jack_error("JackClient::AcquireRealTime error");
247  }
248  }
249  break;
250 
251  case kPortRegistrationOnCallback:
252  jack_log("JackClient::kPortRegistrationOn port_index = %ld", value1);
253  if (fPortRegistration) {
254  fPortRegistration(value1, 1, fPortRegistrationArg);
255  }
256  break;
257 
258  case kPortRegistrationOffCallback:
259  jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1);
260  if (fPortRegistration) {
261  fPortRegistration(value1, 0, fPortRegistrationArg);
262  }
263  break;
264 
265  case kPortConnectCallback:
266  jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2);
267  if (fPortConnect) {
268  fPortConnect(value1, value2, 1, fPortConnectArg);
269  }
270  break;
271 
272  case kPortDisconnectCallback:
273  jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2);
274  if (fPortConnect) {
275  fPortConnect(value1, value2, 0, fPortConnectArg);
276  }
277  break;
278 
279  case kPortRenameCallback:
280  jack_log("JackClient::kPortRenameCallback port = %ld", value1);
281  if (fPortRename) {
282  fPortRename(value1, message, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg);
283  }
284  break;
285 
286  case kXRunCallback:
287  jack_log("JackClient::kXRunCallback");
288  if (fXrun) {
289  res = fXrun(fXrunArg);
290  }
291  break;
292 
293  case kShutDownCallback:
294  jack_log("JackClient::kShutDownCallback");
295  if (fInfoShutdown) {
296  fInfoShutdown((jack_status_t)value1, message, fInfoShutdownArg);
297  fInfoShutdown = NULL;
298  }
299  break;
300 
301  case kSessionCallback:
302  jack_log("JackClient::kSessionCallback");
303  if (fSession) {
305  char uuid_buf[JACK_UUID_SIZE];
306  event->type = (jack_session_event_type_t)value1;
307  event->session_dir = strdup(message);
308  event->command_line = NULL;
309  event->flags = (jack_session_flags_t)0;
310  snprintf(uuid_buf, sizeof(uuid_buf), "%d", GetClientControl()->fSessionID);
311  event->client_uuid = strdup(uuid_buf);
312  fSessionReply = kPendingSessionReply;
313  // Session callback may change fSessionReply by directly using jack_session_reply
314  fSession(event, fSessionArg);
315  res = fSessionReply;
316  }
317  break;
318 
319  case kLatencyCallback:
320  res = HandleLatencyCallback(value1);
321  break;
322  }
323  }
324 
325  return res;
326 }
327 
328 int JackClient::HandleLatencyCallback(int status)
329 {
330  jack_latency_callback_mode_t mode = (status == 0) ? JackCaptureLatency : JackPlaybackLatency;
331  jack_latency_range_t latency = { UINT32_MAX, 0 };
332 
333  /* first setup all latency values of the ports.
334  * this is based on the connections of the ports.
335  */
336  list<jack_port_id_t>::iterator it;
337 
338  for (it = fPortList.begin(); it != fPortList.end(); it++) {
339  JackPort* port = GetGraphManager()->GetPort(*it);
340  if ((port->GetFlags() & JackPortIsOutput) && (mode == JackPlaybackLatency)) {
341  GetGraphManager()->RecalculateLatency(*it, mode);
342  }
343  if ((port->GetFlags() & JackPortIsInput) && (mode == JackCaptureLatency)) {
344  GetGraphManager()->RecalculateLatency(*it, mode);
345  }
346  }
347 
348  if (!fLatency) {
349  /*
350  * default action is to assume all ports depend on each other.
351  * then always take the maximum latency.
352  */
353 
354  if (mode == JackPlaybackLatency) {
355  /* iterate over all OutputPorts, to find maximum playback latency
356  */
357  for (it = fPortList.begin(); it != fPortList.end(); it++) {
358  JackPort* port = GetGraphManager()->GetPort(*it);
359  if (port->GetFlags() & JackPortIsOutput) {
360  jack_latency_range_t other_latency;
361  port->GetLatencyRange(mode, &other_latency);
362  if (other_latency.max > latency.max) {
363  latency.max = other_latency.max;
364  }
365  if (other_latency.min < latency.min) {
366  latency.min = other_latency.min;
367  }
368  }
369  }
370 
371  if (latency.min == UINT32_MAX) {
372  latency.min = 0;
373  }
374 
375  /* now set the found latency on all input ports
376  */
377  for (it = fPortList.begin(); it != fPortList.end(); it++) {
378  JackPort* port = GetGraphManager()->GetPort(*it);
379  if (port->GetFlags() & JackPortIsInput) {
380  port->SetLatencyRange(mode, &latency);
381  }
382  }
383  }
384  if (mode == JackCaptureLatency) {
385  /* iterate over all InputPorts, to find maximum playback latency
386  */
387  for (it = fPortList.begin(); it != fPortList.end(); it++) {
388  JackPort* port = GetGraphManager()->GetPort(*it);
389  if (port->GetFlags() & JackPortIsInput) {
390  jack_latency_range_t other_latency;
391  port->GetLatencyRange(mode, &other_latency);
392  if (other_latency.max > latency.max) {
393  latency.max = other_latency.max;
394  }
395  if (other_latency.min < latency.min) {
396  latency.min = other_latency.min;
397  }
398  }
399  }
400 
401  if (latency.min == UINT32_MAX) {
402  latency.min = 0;
403  }
404 
405  /* now set the found latency on all output ports
406  */
407  for (it = fPortList.begin(); it != fPortList.end(); it++) {
408  JackPort* port = GetGraphManager()->GetPort(*it);
409  if (port->GetFlags() & JackPortIsOutput) {
410  port->SetLatencyRange(mode, &latency);
411  }
412  }
413  }
414  return 0;
415  }
416 
417  /* we have a latency callback setup by the client,
418  * lets use it...
419  */
420  fLatency(mode, fLatencyArg);
421  return 0;
422 }
423 
429 {
430  jack_log("JackClient::Activate");
431  if (IsActive()) {
432  return 0;
433  }
434 
435  // RT thread is started only when needed...
436  if (IsRealTime()) {
437  if (StartThread() < 0) {
438  return -1;
439  }
440  }
441 
442  /*
443  Insertion of client in the graph will cause a kGraphOrderCallback notification
444  to be delivered by the server, the client wants to receive it.
445  */
446  GetClientControl()->fActive = true;
447 
448  // Transport related callback become "active"
449  GetClientControl()->fTransportSync = true;
450  GetClientControl()->fTransportTimebase = true;
451 
452  int result = -1;
453  GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
454  fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
455  return result;
456 }
457 
462 {
463  jack_log("JackClient::Deactivate");
464  if (!IsActive()) {
465  return 0;
466  }
467 
468  GetClientControl()->fActive = false;
469 
470  // Transport related callback become "unactive"
471  GetClientControl()->fTransportSync = false;
472  GetClientControl()->fTransportTimebase = false;
473 
474  // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
475  int result = -1;
476  fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
477  jack_log("JackClient::Deactivate res = %ld", result);
478 
479  // RT thread is stopped only when needed...
480  if (IsRealTime()) {
481  fThread.Kill();
482  }
483  return result;
484 }
485 
486 //----------------------
487 // RT thread management
488 //----------------------
489 
490 void JackClient::InitAux()
491 {
492  if (fInit) {
493  jack_log("JackClient::Init calling client thread init callback");
494  fInit(fInitArg);
495  }
496 }
497 
502 {
503  /*
504  Execute buffer_size callback.
505 
506  Since StartThread uses fThread.StartSync, we are sure that buffer_size callback
507  is executed before StartThread returns (and then IsActive will be true).
508  So no RT callback can be called at the same time.
509  */
510  jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", GetEngineControl()->fBufferSize);
511  if (fBufferSize) {
512  fBufferSize(GetEngineControl()->fBufferSize, fBufferSizeArg);
513  }
514 
515  // Init callback
516  InitAux();
517 
518  // Setup context
519  if (!jack_tls_set(JackGlobals::fRealTimeThread, this)) {
520  jack_error("Failed to set thread realtime key");
521  }
522 
523  if (GetEngineControl()->fRealTime) {
524  set_threaded_log_function();
525  }
526 
527  // Setup RT
528  if (GetEngineControl()->fRealTime) {
529  if (fThread.AcquireSelfRealTime(GetEngineControl()->fClientPriority) < 0) {
530  jack_error("JackClient::AcquireSelfRealTime error");
531  }
532  }
533 
534  return true;
535 }
536 
537 int JackClient::StartThread()
538 {
539  jack_log("JackClient::StartThread : period = %ld computation = %ld constraint = %ld",
540  long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
541  long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
542  long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));
543 
544  // Will do "something" on OSX only...
545  fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
546 
547  if (fThread.StartSync() < 0) {
548  jack_error("Start thread error");
549  return -1;
550  }
551 
552  return 0;
553 }
554 
560 {
561  // Execute a dummy cycle to be sure thread has the correct properties
562  DummyCycle();
563 
564  if (fThreadFun) {
565  fThreadFun(fThreadFunArg);
566  } else {
567  ExecuteThread();
568  }
569  return false;
570 }
571 
572 void JackClient::DummyCycle()
573 {
574  WaitSync();
575  SignalSync();
576 }
577 
578 inline void JackClient::ExecuteThread()
579 {
580  while (true) {
581  CycleWaitAux();
582  CycleSignalAux(CallProcessCallback());
583  }
584 }
585 
586 inline jack_nframes_t JackClient::CycleWaitAux()
587 {
588  if (!WaitSync()) {
589  Error(); // Terminates the thread
590  }
591  CallSyncCallbackAux();
592  return GetEngineControl()->fBufferSize;
593 }
594 
595 inline void JackClient::CycleSignalAux(int status)
596 {
597  if (status == 0) {
598  CallTimebaseCallbackAux();
599  }
600  SignalSync();
601  if (status != 0) {
602  End(); // Terminates the thread
603  }
604 }
605 
606 jack_nframes_t JackClient::CycleWait()
607 {
608  return CycleWaitAux();
609 }
610 
611 void JackClient::CycleSignal(int status)
612 {
613  CycleSignalAux(status);
614 }
615 
616 inline int JackClient::CallProcessCallback()
617 {
618  return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0;
619 }
620 
621 inline bool JackClient::WaitSync()
622 {
623  // Suspend itself: wait on the input synchro
624  if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
625  jack_error("SuspendRefNum error");
626  return false;
627  } else {
628  return true;
629  }
630 }
631 
632 inline void JackClient::SignalSync()
633 {
634  // Resume: signal output clients connected to the running client
635  if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
636  jack_error("ResumeRefNum error");
637  }
638 }
639 
640 inline void JackClient::End()
641 {
642  jack_log("JackClient::Execute end name = %s", GetClientControl()->fName);
643  // Hum... not sure about this, the following "close" code is called in the RT thread...
644  int result;
645  fThread.DropSelfRealTime();
646  GetClientControl()->fActive = false;
647  fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
648  fThread.Terminate();
649 }
650 
651 inline void JackClient::Error()
652 {
653  jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
654  // Hum... not sure about this, the following "close" code is called in the RT thread...
655  int result;
656  fThread.DropSelfRealTime();
657  GetClientControl()->fActive = false;
658  fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
659  ShutDown();
660  fThread.Terminate();
661 }
662 
663 //-----------------
664 // Port management
665 //-----------------
666 
667 int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
668 {
669  // Check if port name is empty
670  string port_name_str = string(port_name);
671  if (port_name_str.size() == 0) {
672  jack_error("port_name is empty");
673  return 0; // Means failure here...
674  }
675 
676  // Check port name length
677  string name = string(GetClientControl()->fName) + string(":") + port_name_str;
678  if (name.size() >= REAL_JACK_PORT_NAME_SIZE) {
679  jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
680  "Please use %lu characters or less",
681  GetClientControl()->fName,
682  port_name,
683  JACK_PORT_NAME_SIZE - 1);
684  return 0; // Means failure here...
685  }
686 
687  int result = -1;
688  jack_port_id_t port_index = NO_PORT;
689  fChannel->PortRegister(GetClientControl()->fRefNum, name.c_str(), port_type, flags, buffer_size, &port_index, &result);
690 
691  if (result == 0) {
692  jack_log("JackClient::PortRegister ref = %ld name = %s type = %s port_index = %ld", GetClientControl()->fRefNum, name.c_str(), port_type, port_index);
693  fPortList.push_back(port_index);
694  return port_index;
695  } else {
696  return 0;
697  }
698 }
699 
700 int JackClient::PortUnRegister(jack_port_id_t port_index)
701 {
702  jack_log("JackClient::PortUnRegister port_index = %ld", port_index);
703  list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
704 
705  if (it != fPortList.end()) {
706  fPortList.erase(it);
707  int result = -1;
708  fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
709  return result;
710  } else {
711  jack_error("unregistering a port %ld that is not own by the client", port_index);
712  return -1;
713  }
714 }
715 
716 int JackClient::PortConnect(const char* src, const char* dst)
717 {
718  jack_log("JackClient::Connect src = %s dst = %s", src, dst);
719  int result = -1;
720  fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
721  return result;
722 }
723 
724 int JackClient::PortDisconnect(const char* src, const char* dst)
725 {
726  jack_log("JackClient::Disconnect src = %s dst = %s", src, dst);
727  int result = -1;
728  fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
729  return result;
730 }
731 
732 int JackClient::PortDisconnect(jack_port_id_t src)
733 {
734  jack_log("JackClient::PortDisconnect src = %ld", src);
735  int result = -1;
736  fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
737  return result;
738 }
739 
740 int JackClient::PortIsMine(jack_port_id_t port_index)
741 {
742  JackPort* port = GetGraphManager()->GetPort(port_index);
743  return GetClientControl()->fRefNum == port->GetRefNum();
744 }
745 
746 int JackClient::PortRename(jack_port_id_t port_index, const char* name)
747 {
748  int result = -1;
749  fChannel->PortRename(GetClientControl()->fRefNum, port_index, name, &result);
750  return result;
751 }
752 
753 //--------------------
754 // Context management
755 //--------------------
756 
757 int JackClient::SetBufferSize(jack_nframes_t buffer_size)
758 {
759  int result = -1;
760  fChannel->SetBufferSize(buffer_size, &result);
761  return result;
762 }
763 
764 int JackClient::SetFreeWheel(int onoff)
765 {
766  int result = -1;
767  fChannel->SetFreewheel(onoff, &result);
768  return result;
769 }
770 
771 int JackClient::ComputeTotalLatencies()
772 {
773  int result = -1;
774  fChannel->ComputeTotalLatencies(&result);
775  return result;
776 }
777 
778 //----------------------
779 // Transport management
780 //----------------------
781 
782 inline int JackClient::ActivateAux()
783 {
784  // If activated without RT thread...
785  if (IsActive() && fThread.GetStatus() != JackThread::kRunning) {
786 
787  jack_log("JackClient::ActivateAux");
788 
789  // RT thread is started
790  if (StartThread() < 0) {
791  return -1;
792  }
793 
794  int result = -1;
795  GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
796  fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
797  return result;
798 
799  } else {
800  return 0;
801  }
802 }
803 
804 int JackClient::ReleaseTimebase()
805 {
806  int result = -1;
807  fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
808  if (result == 0) {
809  GetClientControl()->fTransportTimebase = false;
810  fTimebase = NULL;
811  fTimebaseArg = NULL;
812  }
813  return result;
814 }
815 
816 /* Call the server if the client is active, otherwise keeps the arguments */
817 int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
818 {
819  GetClientControl()->fTransportSync = (fSync != NULL);
820  fSyncArg = arg;
821  fSync = sync_callback;
822  return ActivateAux();
823 }
824 
825 int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
826 {
827  int result = -1;
828  fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
829 
830  if (result == 0) {
831  GetClientControl()->fTransportTimebase = true;
832  fTimebase = timebase_callback;
833  fTimebaseArg = arg;
834  return ActivateAux();
835  } else {
836  fTimebase = NULL;
837  fTimebaseArg = NULL;
838  return -1;
839  }
840 }
841 
842 int JackClient::SetSyncTimeout(jack_time_t timeout)
843 {
844  GetEngineControl()->fTransport.SetSyncTimeout(timeout);
845  return 0;
846 }
847 
848 // Must be RT safe
849 
850 void JackClient::TransportLocate(jack_nframes_t frame)
851 {
852  jack_position_t pos;
853  pos.frame = frame;
854  pos.valid = (jack_position_bits_t)0;
855  jack_log("JackClient::TransportLocate pos = %ld", pos.frame);
856  GetEngineControl()->fTransport.RequestNewPos(&pos);
857 }
858 
859 int JackClient::TransportReposition(const jack_position_t* pos)
860 {
861  jack_position_t tmp = *pos;
862  jack_log("JackClient::TransportReposition pos = %ld", pos->frame);
863  if (tmp.valid & ~JACK_POSITION_MASK) {
864  return EINVAL;
865  } else {
866  GetEngineControl()->fTransport.RequestNewPos(&tmp);
867  return 0;
868  }
869 }
870 
871 jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
872 {
873  return GetEngineControl()->fTransport.Query(pos);
874 }
875 
876 jack_nframes_t JackClient::GetCurrentTransportFrame()
877 {
878  return GetEngineControl()->fTransport.GetCurrentFrame();
879 }
880 
881 // Must be RT safe: directly write in the transport shared mem
882 void JackClient::TransportStart()
883 {
884  GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
885 }
886 
887 // Must be RT safe: directly write in the transport shared mem
888 void JackClient::TransportStop()
889 {
890  GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
891 }
892 
893 // Never called concurently with the server
894 // TODO check concurrency with SetSyncCallback
895 
896 void JackClient::CallSyncCallback()
897 {
898  CallSyncCallbackAux();
899 }
900 
901 inline void JackClient::CallSyncCallbackAux()
902 {
903  if (GetClientControl()->fTransportSync) {
904 
905  JackTransportEngine& transport = GetEngineControl()->fTransport;
906  jack_position_t* cur_pos = transport.ReadCurrentState();
907  jack_transport_state_t transport_state = transport.GetState();
908 
909  if (fSync != NULL) {
910  if (fSync(transport_state, cur_pos, fSyncArg)) {
911  GetClientControl()->fTransportState = JackTransportRolling;
912  GetClientControl()->fTransportSync = false;
913  }
914  } else {
915  GetClientControl()->fTransportState = JackTransportRolling;
916  GetClientControl()->fTransportSync = false;
917  }
918  }
919 }
920 
921 void JackClient::CallTimebaseCallback()
922 {
923  CallTimebaseCallbackAux();
924 }
925 
926 inline void JackClient::CallTimebaseCallbackAux()
927 {
928  JackTransportEngine& transport = GetEngineControl()->fTransport;
929  int master;
930  bool unused;
931 
932  transport.GetTimebaseMaster(master, unused);
933 
934  if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase...
935 
936  jack_transport_state_t transport_state = transport.GetState();
937  jack_position_t* cur_pos = transport.WriteNextStateStart(1);
938 
939  if (GetClientControl()->fTransportTimebase) {
940  fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
941  GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true
942  } else if (transport_state == JackTransportRolling) {
943  fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
944  }
945 
946  transport.WriteNextStateStop(1);
947  }
948 }
949 
950 //---------------------
951 // Callback management
952 //---------------------
953 
954 void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
955 {
956  if (IsActive()) {
957  jack_error("You cannot set callbacks on an active client");
958  } else {
959  fShutdownArg = arg;
960  fShutdown = callback;
961  }
962 }
963 
964 void JackClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg)
965 {
966  if (IsActive()) {
967  jack_error("You cannot set callbacks on an active client");
968  } else {
969  GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
970  fInfoShutdownArg = arg;
971  fInfoShutdown = callback;
972  }
973 }
974 
975 int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
976 {
977  if (IsActive()) {
978  jack_error("You cannot set callbacks on an active client");
979  return -1;
980  } else if (fThreadFun) {
981  jack_error ("A thread callback has already been setup, both models cannot be used at the same time!");
982  return -1;
983  } else {
984  fProcessArg = arg;
985  fProcess = callback;
986  return 0;
987  }
988 }
989 
990 int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
991 {
992  if (IsActive()) {
993  jack_error("You cannot set callbacks on an active client");
994  return -1;
995  } else {
996  GetClientControl()->fCallback[kXRunCallback] = (callback != NULL);
997  fXrunArg = arg;
998  fXrun = callback;
999  return 0;
1000  }
1001 }
1002 
1003 int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
1004 {
1005  if (IsActive()) {
1006  jack_error("You cannot set callbacks on an active client");
1007  return -1;
1008  } else {
1009  fInitArg = arg;
1010  fInit = callback;
1011  /* make sure that the message buffer thread is initialized too */
1012  return JackMessageBuffer::fInstance->SetInitCallback(callback, arg);
1013  }
1014 }
1015 
1016 int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
1017 {
1018  if (IsActive()) {
1019  jack_error("You cannot set callbacks on an active client");
1020  return -1;
1021  } else {
1022  GetClientControl()->fCallback[kGraphOrderCallback] = (callback != NULL);
1023  fGraphOrder = callback;
1024  fGraphOrderArg = arg;
1025  return 0;
1026  }
1027 }
1028 
1029 int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
1030 {
1031  if (IsActive()) {
1032  jack_error("You cannot set callbacks on an active client");
1033  return -1;
1034  } else {
1035  GetClientControl()->fCallback[kBufferSizeCallback] = (callback != NULL);
1036  fBufferSizeArg = arg;
1037  fBufferSize = callback;
1038  return 0;
1039  }
1040 }
1041 
1042 int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg)
1043 {
1044  if (IsActive()) {
1045  jack_error("You cannot set callbacks on an active client");
1046  return -1;
1047  } else {
1048  GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL);
1049  fSampleRateArg = arg;
1050  fSampleRate = callback;
1051  // Now invoke it
1052  if (callback) {
1053  callback(GetEngineControl()->fSampleRate, arg);
1054  }
1055  return 0;
1056  }
1057 }
1058 
1059 int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg)
1060 {
1061  if (IsActive()) {
1062  jack_error("You cannot set callbacks on an active client");
1063  return -1;
1064  } else {
1065  // kAddClient and kRemoveClient notifications must be delivered by the server in any case
1066  fClientRegistrationArg = arg;
1067  fClientRegistration = callback;
1068  return 0;
1069  }
1070 }
1071 
1072 int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
1073 {
1074  if (IsActive()) {
1075  jack_error("You cannot set callbacks on an active client");
1076  return -1;
1077  } else {
1078  GetClientControl()->fCallback[kStartFreewheelCallback] = (callback != NULL);
1079  GetClientControl()->fCallback[kStopFreewheelCallback] = (callback != NULL);
1080  fFreewheelArg = arg;
1081  fFreewheel = callback;
1082  return 0;
1083  }
1084 }
1085 
1086 int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
1087 {
1088  if (IsActive()) {
1089  jack_error("You cannot set callbacks on an active client");
1090  return -1;
1091  } else {
1092  GetClientControl()->fCallback[kPortRegistrationOnCallback] = (callback != NULL);
1093  GetClientControl()->fCallback[kPortRegistrationOffCallback] = (callback != NULL);
1094  fPortRegistrationArg = arg;
1095  fPortRegistration = callback;
1096  return 0;
1097  }
1098 }
1099 
1100 int JackClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg)
1101 {
1102  if (IsActive()) {
1103  jack_error("You cannot set callbacks on an active client");
1104  return -1;
1105  } else {
1106  GetClientControl()->fCallback[kPortConnectCallback] = (callback != NULL);
1107  GetClientControl()->fCallback[kPortDisconnectCallback] = (callback != NULL);
1108  fPortConnectArg = arg;
1109  fPortConnect = callback;
1110  return 0;
1111  }
1112 }
1113 
1114 int JackClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg)
1115 {
1116  if (IsActive()) {
1117  jack_error("You cannot set callbacks on an active client");
1118  return -1;
1119  } else {
1120  GetClientControl()->fCallback[kPortRenameCallback] = (callback != NULL);
1121  fPortRenameArg = arg;
1122  fPortRename = callback;
1123  return 0;
1124  }
1125 }
1126 
1127 int JackClient::SetProcessThread(JackThreadCallback fun, void *arg)
1128 {
1129  if (IsActive()) {
1130  jack_error("You cannot set callbacks on an active client");
1131  return -1;
1132  } else if (fProcess) {
1133  jack_error("A process callback has already been setup, both models cannot be used at the same time!");
1134  return -1;
1135  } else {
1136  fThreadFun = fun;
1137  fThreadFunArg = arg;
1138  return 0;
1139  }
1140 }
1141 
1142 int JackClient::SetSessionCallback(JackSessionCallback callback, void *arg)
1143 {
1144  if (IsActive()) {
1145  jack_error("You cannot set callbacks on an active client");
1146  return -1;
1147  } else {
1148  GetClientControl()->fCallback[kSessionCallback] = (callback != NULL);
1149  fSessionArg = arg;
1150  fSession = callback;
1151  return 0;
1152  }
1153 }
1154 
1155 int JackClient::SetLatencyCallback(JackLatencyCallback callback, void *arg)
1156 {
1157  if (IsActive()) {
1158  jack_error("You cannot set callbacks on an active client");
1159  return -1;
1160  } else {
1161  // fCallback[kLatencyCallback] must always be 'true'
1162  fLatencyArg = arg;
1163  fLatency = callback;
1164  return 0;
1165  }
1166 }
1167 
1168 //------------------
1169 // Internal clients
1170 //------------------
1171 
1172 char* JackClient::GetInternalClientName(int ref)
1173 {
1174  char name_res[JACK_CLIENT_NAME_SIZE + 1];
1175  int result = -1;
1176  fChannel->GetInternalClientName(GetClientControl()->fRefNum, ref, name_res, &result);
1177  return (result < 0) ? NULL : strdup(name_res);
1178 }
1179 
1180 int JackClient::InternalClientHandle(const char* client_name, jack_status_t* status)
1181 {
1182  int int_ref, result = -1;
1183  fChannel->InternalClientHandle(GetClientControl()->fRefNum, client_name, (int*)status, &int_ref, &result);
1184  return int_ref;
1185 }
1186 
1187 int JackClient::InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va)
1188 {
1189  if (strlen(client_name) >= JACK_CLIENT_NAME_SIZE) {
1190  jack_error ("\"%s\" is too long for a JACK client name.\n"
1191  "Please use %lu characters or less.",
1192  client_name, JACK_CLIENT_NAME_SIZE);
1193  return 0;
1194  }
1195 
1196  if (va->load_name && (strlen(va->load_name) >= JACK_PATH_MAX)) {
1197  jack_error("\"%s\" is too long for a shared object name.\n"
1198  "Please use %lu characters or less.",
1199  va->load_name, JACK_PATH_MAX);
1200  int my_status1 = *status | (JackFailure | JackInvalidOption);
1201  *status = (jack_status_t)my_status1;
1202  return 0;
1203  }
1204 
1205  if (va->load_init && (strlen(va->load_init) >= JACK_LOAD_INIT_LIMIT)) {
1206  jack_error ("\"%s\" is too long for internal client init "
1207  "string.\nPlease use %lu characters or less.",
1208  va->load_init, JACK_LOAD_INIT_LIMIT);
1209  int my_status1 = *status | (JackFailure | JackInvalidOption);
1210  *status = (jack_status_t)my_status1;
1211  return 0;
1212  }
1213 
1214  int int_ref, result = -1;
1215  fChannel->InternalClientLoad(GetClientControl()->fRefNum, client_name, va->load_name, va->load_init, options, (int*)status, &int_ref, -1, &result);
1216  return int_ref;
1217 }
1218 
1219 void JackClient::InternalClientUnload(int ref, jack_status_t* status)
1220 {
1221  int result = -1;
1222  fChannel->InternalClientUnload(GetClientControl()->fRefNum, ref, (int*)status, &result);
1223 }
1224 
1225 //------------------
1226 // Session API
1227 //------------------
1228 
1229 jack_session_command_t* JackClient::SessionNotify(const char* target, jack_session_event_type_t type, const char* path)
1230 {
1232  fChannel->SessionNotify(GetClientControl()->fRefNum, target, type, path, &res);
1233  return res;
1234 }
1235 
1236 int JackClient::SessionReply(jack_session_event_t* ev)
1237 {
1238  if (ev->command_line) {
1239  strncpy(GetClientControl()->fSessionCommand, ev->command_line, sizeof(GetClientControl()->fSessionCommand));
1240  } else {
1241  GetClientControl()->fSessionCommand[0] = '\0';
1242  }
1243 
1244  GetClientControl()->fSessionFlags = ev->flags;
1245 
1246  jack_log("JackClient::SessionReply... we are here");
1247  if (fChannel->IsChannelThread()) {
1248  jack_log("JackClient::SessionReply... in callback reply");
1249  // OK, immediate reply...
1250  fSessionReply = kImmediateSessionReply;
1251  return 0;
1252  }
1253 
1254  jack_log("JackClient::SessionReply... out of cb");
1255 
1256  int result = -1;
1257  fChannel->SessionReply(GetClientControl()->fRefNum, &result);
1258  return result;
1259 }
1260 
1261 char* JackClient::GetUUIDForClientName(const char* client_name)
1262 {
1263  char uuid_res[JACK_UUID_SIZE];
1264  int result = -1;
1265  fChannel->GetUUIDForClientName(GetClientControl()->fRefNum, client_name, uuid_res, &result);
1266  return (result) ? NULL : strdup(uuid_res);
1267 }
1268 
1269 char* JackClient::GetClientNameByUUID(const char* uuid)
1270 {
1271  char name_res[JACK_CLIENT_NAME_SIZE + 1];
1272  int result = -1;
1273  fChannel->GetClientNameForUUID(GetClientControl()->fRefNum, uuid, name_res, &result);
1274  return (result) ? NULL : strdup(name_res);
1275 }
1276 
1277 int JackClient::ReserveClientName(const char* client_name, const char* uuid)
1278 {
1279  int result = -1;
1280  fChannel->ReserveClientName( GetClientControl()->fRefNum, client_name, uuid, &result);
1281  return result;
1282 }
1283 
1284 int JackClient::ClientHasSessionCallback(const char* client_name)
1285 {
1286  int result = -1;
1287  fChannel->ClientHasSessionCallback(client_name, &result);
1288  return result;
1289 }
1290 
1291 } // end of namespace
1292 
jack_session_flags_t flags
Definition: JackSession.h:50
virtual int Deactivate()
Need to stop thread after deactivating in the server.
Definition: JackClient.cpp:461
virtual int ClientNotifyImp(int refnum, const char *name, int notify, int sync, const char *message, int value1, int value)
Notification received from the server.
Definition: JackClient.cpp:158
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:91
jack_position_bits_t valid
Definition: types.h:561
jack_nframes_t min
Definition: types.h:268
jack_nframes_t max
Definition: types.h:272
virtual int Activate()
We need to start thread before activating in the server, otherwise the FW driver connected to the cli...
Definition: JackClient.cpp:428
void SetupDriverSync(bool freewheel)
Definition: JackClient.cpp:140
bool Execute()
RT thread.
Definition: JackClient.cpp:559
bool Init()
Called once when the thread starts.
Definition: JackClient.cpp:501
jack_nframes_t frame
Definition: types.h:559
enum JackSessionFlags jack_session_flags_t
Definition: session.h:98
jack_session_event_type_t type
Definition: JackSession.h:46
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:107
void(* JackSessionCallback)(jack_session_event_t *event, void *arg)
Definition: session.h:162