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