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.

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