jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1139 lines
34KB

  1. /*
  2. Copyright (C) 2001 Paul Davis
  3. Copyright (C) 2004-2008 Grame
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  15. */
  16. #include "JackClient.h"
  17. #include "JackGraphManager.h"
  18. #include "JackClientControl.h"
  19. #include "JackEngineControl.h"
  20. #include "JackGlobals.h"
  21. #include "JackChannel.h"
  22. #include "JackTransportEngine.h"
  23. #include "driver_interface.h"
  24. #include "JackLibGlobals.h"
  25. #include <math.h>
  26. #include <string>
  27. #include <algorithm>
  28. using namespace std;
  29. namespace Jack
  30. {
  31. #define IsRealTime() ((fProcess != NULL) | (fThreadFun != NULL) | (fSync != NULL) | (fTimebase != NULL))
  32. JackClient::JackClient():fThread(this)
  33. {}
  34. JackClient::JackClient(JackSynchro* table):fThread(this)
  35. {
  36. fSynchroTable = table;
  37. fProcess = NULL;
  38. fGraphOrder = NULL;
  39. fXrun = NULL;
  40. fShutdown = NULL;
  41. fInfoShutdown = NULL;
  42. fInit = NULL;
  43. fBufferSize = NULL;
  44. fClientRegistration = NULL;
  45. fFreewheel = NULL;
  46. fPortRegistration = NULL;
  47. fPortConnect = NULL;
  48. fPortRename = NULL;
  49. fTimebase = NULL;
  50. fSync = NULL;
  51. fThreadFun = NULL;
  52. fProcessArg = NULL;
  53. fGraphOrderArg = NULL;
  54. fXrunArg = NULL;
  55. fShutdownArg = NULL;
  56. fInfoShutdownArg = NULL;
  57. fInitArg = NULL;
  58. fBufferSizeArg = NULL;
  59. fFreewheelArg = NULL;
  60. fClientRegistrationArg = NULL;
  61. fPortRegistrationArg = NULL;
  62. fPortConnectArg = NULL;
  63. fPortRenameArg = NULL;
  64. fSyncArg = NULL;
  65. fTimebaseArg = NULL;
  66. fThreadFunArg = NULL;
  67. }
  68. JackClient::~JackClient()
  69. {}
  70. int JackClient::Close()
  71. {
  72. jack_log("JackClient::Close ref = %ld", GetClientControl()->fRefNum);
  73. int result = 0;
  74. Deactivate();
  75. fChannel->Stop(); // Channels is stopped first to avoid receiving notifications while closing
  76. // Request close only if server is still running
  77. if (JackGlobals::fServerRunning) {
  78. fChannel->ClientClose(GetClientControl()->fRefNum, &result);
  79. } else {
  80. jack_log("JackClient::Close server is shutdown");
  81. }
  82. fChannel->Close();
  83. fSynchroTable[GetClientControl()->fRefNum].Disconnect();
  84. JackGlobals::fClientTable[GetClientControl()->fRefNum] = NULL;
  85. return result;
  86. }
  87. bool JackClient::IsActive()
  88. {
  89. return (GetClientControl()) ? GetClientControl()->fActive : false;
  90. }
  91. pthread_t JackClient::GetThreadID()
  92. {
  93. return fThread.GetThreadID();
  94. }
  95. /*!
  96. In "async" mode, the server does not synchronize itself on the output drivers, thus it would never "consume" the activations.
  97. The synchronization primitives for drivers are setup in "flush" mode that to not keep unneeded activations.
  98. Drivers synchro are setup in "flush" mode if server is "async" and NOT freewheel.
  99. */
  100. void JackClient::SetupDriverSync(bool freewheel)
  101. {
  102. if (!freewheel && !GetEngineControl()->fSyncMode) {
  103. jack_log("JackClient::SetupDriverSync driver sem in flush mode");
  104. for (int i = 0; i < GetEngineControl()->fDriverNum; i++) {
  105. fSynchroTable[i].SetFlush(true);
  106. }
  107. } else {
  108. jack_log("JackClient::SetupDriverSync driver sem in normal mode");
  109. for (int i = 0; i < GetEngineControl()->fDriverNum; i++)
  110. fSynchroTable[i].SetFlush(false);
  111. }
  112. }
  113. /*!
  114. \brief Notification received from the server.
  115. */
  116. int JackClient::ClientNotifyImp(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
  117. {
  118. return 0;
  119. }
  120. int JackClient::ClientNotify(int refnum, const char* name, int notify, int sync, const char* message, int value1, int value2)
  121. {
  122. int res = 0;
  123. // Done all time: redirected on subclass implementation JackLibClient and JackInternalClient
  124. switch (notify) {
  125. case kAddClient:
  126. res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
  127. break;
  128. case kRemoveClient:
  129. res = ClientNotifyImp(refnum, name, notify, sync, message, value1, value2);
  130. break;
  131. case kActivateClient:
  132. jack_log("JackClient::kActivateClient name = %s ref = %ld ", name, refnum);
  133. Init();
  134. break;
  135. }
  136. /*
  137. The current semantic is that notifications can only be received when the client has been activated,
  138. although is this implementation, one could imagine calling notifications as soon as the client has be opened.
  139. */
  140. if (IsActive()) {
  141. switch (notify) {
  142. case kAddClient:
  143. jack_log("JackClient::kAddClient fName = %s name = %s", GetClientControl()->fName, name);
  144. if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
  145. fClientRegistration(name, 1, fClientRegistrationArg);
  146. }
  147. break;
  148. case kRemoveClient:
  149. jack_log("JackClient::kRemoveClient fName = %s name = %s", GetClientControl()->fName, name);
  150. if (fClientRegistration && strcmp(GetClientControl()->fName, name) != 0) { // Don't call the callback for the registering client itself
  151. fClientRegistration(name, 0, fClientRegistrationArg);
  152. }
  153. break;
  154. case kBufferSizeCallback:
  155. jack_log("JackClient::kBufferSizeCallback buffer_size = %ld", value1);
  156. if (fBufferSize) {
  157. res = fBufferSize(value1, fBufferSizeArg);
  158. }
  159. break;
  160. case kSampleRateCallback:
  161. jack_log("JackClient::kSampleRateCallback sample_rate = %ld", value1);
  162. if (fSampleRate) {
  163. res = fSampleRate(value1, fSampleRateArg);
  164. }
  165. break;
  166. case kGraphOrderCallback:
  167. jack_log("JackClient::kGraphOrderCallback");
  168. if (fGraphOrder) {
  169. res = fGraphOrder(fGraphOrderArg);
  170. }
  171. break;
  172. case kStartFreewheelCallback:
  173. jack_log("JackClient::kStartFreewheel");
  174. SetupDriverSync(true);
  175. fThread.DropRealTime(); // Always done (JACK server in RT mode or not...)
  176. if (fFreewheel) {
  177. fFreewheel(1, fFreewheelArg);
  178. }
  179. break;
  180. case kStopFreewheelCallback:
  181. jack_log("JackClient::kStopFreewheel");
  182. SetupDriverSync(false);
  183. if (fFreewheel) {
  184. fFreewheel(0, fFreewheelArg);
  185. }
  186. if (GetEngineControl()->fRealTime) {
  187. fThread.AcquireRealTime();
  188. }
  189. break;
  190. case kPortRegistrationOnCallback:
  191. jack_log("JackClient::kPortRegistrationOn port_index = %ld", value1);
  192. if (fPortRegistration) {
  193. fPortRegistration(value1, 1, fPortRegistrationArg);
  194. }
  195. break;
  196. case kPortRegistrationOffCallback:
  197. jack_log("JackClient::kPortRegistrationOff port_index = %ld ", value1);
  198. if (fPortRegistration) {
  199. fPortRegistration(value1, 0, fPortRegistrationArg);
  200. }
  201. break;
  202. case kPortConnectCallback:
  203. jack_log("JackClient::kPortConnectCallback src = %ld dst = %ld", value1, value2);
  204. if (fPortConnect) {
  205. fPortConnect(value1, value2, 1, fPortConnectArg);
  206. }
  207. break;
  208. case kPortDisconnectCallback:
  209. jack_log("JackClient::kPortDisconnectCallback src = %ld dst = %ld", value1, value2);
  210. if (fPortConnect) {
  211. fPortConnect(value1, value2, 0, fPortConnectArg);
  212. }
  213. break;
  214. case kPortRenameCallback:
  215. jack_log("JackClient::kPortRenameCallback port = %ld", value1);
  216. if (fPortRename) {
  217. fPortRename(value1, message, GetGraphManager()->GetPort(value1)->GetName(), fPortRenameArg);
  218. }
  219. break;
  220. case kXRunCallback:
  221. jack_log("JackClient::kXRunCallback");
  222. if (fXrun) {
  223. res = fXrun(fXrunArg);
  224. }
  225. break;
  226. case kShutDownCallback:
  227. jack_log("JackClient::kShutDownCallback");
  228. if (fInfoShutdown) {
  229. fInfoShutdown((jack_status_t)value1, message, fInfoShutdownArg);
  230. fInfoShutdown = NULL;
  231. }
  232. break;
  233. case kSessionCallback:
  234. jack_log("JackClient::kSessionCallback");
  235. if (fSession) {
  236. jack_session_event_t *event = (jack_session_event_t *) malloc( sizeof(jack_session_event_t) );
  237. char uuid_buf[JACK_UUID_SIZE];
  238. event->type = (jack_session_event_type_t) value1;
  239. event->session_dir = strdup( message );
  240. event->command_line = NULL;
  241. event->flags = (jack_session_flags_t) 0;
  242. snprintf( uuid_buf, sizeof(uuid_buf), "%d", GetClientControl()->fSessionID );
  243. event->client_uuid = strdup( uuid_buf );
  244. fImmediateSessionReply = false;
  245. fSession(event, fSessionArg);
  246. res = (fImmediateSessionReply) ? 1 : 2;
  247. }
  248. break;
  249. }
  250. }
  251. return res;
  252. }
  253. /*!
  254. \brief We need to start thread before activating in the server, otherwise the FW driver
  255. connected to the client may not be activated.
  256. */
  257. int JackClient::Activate()
  258. {
  259. jack_log("JackClient::Activate");
  260. if (IsActive())
  261. return 0;
  262. // RT thread is started only when needed...
  263. if (IsRealTime()) {
  264. if (StartThread() < 0)
  265. return -1;
  266. }
  267. /*
  268. Insertion of client in the graph will cause a kGraphOrderCallback notification
  269. to be delivered by the server, the client wants to receive it.
  270. */
  271. GetClientControl()->fActive = true;
  272. // Transport related callback become "active"
  273. GetClientControl()->fTransportSync = true;
  274. GetClientControl()->fTransportTimebase = true;
  275. int result = -1;
  276. GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
  277. fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
  278. return result;
  279. }
  280. /*!
  281. \brief Need to stop thread after deactivating in the server.
  282. */
  283. int JackClient::Deactivate()
  284. {
  285. jack_log("JackClient::Deactivate");
  286. if (!IsActive())
  287. return 0;
  288. GetClientControl()->fActive = false;
  289. // Transport related callback become "unactive"
  290. GetClientControl()->fTransportSync = false;
  291. GetClientControl()->fTransportTimebase = false;
  292. // We need to wait for the new engine cycle before stopping the RT thread, but this is done by ClientDeactivate
  293. int result = -1;
  294. fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
  295. jack_log("JackClient::Deactivate res = %ld", result);
  296. // RT thread is stopped only when needed...
  297. if (IsRealTime())
  298. fThread.Kill();
  299. return result;
  300. }
  301. //----------------------
  302. // RT thread management
  303. //----------------------
  304. /*!
  305. \brief Called once when the thread starts.
  306. */
  307. bool JackClient::Init()
  308. {
  309. if (fInit) {
  310. jack_log("JackClient::Init calling client thread init callback");
  311. fInit(fInitArg);
  312. }
  313. return true;
  314. }
  315. int JackClient::StartThread()
  316. {
  317. jack_log("JackClient::StartThread : period = %ld computation = %ld constraint = %ld",
  318. long(int64_t(GetEngineControl()->fPeriod) / 1000.0f),
  319. long(int64_t(GetEngineControl()->fComputation) / 1000.0f),
  320. long(int64_t(GetEngineControl()->fConstraint) / 1000.0f));
  321. // Will do "something" on OSX only...
  322. fThread.SetParams(GetEngineControl()->fPeriod, GetEngineControl()->fComputation, GetEngineControl()->fConstraint);
  323. if (fThread.StartSync() < 0) {
  324. jack_error("Start thread error");
  325. return -1;
  326. }
  327. if (GetEngineControl()->fRealTime) {
  328. if (fThread.AcquireRealTime(GetEngineControl()->fClientPriority) < 0) {
  329. jack_error("AcquireRealTime error");
  330. }
  331. }
  332. return 0;
  333. }
  334. /*!
  335. \brief RT thread.
  336. */
  337. bool JackClient::Execute()
  338. {
  339. if (!jack_tls_set(JackGlobals::fRealTime, this))
  340. jack_error("failed to set thread realtime key");
  341. if (GetEngineControl()->fRealTime)
  342. set_threaded_log_function();
  343. // Execute a dummy cycle to be sure thread has the correct properties
  344. DummyCycle();
  345. if (fThreadFun) {
  346. fThreadFun(fThreadFunArg);
  347. } else {
  348. ExecuteThread();
  349. }
  350. return false;
  351. }
  352. void JackClient::DummyCycle()
  353. {
  354. WaitSync();
  355. SignalSync();
  356. }
  357. inline void JackClient::ExecuteThread()
  358. {
  359. while (true) {
  360. CycleWaitAux();
  361. CycleSignalAux(CallProcessCallback());
  362. }
  363. }
  364. inline jack_nframes_t JackClient::CycleWaitAux()
  365. {
  366. if (!WaitSync())
  367. Error(); // Terminates the thread
  368. CallSyncCallbackAux();
  369. return GetEngineControl()->fBufferSize;
  370. }
  371. inline void JackClient::CycleSignalAux(int status)
  372. {
  373. if (status == 0)
  374. CallTimebaseCallbackAux();
  375. SignalSync();
  376. if (status != 0)
  377. End(); // Terminates the thread
  378. }
  379. jack_nframes_t JackClient::CycleWait()
  380. {
  381. return CycleWaitAux();
  382. }
  383. void JackClient::CycleSignal(int status)
  384. {
  385. CycleSignalAux(status);
  386. }
  387. inline int JackClient::CallProcessCallback()
  388. {
  389. return (fProcess != NULL) ? fProcess(GetEngineControl()->fBufferSize, fProcessArg) : 0;
  390. }
  391. inline bool JackClient::WaitSync()
  392. {
  393. // Suspend itself: wait on the input synchro
  394. if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
  395. jack_error("SuspendRefNum error");
  396. return false;
  397. } else {
  398. return true;
  399. }
  400. }
  401. inline void JackClient::SignalSync()
  402. {
  403. // Resume: signal output clients connected to the running client
  404. if (GetGraphManager()->ResumeRefNum(GetClientControl(), fSynchroTable) < 0) {
  405. jack_error("ResumeRefNum error");
  406. }
  407. }
  408. inline void JackClient::End()
  409. {
  410. jack_log("JackClient::Execute end name = %s", GetClientControl()->fName);
  411. // Hum... not sure about this, the following "close" code is called in the RT thread...
  412. int result;
  413. fThread.DropSelfRealTime();
  414. GetClientControl()->fActive = false;
  415. fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
  416. fThread.Terminate();
  417. }
  418. inline void JackClient::Error()
  419. {
  420. jack_error("JackClient::Execute error name = %s", GetClientControl()->fName);
  421. // Hum... not sure about this, the following "close" code is called in the RT thread...
  422. int result;
  423. fThread.DropSelfRealTime();
  424. GetClientControl()->fActive = false;
  425. fChannel->ClientDeactivate(GetClientControl()->fRefNum, &result);
  426. ShutDown();
  427. fThread.Terminate();
  428. }
  429. //-----------------
  430. // Port management
  431. //-----------------
  432. int JackClient::PortRegister(const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size)
  433. {
  434. // Check if port name is empty
  435. string port_name_str = string(port_name);
  436. if (port_name_str.size() == 0) {
  437. jack_error("port_name is empty");
  438. return 0; // Means failure here...
  439. }
  440. // Check port name length
  441. string name = string(GetClientControl()->fName) + string(":") + port_name_str;
  442. if (name.size() >= JACK_PORT_NAME_SIZE) {
  443. jack_error("\"%s:%s\" is too long to be used as a JACK port name.\n"
  444. "Please use %lu characters or less",
  445. GetClientControl()->fName,
  446. port_name,
  447. JACK_PORT_NAME_SIZE - 1);
  448. return 0; // Means failure here...
  449. }
  450. int result = -1;
  451. jack_port_id_t port_index = NO_PORT;
  452. fChannel->PortRegister(GetClientControl()->fRefNum, name.c_str(), port_type, flags, buffer_size, &port_index, &result);
  453. if (result == 0) {
  454. jack_log("JackClient::PortRegister ref = %ld name = %s type = %s port_index = %ld", GetClientControl()->fRefNum, name.c_str(), port_type, port_index);
  455. fPortList.push_back(port_index);
  456. return port_index;
  457. } else {
  458. return 0;
  459. }
  460. }
  461. int JackClient::PortUnRegister(jack_port_id_t port_index)
  462. {
  463. jack_log("JackClient::PortUnRegister port_index = %ld", port_index);
  464. list<jack_port_id_t>::iterator it = find(fPortList.begin(), fPortList.end(), port_index);
  465. if (it != fPortList.end()) {
  466. fPortList.erase(it);
  467. int result = -1;
  468. fChannel->PortUnRegister(GetClientControl()->fRefNum, port_index, &result);
  469. return result;
  470. } else {
  471. jack_error("unregistering a port %ld that is not own by the client", port_index);
  472. return -1;
  473. }
  474. }
  475. int JackClient::PortConnect(const char* src, const char* dst)
  476. {
  477. jack_log("JackClient::Connect src = %s dst = %s", src, dst);
  478. int result = -1;
  479. fChannel->PortConnect(GetClientControl()->fRefNum, src, dst, &result);
  480. return result;
  481. }
  482. int JackClient::PortDisconnect(const char* src, const char* dst)
  483. {
  484. jack_log("JackClient::Disconnect src = %s dst = %s", src, dst);
  485. int result = -1;
  486. fChannel->PortDisconnect(GetClientControl()->fRefNum, src, dst, &result);
  487. return result;
  488. }
  489. int JackClient::PortDisconnect(jack_port_id_t src)
  490. {
  491. jack_log("JackClient::PortDisconnect src = %ld", src);
  492. int result = -1;
  493. fChannel->PortDisconnect(GetClientControl()->fRefNum, src, ALL_PORTS, &result);
  494. return result;
  495. }
  496. int JackClient::PortIsMine(jack_port_id_t port_index)
  497. {
  498. JackPort* port = GetGraphManager()->GetPort(port_index);
  499. return GetClientControl()->fRefNum == port->GetRefNum();
  500. }
  501. int JackClient::PortRename(jack_port_id_t port_index, const char* name)
  502. {
  503. int result = -1;
  504. fChannel->PortRename(GetClientControl()->fRefNum, port_index, name, &result);
  505. return result;
  506. }
  507. //--------------------
  508. // Context management
  509. //--------------------
  510. int JackClient::SetBufferSize(jack_nframes_t buffer_size)
  511. {
  512. int result = -1;
  513. fChannel->SetBufferSize(buffer_size, &result);
  514. return result;
  515. }
  516. int JackClient::SetFreeWheel(int onoff)
  517. {
  518. int result = -1;
  519. fChannel->SetFreewheel(onoff, &result);
  520. return result;
  521. }
  522. /*
  523. ShutDown is called:
  524. - from the RT thread when Execute method fails
  525. - possibly from a "closed" notification channel
  526. (Not needed since the synch object used (Sema of Fifo will fails when server quits... see ShutDown))
  527. */
  528. void JackClient::ShutDown()
  529. {
  530. jack_log("ShutDown");
  531. JackGlobals::fServerRunning = false;
  532. if (fInfoShutdown) {
  533. fInfoShutdown(JackFailure, "JACK server has been closed", fInfoShutdownArg);
  534. fInfoShutdown = NULL;
  535. } else if (fShutdown) {
  536. fShutdown(fShutdownArg);
  537. fShutdown = NULL;
  538. }
  539. }
  540. //----------------------
  541. // Transport management
  542. //----------------------
  543. inline int JackClient::ActivateAux()
  544. {
  545. // If activated without RT thread...
  546. if (IsActive() && fThread.GetStatus() != JackThread::kRunning) {
  547. jack_log("ActivateAux");
  548. // RT thread is started
  549. if (StartThread() < 0)
  550. return -1;
  551. int result = -1;
  552. GetClientControl()->fCallback[kRealTimeCallback] = IsRealTime();
  553. fChannel->ClientActivate(GetClientControl()->fRefNum, IsRealTime(), &result);
  554. return result;
  555. } else {
  556. return 0;
  557. }
  558. }
  559. int JackClient::ReleaseTimebase()
  560. {
  561. int result = -1;
  562. fChannel->ReleaseTimebase(GetClientControl()->fRefNum, &result);
  563. if (result == 0) {
  564. GetClientControl()->fTransportTimebase = false;
  565. fTimebase = NULL;
  566. fTimebaseArg = NULL;
  567. }
  568. return result;
  569. }
  570. /* Call the server if the client is active, otherwise keeps the arguments */
  571. int JackClient::SetSyncCallback(JackSyncCallback sync_callback, void* arg)
  572. {
  573. GetClientControl()->fTransportSync = (fSync != NULL);
  574. fSyncArg = arg;
  575. fSync = sync_callback;
  576. return ActivateAux();
  577. }
  578. int JackClient::SetTimebaseCallback(int conditional, JackTimebaseCallback timebase_callback, void* arg)
  579. {
  580. int result = -1;
  581. fChannel->SetTimebaseCallback(GetClientControl()->fRefNum, conditional, &result);
  582. if (result == 0) {
  583. GetClientControl()->fTransportTimebase = true;
  584. fTimebase = timebase_callback;
  585. fTimebaseArg = arg;
  586. return ActivateAux();
  587. } else {
  588. fTimebase = NULL;
  589. fTimebaseArg = NULL;
  590. return -1;
  591. }
  592. }
  593. int JackClient::SetSyncTimeout(jack_time_t timeout)
  594. {
  595. GetEngineControl()->fTransport.SetSyncTimeout(timeout);
  596. return 0;
  597. }
  598. // Must be RT safe
  599. void JackClient::TransportLocate(jack_nframes_t frame)
  600. {
  601. jack_position_t pos;
  602. pos.frame = frame;
  603. pos.valid = (jack_position_bits_t)0;
  604. jack_log("TransportLocate pos = %ld", pos.frame);
  605. GetEngineControl()->fTransport.RequestNewPos(&pos);
  606. }
  607. int JackClient::TransportReposition(jack_position_t* pos)
  608. {
  609. jack_position_t tmp = *pos;
  610. jack_log("TransportReposition pos = %ld", pos->frame);
  611. if (tmp.valid & ~JACK_POSITION_MASK) {
  612. return EINVAL;
  613. } else {
  614. GetEngineControl()->fTransport.RequestNewPos(pos);
  615. return 0;
  616. }
  617. }
  618. jack_transport_state_t JackClient::TransportQuery(jack_position_t* pos)
  619. {
  620. return GetEngineControl()->fTransport.Query(pos);
  621. }
  622. jack_nframes_t JackClient::GetCurrentTransportFrame()
  623. {
  624. return GetEngineControl()->fTransport.GetCurrentFrame();
  625. }
  626. // Must be RT safe: directly write in the transport shared mem
  627. void JackClient::TransportStart()
  628. {
  629. GetEngineControl()->fTransport.SetCommand(TransportCommandStart);
  630. }
  631. // Must be RT safe: directly write in the transport shared mem
  632. void JackClient::TransportStop()
  633. {
  634. GetEngineControl()->fTransport.SetCommand(TransportCommandStop);
  635. }
  636. // Never called concurently with the server
  637. // TODO check concurrency with SetSyncCallback
  638. void JackClient::CallSyncCallback()
  639. {
  640. CallSyncCallbackAux();
  641. }
  642. inline void JackClient::CallSyncCallbackAux()
  643. {
  644. if (GetClientControl()->fTransportSync) {
  645. JackTransportEngine& transport = GetEngineControl()->fTransport;
  646. jack_position_t* cur_pos = transport.ReadCurrentState();
  647. jack_transport_state_t transport_state = transport.GetState();
  648. if (fSync != NULL) {
  649. if (fSync(transport_state, cur_pos, fSyncArg)) {
  650. GetClientControl()->fTransportState = JackTransportRolling;
  651. GetClientControl()->fTransportSync = false;
  652. }
  653. } else {
  654. GetClientControl()->fTransportState = JackTransportRolling;
  655. GetClientControl()->fTransportSync = false;
  656. }
  657. }
  658. }
  659. void JackClient::CallTimebaseCallback()
  660. {
  661. CallTimebaseCallbackAux();
  662. }
  663. inline void JackClient::CallTimebaseCallbackAux()
  664. {
  665. JackTransportEngine& transport = GetEngineControl()->fTransport;
  666. int master;
  667. bool unused;
  668. transport.GetTimebaseMaster(master, unused);
  669. if (GetClientControl()->fRefNum == master && fTimebase) { // Client *is* timebase...
  670. jack_transport_state_t transport_state = transport.GetState();
  671. jack_position_t* cur_pos = transport.WriteNextStateStart(1);
  672. if (GetClientControl()->fTransportTimebase) {
  673. fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, true, fTimebaseArg);
  674. GetClientControl()->fTransportTimebase = false; // Callback is called only once with "new_pos" = true
  675. } else if (transport_state == JackTransportRolling) {
  676. fTimebase(transport_state, GetEngineControl()->fBufferSize, cur_pos, false, fTimebaseArg);
  677. }
  678. transport.WriteNextStateStop(1);
  679. }
  680. }
  681. //---------------------
  682. // Callback management
  683. //---------------------
  684. void JackClient::OnShutdown(JackShutdownCallback callback, void *arg)
  685. {
  686. if (IsActive()) {
  687. jack_error("You cannot set callbacks on an active client");
  688. } else {
  689. fShutdownArg = arg;
  690. fShutdown = callback;
  691. }
  692. }
  693. void JackClient::OnInfoShutdown(JackInfoShutdownCallback callback, void *arg)
  694. {
  695. if (IsActive()) {
  696. jack_error("You cannot set callbacks on an active client");
  697. } else {
  698. GetClientControl()->fCallback[kShutDownCallback] = (callback != NULL);
  699. fInfoShutdownArg = arg;
  700. fInfoShutdown = callback;
  701. }
  702. }
  703. int JackClient::SetProcessCallback(JackProcessCallback callback, void *arg)
  704. {
  705. if (IsActive()) {
  706. jack_error("You cannot set callbacks on an active client");
  707. return -1;
  708. } else if (fThreadFun) {
  709. jack_error ("A thread callback has already been setup, both models cannot be used at the same time!");
  710. return -1;
  711. } else {
  712. fProcessArg = arg;
  713. fProcess = callback;
  714. return 0;
  715. }
  716. }
  717. int JackClient::SetXRunCallback(JackXRunCallback callback, void *arg)
  718. {
  719. if (IsActive()) {
  720. jack_error("You cannot set callbacks on an active client");
  721. return -1;
  722. } else {
  723. GetClientControl()->fCallback[kXRunCallback] = (callback != NULL);
  724. fXrunArg = arg;
  725. fXrun = callback;
  726. return 0;
  727. }
  728. }
  729. int JackClient::SetInitCallback(JackThreadInitCallback callback, void *arg)
  730. {
  731. if (IsActive()) {
  732. jack_error("You cannot set callbacks on an active client");
  733. return -1;
  734. } else {
  735. fInitArg = arg;
  736. fInit = callback;
  737. /* make sure that the message buffer thread is initialized too */
  738. JackMessageBuffer::fInstance->SetInitCallback(callback, arg);
  739. return 0;
  740. }
  741. }
  742. int JackClient::SetGraphOrderCallback(JackGraphOrderCallback callback, void *arg)
  743. {
  744. jack_log("SetGraphOrderCallback ");
  745. if (IsActive()) {
  746. jack_error("You cannot set callbacks on an active client");
  747. return -1;
  748. } else {
  749. GetClientControl()->fCallback[kGraphOrderCallback] = (callback != NULL);
  750. fGraphOrder = callback;
  751. fGraphOrderArg = arg;
  752. return 0;
  753. }
  754. }
  755. int JackClient::SetBufferSizeCallback(JackBufferSizeCallback callback, void *arg)
  756. {
  757. if (IsActive()) {
  758. jack_error("You cannot set callbacks on an active client");
  759. return -1;
  760. } else {
  761. GetClientControl()->fCallback[kBufferSizeCallback] = (callback != NULL);
  762. fBufferSizeArg = arg;
  763. fBufferSize = callback;
  764. return 0;
  765. }
  766. }
  767. int JackClient::SetSampleRateCallback(JackSampleRateCallback callback, void *arg)
  768. {
  769. if (IsActive()) {
  770. jack_error("You cannot set callbacks on an active client");
  771. return -1;
  772. } else {
  773. GetClientControl()->fCallback[kSampleRateCallback] = (callback != NULL);
  774. fSampleRateArg = arg;
  775. fSampleRate = callback;
  776. // Now invoke it
  777. if (callback)
  778. callback(GetEngineControl()->fSampleRate, arg);
  779. return 0;
  780. }
  781. }
  782. int JackClient::SetClientRegistrationCallback(JackClientRegistrationCallback callback, void* arg)
  783. {
  784. if (IsActive()) {
  785. jack_error("You cannot set callbacks on an active client");
  786. return -1;
  787. } else {
  788. // kAddClient and kRemoveClient notifications must be delivered by the server in any case
  789. fClientRegistrationArg = arg;
  790. fClientRegistration = callback;
  791. return 0;
  792. }
  793. }
  794. int JackClient::SetFreewheelCallback(JackFreewheelCallback callback, void *arg)
  795. {
  796. if (IsActive()) {
  797. jack_error("You cannot set callbacks on an active client");
  798. return -1;
  799. } else {
  800. GetClientControl()->fCallback[kStartFreewheelCallback] = (callback != NULL);
  801. GetClientControl()->fCallback[kStopFreewheelCallback] = (callback != NULL);
  802. fFreewheelArg = arg;
  803. fFreewheel = callback;
  804. return 0;
  805. }
  806. }
  807. int JackClient::SetPortRegistrationCallback(JackPortRegistrationCallback callback, void *arg)
  808. {
  809. if (IsActive()) {
  810. jack_error("You cannot set callbacks on an active client");
  811. return -1;
  812. } else {
  813. GetClientControl()->fCallback[kPortRegistrationOnCallback] = (callback != NULL);
  814. GetClientControl()->fCallback[kPortRegistrationOffCallback] = (callback != NULL);
  815. fPortRegistrationArg = arg;
  816. fPortRegistration = callback;
  817. return 0;
  818. }
  819. }
  820. int JackClient::SetPortConnectCallback(JackPortConnectCallback callback, void *arg)
  821. {
  822. if (IsActive()) {
  823. jack_error("You cannot set callbacks on an active client");
  824. return -1;
  825. } else {
  826. GetClientControl()->fCallback[kPortConnectCallback] = (callback != NULL);
  827. GetClientControl()->fCallback[kPortDisconnectCallback] = (callback != NULL);
  828. fPortConnectArg = arg;
  829. fPortConnect = callback;
  830. return 0;
  831. }
  832. }
  833. int JackClient::SetPortRenameCallback(JackPortRenameCallback callback, void *arg)
  834. {
  835. if (IsActive()) {
  836. jack_error("You cannot set callbacks on an active client");
  837. return -1;
  838. } else {
  839. GetClientControl()->fCallback[kPortRenameCallback] = (callback != NULL);
  840. fPortRenameArg = arg;
  841. fPortRename = callback;
  842. return 0;
  843. }
  844. }
  845. int JackClient::SetProcessThread(JackThreadCallback fun, void *arg)
  846. {
  847. if (IsActive()) {
  848. jack_error("You cannot set callbacks on an active client");
  849. return -1;
  850. } else if (fProcess) {
  851. jack_error ("A process callback has already been setup, both models cannot be used at the same time!");
  852. return -1;
  853. } else {
  854. fThreadFun = fun;
  855. fThreadFunArg = arg;
  856. return 0;
  857. }
  858. }
  859. int JackClient::SetSessionCallback(JackSessionCallback callback, void *arg)
  860. {
  861. if (IsActive()) {
  862. jack_error("You cannot set callbacks on an active client");
  863. return -1;
  864. } else {
  865. GetClientControl()->fCallback[kSessionCallback] = (callback != NULL);
  866. fSessionArg = arg;
  867. fSession = callback;
  868. return 0;
  869. }
  870. }
  871. //------------------
  872. // Internal clients
  873. //------------------
  874. char* JackClient::GetInternalClientName(int ref)
  875. {
  876. char name_res[JACK_CLIENT_NAME_SIZE + 1];
  877. int result = -1;
  878. fChannel->GetInternalClientName(GetClientControl()->fRefNum, ref, name_res, &result);
  879. return (result < 0) ? NULL : strdup(name_res);
  880. }
  881. int JackClient::InternalClientHandle(const char* client_name, jack_status_t* status)
  882. {
  883. int int_ref, result = -1;
  884. fChannel->InternalClientHandle(GetClientControl()->fRefNum, client_name, (int*)status, &int_ref, &result);
  885. return int_ref;
  886. }
  887. int JackClient::InternalClientLoad(const char* client_name, jack_options_t options, jack_status_t* status, jack_varargs_t* va)
  888. {
  889. if (strlen(client_name) >= JACK_CLIENT_NAME_SIZE) {
  890. jack_error ("\"%s\" is too long for a JACK client name.\n"
  891. "Please use %lu characters or less.",
  892. client_name, JACK_CLIENT_NAME_SIZE);
  893. return 0;
  894. }
  895. if (va->load_name && (strlen(va->load_name) >= JACK_PATH_MAX)) {
  896. jack_error("\"%s\" is too long for a shared object name.\n"
  897. "Please use %lu characters or less.",
  898. va->load_name, JACK_PATH_MAX);
  899. int my_status1 = *status | (JackFailure | JackInvalidOption);
  900. *status = (jack_status_t)my_status1;
  901. return 0;
  902. }
  903. if (va->load_init && (strlen(va->load_init) >= JACK_LOAD_INIT_LIMIT)) {
  904. jack_error ("\"%s\" is too long for internal client init "
  905. "string.\nPlease use %lu characters or less.",
  906. va->load_init, JACK_LOAD_INIT_LIMIT);
  907. int my_status1 = *status | (JackFailure | JackInvalidOption);
  908. *status = (jack_status_t)my_status1;
  909. return 0;
  910. }
  911. int int_ref, result = -1;
  912. fChannel->InternalClientLoad(GetClientControl()->fRefNum, client_name, va->load_name, va->load_init, options, (int*)status, &int_ref, -1, &result);
  913. return int_ref;
  914. }
  915. void JackClient::InternalClientUnload(int ref, jack_status_t* status)
  916. {
  917. int result = -1;
  918. fChannel->InternalClientUnload(GetClientControl()->fRefNum, ref, (int*)status, &result);
  919. }
  920. //------------------
  921. // Session API
  922. //------------------
  923. jack_session_command_t *JackClient::SessionNotify( const char* target, jack_session_event_type_t type, const char* path )
  924. {
  925. jack_session_command_t *res;
  926. fChannel->SessionNotify( GetClientControl()->fRefNum, target, type, path, &res );
  927. return res;
  928. }
  929. int JackClient::SessionReply( jack_session_event_t *ev )
  930. {
  931. if (ev->command_line) {
  932. strncpy( GetClientControl()->fSessionCommand, ev->command_line, sizeof(GetClientControl()->fSessionCommand) );
  933. } else {
  934. GetClientControl()->fSessionCommand[0] = '\0';
  935. }
  936. GetClientControl()->fSessionFlags = ev->flags;
  937. jack_log( "JackClient::SessionReply... we are here" );
  938. if (fChannel->IsChannelThread()) {
  939. jack_log( "JackClient::SessionReply... in callback reply" );
  940. fImmediateSessionReply = true;
  941. return 0;
  942. }
  943. jack_log( "JackClient::SessionReply... out of cb" );
  944. int res;
  945. fChannel->SessionReply( GetClientControl()->fRefNum, &res);
  946. return res;
  947. }
  948. char* JackClient::GetUUIDForClientName(const char* client_name)
  949. {
  950. char uuid_res[JACK_UUID_SIZE];
  951. int result = -1;
  952. fChannel->GetUUIDForClientName( GetClientControl()->fRefNum, client_name, uuid_res, &result);
  953. if (result)
  954. return NULL;
  955. return strdup(uuid_res);
  956. }
  957. char* JackClient::GetClientNameForUUID(const char* uuid)
  958. {
  959. char name_res[JACK_CLIENT_NAME_SIZE + 1];
  960. int result = -1;
  961. fChannel->GetClientNameForUUID(GetClientControl()->fRefNum, uuid, name_res, &result);
  962. if (result)
  963. return NULL;
  964. return strdup(name_res);
  965. }
  966. int JackClient::ReserveClientName(const char *name, const char* uuid)
  967. {
  968. int result = -1;
  969. fChannel->ReserveClientName( GetClientControl()->fRefNum, name, uuid, &result);
  970. return result;
  971. }
  972. } // end of namespace