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