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.

273 lines
9.1KB

  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 General Public License as published by
  6. the Free Software Foundation; either version 2 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 General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. */
  16. #include "JackTransportEngine.h"
  17. #include "JackClientControl.h"
  18. #include "JackError.h"
  19. #include "JackTime.h"
  20. #include <assert.h>
  21. #include <stdlib.h>
  22. using namespace std;
  23. namespace Jack
  24. {
  25. JackTransportEngine::JackTransportEngine(): JackAtomicArrayState<jack_position_t>()
  26. {
  27. fTransportState = JackTransportStopped;
  28. fTransportCmd = fPreviousCmd = TransportCommandStop;
  29. fSyncTimeout = 2000000; /* 2 second default */
  30. fSyncTimeLeft = 0;
  31. fTimeBaseMaster = -1;
  32. fWriteCounter = 0;
  33. fPendingPos = false;
  34. }
  35. // compute the number of cycle for timeout
  36. void JackTransportEngine::SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size)
  37. {
  38. long buf_usecs = (long)((buffer_size * (jack_time_t)1000000) / frame_rate);
  39. fSyncTimeLeft = fSyncTimeout / buf_usecs;
  40. jack_log("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld", (long)fSyncTimeout, (long)fSyncTimeLeft);
  41. }
  42. // Server
  43. int JackTransportEngine::ResetTimebase(int refnum)
  44. {
  45. if (fTimeBaseMaster == refnum) {
  46. jack_position_t* request = WriteNextStateStart(2); // To check
  47. request->valid = (jack_position_bits_t)0;
  48. WriteNextStateStop(2);
  49. fTimeBaseMaster = -1;
  50. return 0;
  51. } else {
  52. return EINVAL;
  53. }
  54. }
  55. // Server
  56. int JackTransportEngine::SetTimebase(int refnum, bool conditionnal)
  57. {
  58. if (conditionnal && fTimeBaseMaster > 0) {
  59. if (refnum != fTimeBaseMaster) {
  60. jack_log("conditional timebase for ref = %ld failed: %ld is already the master", refnum, fTimeBaseMaster);
  61. return EBUSY;
  62. } else {
  63. jack_log("ref = %ld was already timebase master", refnum);
  64. return 0;
  65. }
  66. } else {
  67. fTimeBaseMaster = refnum;
  68. jack_log("new timebase master: ref = %ld", refnum);
  69. return 0;
  70. }
  71. }
  72. // RT
  73. bool JackTransportEngine::CheckAllRolling(JackClientInterface** table)
  74. {
  75. for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
  76. JackClientInterface* client = table[i];
  77. if (client && client->GetClientControl()->fTransportState != JackTransportRolling) {
  78. jack_log("CheckAllRolling ref = %ld is not rolling", i);
  79. return false;
  80. }
  81. }
  82. jack_log("CheckAllRolling");
  83. return true;
  84. }
  85. // RT
  86. void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table)
  87. {
  88. for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
  89. JackClientInterface* client = table[i];
  90. if (client) {
  91. JackClientControl* control = client->GetClientControl();
  92. // Inactive clients don't have their process function called at all, so they must appear as already "rolling" for the transport....
  93. control->fTransportState = (control->fActive && control->fCallback[kRealTimeCallback]) ? JackTransportStarting : JackTransportRolling;
  94. control->fTransportSync = true;
  95. control->fTransportTimebase = true;
  96. jack_log("MakeAllStartingLocating ref = %ld", i);
  97. }
  98. }
  99. }
  100. // RT
  101. void JackTransportEngine::MakeAllStopping(JackClientInterface** table)
  102. {
  103. for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
  104. JackClientInterface* client = table[i];
  105. if (client) {
  106. JackClientControl* control = client->GetClientControl();
  107. control->fTransportState = JackTransportStopped;
  108. control->fTransportSync = false;
  109. control->fTransportTimebase = false;
  110. jack_log("MakeAllStopping ref = %ld", i);
  111. }
  112. }
  113. }
  114. // RT
  115. void JackTransportEngine::MakeAllLocating(JackClientInterface** table)
  116. {
  117. for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
  118. JackClientInterface* client = table[i];
  119. if (client) {
  120. JackClientControl* control = client->GetClientControl();
  121. control->fTransportState = JackTransportStopped;
  122. control->fTransportTimebase = true;
  123. jack_log("MakeAllLocating ref = %ld", i);
  124. }
  125. }
  126. }
  127. // RT
  128. void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time)
  129. {
  130. jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
  131. pending->usecs = time;
  132. pending->frame_rate = frame_rate;
  133. WriteNextStateStop(1);
  134. }
  135. // RT
  136. void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
  137. {
  138. TrySwitchState(1); // Switch from "pending" to "current", it always works since there is always a pending state
  139. /* Handle any new transport command from the last cycle. */
  140. transport_command_t cmd = fTransportCmd;
  141. if (cmd != fPreviousCmd) {
  142. fPreviousCmd = cmd;
  143. jack_log("transport command: %s", (cmd == TransportCommandStart ? "Transport start" : "Transport stop"));
  144. } else {
  145. cmd = TransportCommandNone;
  146. }
  147. /* state transition switch */
  148. switch (fTransportState) {
  149. case JackTransportStopped:
  150. // Set a JackTransportStarting for the current cycle, if all clients are ready (no slow_sync) ==> JackTransportRolling next state
  151. if (cmd == TransportCommandStart) {
  152. jack_log("transport stopped ==> starting");
  153. fTransportState = JackTransportStarting;
  154. MakeAllStartingLocating(table);
  155. SyncTimeout(frame_rate, buffer_size);
  156. } else if (fPendingPos) {
  157. jack_log("transport stopped ==> stopped (locating)");
  158. MakeAllLocating(table);
  159. }
  160. break;
  161. case JackTransportStarting:
  162. if (cmd == TransportCommandStop) {
  163. jack_log("transport starting ==> stopped");
  164. fTransportState = JackTransportStopped;
  165. MakeAllStopping(table);
  166. } else if (fPendingPos) {
  167. jack_log("transport starting ==> starting");
  168. fTransportState = JackTransportStarting;
  169. MakeAllStartingLocating(table);
  170. SyncTimeout(frame_rate, buffer_size);
  171. } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) { // Slow clients may still catch up
  172. jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft);
  173. fTransportState = JackTransportRolling;
  174. }
  175. break;
  176. case JackTransportRolling:
  177. if (cmd == TransportCommandStop) {
  178. jack_log("transport rolling ==> stopped");
  179. fTransportState = JackTransportStopped;
  180. MakeAllStopping(table);
  181. } else if (fPendingPos) {
  182. jack_log("transport rolling ==> starting");
  183. fTransportState = JackTransportStarting;
  184. MakeAllStartingLocating(table);
  185. SyncTimeout(frame_rate, buffer_size);
  186. }
  187. break;
  188. default:
  189. jack_error("Invalid JACK transport state: %d", fTransportState);
  190. }
  191. /* Update timebase, if needed. */
  192. if (fTransportState == JackTransportRolling) {
  193. jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
  194. pending->frame += buffer_size;
  195. WriteNextStateStop(1);
  196. }
  197. /* See if an asynchronous position request arrived during the last cycle. */
  198. jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
  199. if (fPendingPos) {
  200. jack_log("New pos = %ld", request->frame);
  201. jack_position_t* pending = WriteNextStateStart(1);
  202. TransportCopyPosition(request, pending);
  203. WriteNextStateStop(1);
  204. }
  205. }
  206. // Client
  207. void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
  208. {
  209. UInt16 next_index = GetCurrentIndex();
  210. UInt16 cur_index;
  211. do {
  212. cur_index = next_index;
  213. memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
  214. next_index = GetCurrentIndex();
  215. } while (cur_index != next_index); // Until a coherent state has been read
  216. }
  217. // RT, client
  218. void JackTransportEngine::TransportCopyPosition(jack_position_t* from, jack_position_t* to)
  219. {
  220. int tries = 0;
  221. long timeout = 1000;
  222. do {
  223. /* throttle the busy wait if we don't get the answer
  224. * very quickly. See comment above about this
  225. * design.
  226. */
  227. if (tries > 10) {
  228. JackSleep(20);
  229. tries = 0;
  230. /* debug code to avoid system hangs... */
  231. if (--timeout == 0) {
  232. jack_error("hung in loop copying position B");
  233. abort();
  234. }
  235. }
  236. *to = *from;
  237. tries++;
  238. } while (to->unique_1 != to->unique_2);
  239. }
  240. } // end of namespace