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.

277 lines
9.2KB

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