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.

322 lines
11KB

  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 "JackTransportEngine.h"
  17. #include "JackClientInterface.h"
  18. #include "JackClientControl.h"
  19. #include "JackEngineControl.h"
  20. #include "JackGlobals.h"
  21. #include "JackError.h"
  22. #include "JackTime.h"
  23. #include <assert.h>
  24. #include <math.h>
  25. #include <stdlib.h>
  26. using namespace std;
  27. namespace Jack
  28. {
  29. JackTransportEngine::JackTransportEngine(): JackAtomicArrayState<jack_position_t>()
  30. {
  31. static_assert(offsetof(JackTransportEngine, fWriteCounter) % sizeof(fWriteCounter) == 0,
  32. "fWriteCounter must be first member of JackTransportEngine to ensure its alignment");
  33. fTransportState = JackTransportStopped;
  34. fTransportCmd = fPreviousCmd = TransportCommandStop;
  35. fSyncTimeout = 10000000; /* 10 seconds default...
  36. in case of big netjack1 roundtrip */
  37. fSyncTimeLeft = 0;
  38. fTimeBaseMaster = -1;
  39. fWriteCounter = 0;
  40. fConditionnal = false;
  41. fPendingPos = false;
  42. fNetworkSync = false;
  43. }
  44. // compute the number of cycle for timeout
  45. void JackTransportEngine::SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size)
  46. {
  47. long buf_usecs = (long)((buffer_size * (jack_time_t)1000000) / frame_rate);
  48. fSyncTimeLeft = fSyncTimeout / buf_usecs;
  49. jack_log("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld", (long)fSyncTimeout, (long)fSyncTimeLeft);
  50. }
  51. // Server
  52. int JackTransportEngine::ResetTimebase(int refnum)
  53. {
  54. if (fTimeBaseMaster == refnum) {
  55. jack_position_t* request = WriteNextStateStart(2); // To check
  56. request->valid = (jack_position_bits_t)0;
  57. WriteNextStateStop(2);
  58. fTimeBaseMaster = -1;
  59. return 0;
  60. } else {
  61. return EINVAL;
  62. }
  63. }
  64. // Server
  65. int JackTransportEngine::SetTimebaseMaster(int refnum, bool conditionnal)
  66. {
  67. if (conditionnal && fTimeBaseMaster > 0) {
  68. if (refnum != fTimeBaseMaster) {
  69. jack_log("conditional timebase for ref = %ld failed: %ld is already the master", refnum, fTimeBaseMaster);
  70. return EBUSY;
  71. } else {
  72. jack_log("ref = %ld was already timebase master", refnum);
  73. return 0;
  74. }
  75. } else {
  76. fTimeBaseMaster = refnum;
  77. fConditionnal = conditionnal;
  78. jack_log("new timebase master: ref = %ld", refnum);
  79. return 0;
  80. }
  81. }
  82. // RT
  83. bool JackTransportEngine::CheckAllRolling(JackClientInterface** table)
  84. {
  85. for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
  86. JackClientInterface* client = table[i];
  87. if (client && client->GetClientControl()->fTransportState != JackTransportRolling) {
  88. jack_log("CheckAllRolling ref = %ld is not rolling", i);
  89. return false;
  90. }
  91. }
  92. jack_log("CheckAllRolling");
  93. return true;
  94. }
  95. // RT
  96. void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table)
  97. {
  98. for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
  99. JackClientInterface* client = table[i];
  100. if (client) {
  101. JackClientControl* control = client->GetClientControl();
  102. // Inactive clients don't have their process function called at all, so they must appear as already "rolling" for the transport....
  103. control->fTransportState = (control->fActive && control->fCallback[kRealTimeCallback]) ? JackTransportStarting : JackTransportRolling;
  104. control->fTransportSync = true;
  105. control->fTransportTimebase = true;
  106. jack_log("MakeAllStartingLocating ref = %ld", i);
  107. }
  108. }
  109. }
  110. // RT
  111. void JackTransportEngine::MakeAllStopping(JackClientInterface** table)
  112. {
  113. for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
  114. JackClientInterface* client = table[i];
  115. if (client) {
  116. JackClientControl* control = client->GetClientControl();
  117. control->fTransportState = JackTransportStopped;
  118. control->fTransportSync = false;
  119. control->fTransportTimebase = false;
  120. jack_log("MakeAllStopping ref = %ld", i);
  121. }
  122. }
  123. }
  124. // RT
  125. void JackTransportEngine::MakeAllLocating(JackClientInterface** table)
  126. {
  127. for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
  128. JackClientInterface* client = table[i];
  129. if (client) {
  130. JackClientControl* control = client->GetClientControl();
  131. control->fTransportState = JackTransportStopped;
  132. control->fTransportSync = true;
  133. control->fTransportTimebase = true;
  134. jack_log("MakeAllLocating ref = %ld", i);
  135. }
  136. }
  137. }
  138. // RT
  139. void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time)
  140. {
  141. jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
  142. pending->usecs = time;
  143. pending->frame_rate = frame_rate;
  144. WriteNextStateStop(1);
  145. }
  146. // RT
  147. void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
  148. {
  149. TrySwitchState(1); // Switch from "pending" to "current", it always works since there is always a pending state
  150. /* Handle any new transport command from the last cycle. */
  151. transport_command_t cmd = fTransportCmd;
  152. if (cmd != fPreviousCmd) {
  153. fPreviousCmd = cmd;
  154. jack_log("transport command: %s", (cmd == TransportCommandStart ? "Transport start" : "Transport stop"));
  155. } else {
  156. cmd = TransportCommandNone;
  157. }
  158. /* state transition switch */
  159. switch (fTransportState) {
  160. case JackTransportStopped:
  161. // Set a JackTransportStarting for the current cycle, if all clients are ready (no slow_sync) ==> JackTransportRolling next state
  162. if (cmd == TransportCommandStart) {
  163. jack_log("transport stopped ==> starting frame = %d", ReadCurrentState()->frame);
  164. fTransportState = JackTransportStarting;
  165. MakeAllStartingLocating(table);
  166. SyncTimeout(frame_rate, buffer_size);
  167. } else if (fPendingPos) {
  168. jack_log("transport stopped ==> stopped (locating) frame = %d", ReadCurrentState()->frame);
  169. MakeAllLocating(table);
  170. }
  171. break;
  172. case JackTransportStarting:
  173. if (cmd == TransportCommandStop) {
  174. jack_log("transport starting ==> stopped frame = %d", ReadCurrentState()->frame);
  175. fTransportState = JackTransportStopped;
  176. MakeAllStopping(table);
  177. } else if (fPendingPos) {
  178. jack_log("transport starting ==> starting frame = %d", ReadCurrentState()->frame);
  179. fTransportState = JackTransportStarting;
  180. MakeAllStartingLocating(table);
  181. SyncTimeout(frame_rate, buffer_size);
  182. } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) { // Slow clients may still catch up
  183. if (fNetworkSync) {
  184. jack_log("transport starting ==> netstarting frame = %d");
  185. fTransportState = JackTransportNetStarting;
  186. } else {
  187. jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft);
  188. fTransportState = JackTransportRolling;
  189. }
  190. }
  191. break;
  192. case JackTransportRolling:
  193. if (cmd == TransportCommandStop) {
  194. jack_log("transport rolling ==> stopped");
  195. fTransportState = JackTransportStopped;
  196. MakeAllStopping(table);
  197. } else if (fPendingPos) {
  198. jack_log("transport rolling ==> starting");
  199. fTransportState = JackTransportStarting;
  200. MakeAllStartingLocating(table);
  201. SyncTimeout(frame_rate, buffer_size);
  202. }
  203. break;
  204. case JackTransportNetStarting:
  205. break;
  206. default:
  207. jack_error("Invalid JACK transport state: %d", fTransportState);
  208. }
  209. /* Update timebase, if needed. */
  210. if (fTransportState == JackTransportRolling) {
  211. jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
  212. pending->frame += buffer_size;
  213. WriteNextStateStop(1);
  214. }
  215. /* See if an asynchronous position request arrived during the last cycle. */
  216. jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
  217. if (fPendingPos) {
  218. jack_log("New pos = %ld", request->frame);
  219. jack_position_t* pending = WriteNextStateStart(1);
  220. CopyPosition(request, pending);
  221. WriteNextStateStop(1);
  222. }
  223. }
  224. // Client
  225. void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
  226. {
  227. UInt16 next_index = GetCurrentIndex();
  228. UInt16 cur_index;
  229. do {
  230. cur_index = next_index;
  231. memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
  232. next_index = GetCurrentIndex();
  233. } while (cur_index != next_index); // Until a coherent state has been read
  234. }
  235. void JackTransportEngine::RequestNewPos(jack_position_t* pos)
  236. {
  237. jack_position_t* request = WriteNextStateStart(2);
  238. pos->unique_1 = pos->unique_2 = GenerateUniqueID();
  239. CopyPosition(pos, request);
  240. jack_log("RequestNewPos pos = %ld", pos->frame);
  241. WriteNextStateStop(2);
  242. }
  243. jack_transport_state_t JackTransportEngine::Query(jack_position_t* pos)
  244. {
  245. if (pos)
  246. ReadCurrentPos(pos);
  247. return GetState();
  248. }
  249. jack_nframes_t JackTransportEngine::GetCurrentFrame()
  250. {
  251. jack_position_t pos;
  252. ReadCurrentPos(&pos);
  253. if (fTransportState == JackTransportRolling) {
  254. float usecs = GetMicroSeconds() - pos.usecs;
  255. jack_nframes_t elapsed = (jack_nframes_t)floor((((float) pos.frame_rate) / 1000000.0f) * usecs);
  256. return pos.frame + elapsed;
  257. } else {
  258. return pos.frame;
  259. }
  260. }
  261. // RT, client
  262. void JackTransportEngine::CopyPosition(jack_position_t* from, jack_position_t* to)
  263. {
  264. int tries = 0;
  265. long timeout = 1000;
  266. do {
  267. /* throttle the busy wait if we don't get the answer
  268. * very quickly. See comment above about this
  269. * design.
  270. */
  271. if (tries > 10) {
  272. JackSleep(20);
  273. tries = 0;
  274. /* debug code to avoid system hangs... */
  275. if (--timeout == 0) {
  276. jack_error("hung in loop copying position B");
  277. abort();
  278. }
  279. }
  280. *to = *from;
  281. tries++;
  282. } while (to->unique_1 != to->unique_2);
  283. }
  284. } // end of namespace