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