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.

254 lines
8.5KB

  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. // Inactive clients don't have their process function called at all, so they appear as already "rolling" for the transport....
  92. client->GetClientControl()->fTransportState = (client->GetClientControl()->fActive) ? JackTransportStarting : JackTransportRolling;
  93. client->GetClientControl()->fTransportSync = true;
  94. client->GetClientControl()->fTransportTimebase = true;
  95. jack_log("MakeAllStartingLocating ref = %ld", i);
  96. }
  97. }
  98. }
  99. // RT
  100. void JackTransportEngine::MakeAllStopping(JackClientInterface** table)
  101. {
  102. for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
  103. JackClientInterface* client = table[i];
  104. if (client) {
  105. client->GetClientControl()->fTransportSync = false;
  106. client->GetClientControl()->fTransportTimebase = false;
  107. client->GetClientControl()->fTransportState = JackTransportStopped;
  108. jack_log("MakeAllStopping ref = %ld", i);
  109. }
  110. }
  111. }
  112. // RT
  113. void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time) // really needed?? (would be done in CycleEnd...)
  114. {
  115. jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
  116. pending->usecs = time;
  117. pending->frame_rate = frame_rate;
  118. WriteNextStateStop(1);
  119. }
  120. // RT
  121. void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
  122. {
  123. TrySwitchState(1); // Switch from "pending" to "current", it always works since there is always a pending state
  124. /* Handle any new transport command from the last cycle. */
  125. transport_command_t cmd = fTransportCmd;
  126. if (cmd != fPreviousCmd) {
  127. fPreviousCmd = cmd;
  128. jack_log("transport command: %s", (cmd == TransportCommandStart ? "START" : "STOP"));
  129. } else {
  130. cmd = TransportCommandNone;
  131. }
  132. /* state transition switch */
  133. switch (fTransportState) {
  134. case JackTransportStopped:
  135. // Set a JackTransportStarting for the current cycle, if all clients are ready (no slow_sync) ==> JackTransportRolling next state
  136. if (cmd == TransportCommandStart) {
  137. jack_log("transport stopped ==> starting");
  138. fTransportState = JackTransportStarting;
  139. MakeAllStartingLocating(table);
  140. SyncTimeout(frame_rate, buffer_size);
  141. }
  142. break;
  143. case JackTransportStarting:
  144. if (cmd == TransportCommandStop) {
  145. jack_log("transport starting ==> stopped");
  146. fTransportState = JackTransportStopped;
  147. MakeAllStopping(table);
  148. } else if (fPendingPos) {
  149. jack_log("transport starting ==> starting");
  150. fTransportState = JackTransportStarting;
  151. MakeAllStartingLocating(table);
  152. SyncTimeout(frame_rate, buffer_size);
  153. } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) { // Slow clients may still catch up
  154. jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft);
  155. fTransportState = JackTransportRolling;
  156. }
  157. break;
  158. case JackTransportRolling:
  159. if (cmd == TransportCommandStop) {
  160. jack_log("transport rolling ==> stopped");
  161. fTransportState = JackTransportStopped;
  162. MakeAllStopping(table);
  163. } else if (fPendingPos) {
  164. jack_log("transport rolling ==> starting");
  165. fTransportState = JackTransportStarting;
  166. MakeAllStartingLocating(table);
  167. SyncTimeout(frame_rate, buffer_size);
  168. }
  169. break;
  170. default:
  171. jack_error("Invalid JACK transport state: %d", fTransportState);
  172. }
  173. /* Update timebase, if needed. */
  174. if (fTransportState == JackTransportRolling) {
  175. jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
  176. pending->frame += buffer_size;
  177. WriteNextStateStop(1);
  178. }
  179. /* See if an asynchronous position request arrived during the last cycle. */
  180. jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
  181. if (fPendingPos) {
  182. jack_log("New pos = %ld", request->frame);
  183. jack_position_t* pending = WriteNextStateStart(1);
  184. TransportCopyPosition(request, pending);
  185. WriteNextStateStop(1);
  186. }
  187. }
  188. // Client
  189. void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
  190. {
  191. UInt16 next_index = GetCurrentIndex();
  192. UInt16 cur_index;
  193. do {
  194. cur_index = next_index;
  195. memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
  196. next_index = GetCurrentIndex();
  197. } while (cur_index != next_index); // Until a coherent state has been read
  198. }
  199. // RT, client
  200. void JackTransportEngine::TransportCopyPosition(jack_position_t* from, jack_position_t* to)
  201. {
  202. int tries = 0;
  203. long timeout = 1000;
  204. do {
  205. /* throttle the busy wait if we don't get the answer
  206. * very quickly. See comment above about this
  207. * design.
  208. */
  209. if (tries > 10) {
  210. JackSleep(20);
  211. tries = 0;
  212. /* debug code to avoid system hangs... */
  213. if (--timeout == 0) {
  214. jack_error("hung in loop copying position B");
  215. abort();
  216. }
  217. }
  218. *to = *from;
  219. tries++;
  220. } while (to->unique_1 != to->unique_2);
  221. }
  222. } // end of namespace