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