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.

260 lines
8.9KB

  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. int JackTransportEngine::ResetTimebase(int refnum)
  43. {
  44. if (fTimeBaseMaster == refnum) {
  45. jack_position_t* request = WriteNextStateStart(2); // To check
  46. request->valid = (jack_position_bits_t)0;
  47. WriteNextStateStop(2);
  48. fTimeBaseMaster = -1;
  49. return 0;
  50. } else {
  51. return EINVAL;
  52. }
  53. }
  54. int JackTransportEngine::SetTimebase(int refnum, bool conditionnal)
  55. {
  56. if (conditionnal && fTimeBaseMaster > 0) {
  57. if (refnum != fTimeBaseMaster) {
  58. jack_log("conditional timebase for ref = %ld failed: %ld is already the master", refnum, fTimeBaseMaster);
  59. return EBUSY;
  60. } else {
  61. jack_log("ref = %ld was already timebase master", refnum);
  62. return 0;
  63. }
  64. } else {
  65. fTimeBaseMaster = refnum;
  66. jack_log("new timebase master: ref = %ld", refnum);
  67. return 0;
  68. }
  69. }
  70. bool JackTransportEngine::CheckOneSynching(JackClientInterface** table)
  71. {
  72. for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
  73. JackClientInterface* client = table[i];
  74. if (client && client->GetClientControl()->fTransportState == JackTransportSynching) {
  75. jack_log("CheckOneSynching");
  76. return true;
  77. }
  78. }
  79. return false;
  80. }
  81. bool JackTransportEngine::CheckAllRolling(JackClientInterface** table)
  82. {
  83. for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
  84. JackClientInterface* client = table[i];
  85. if (client && client->GetClientControl()->fTransportState != JackTransportRolling) {
  86. jack_log("CheckAllRolling refnum = %ld is not rolling", i);
  87. return false;
  88. }
  89. }
  90. jack_log("CheckAllRolling");
  91. return true;
  92. }
  93. void JackTransportEngine::MakeAllStarting(JackClientInterface** table)
  94. {
  95. for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
  96. JackClientInterface* client = table[i];
  97. if (client) {
  98. // Unactive clients don't have their process function called at all, they appear as already "rolling" for the transport....
  99. client->GetClientControl()->fTransportState = (client->GetClientControl()->fActive) ? JackTransportStarting : JackTransportRolling;
  100. jack_log("MakeAllStarting refnum = %ld ", i);
  101. }
  102. }
  103. jack_log("MakeAllStarting");
  104. }
  105. void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time) // really needed?? (would be done in CycleEnd...)
  106. {
  107. jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
  108. pending->usecs = time;
  109. pending->frame_rate = frame_rate;
  110. WriteNextStateStop(1);
  111. }
  112. void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
  113. {
  114. TrySwitchState(1); // Switch from "pending" to "current", it always works since there is always a pending state
  115. /* Handle any new transport command from the last cycle. */
  116. transport_command_t cmd = fTransportCmd;
  117. if (cmd != fPreviousCmd) {
  118. fPreviousCmd = cmd;
  119. jack_log("transport command: %s", (cmd == TransportCommandStart ? "START" : "STOP"));
  120. } else {
  121. cmd = TransportCommandNone;
  122. }
  123. /* state transition switch */
  124. switch (fTransportState) {
  125. case JackTransportSynching:
  126. if (cmd == TransportCommandStart) {
  127. fTransportState = JackTransportStarting;
  128. MakeAllStarting(table);
  129. SyncTimeout(frame_rate, buffer_size);
  130. jack_log("transport locate ==> starting....");
  131. } else if (fPendingPos) {
  132. fTransportState = JackTransportSynching;
  133. jack_log("transport locate ==> locate....");
  134. } else {
  135. fTransportState = JackTransportStopped;
  136. jack_log("transport locate ==> stopped....");
  137. }
  138. break;
  139. case JackTransportStopped:
  140. // Set a JackTransportStarting for the current cycle, if all clients are ready (now slow_sync) ==> JackTransportRolling next state
  141. if (cmd == TransportCommandStart) {
  142. fTransportState = JackTransportStarting;
  143. MakeAllStarting(table);
  144. SyncTimeout(frame_rate, buffer_size);
  145. jack_log("transport stopped ==> starting....");
  146. } else if (fPendingPos || CheckOneSynching(table)) {
  147. fTransportState = JackTransportSynching;
  148. jack_log("transport stopped ==> locate....");
  149. }
  150. break;
  151. case JackTransportStarting:
  152. jack_log("transport starting fSyncTimeLeft %ld", fSyncTimeLeft);
  153. if (cmd == TransportCommandStop) {
  154. fTransportState = JackTransportStopped;
  155. jack_log("transport starting ==> stopped");
  156. } else if (fPendingPos) {
  157. fTransportState = JackTransportStarting;
  158. MakeAllStarting(table);
  159. SyncTimeout(frame_rate, buffer_size);
  160. } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) {
  161. fTransportState = JackTransportRolling;
  162. jack_log("transport starting ==> rolling.... fSyncTimeLeft %ld", fSyncTimeLeft);
  163. }
  164. break;
  165. case JackTransportRolling:
  166. if (cmd == TransportCommandStop) {
  167. fTransportState = JackTransportStopped;
  168. jack_log("transport rolling ==> stopped");
  169. } else if (fPendingPos || CheckOneSynching(table)) {
  170. fTransportState = JackTransportStarting;
  171. MakeAllStarting(table);
  172. SyncTimeout(frame_rate, buffer_size);
  173. jack_log("transport rolling ==> starting....");
  174. }
  175. break;
  176. default:
  177. jack_error("Invalid JACK transport state: %d", fTransportState);
  178. }
  179. /* Update timebase, if needed. */
  180. if (fTransportState == JackTransportRolling) {
  181. jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
  182. pending->frame += buffer_size;
  183. WriteNextStateStop(1);
  184. }
  185. /* See if an asynchronous position request arrived during the last cycle. */
  186. jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
  187. if (fPendingPos) {
  188. jack_log("New pos = %ld", request->frame);
  189. jack_position_t* pending = WriteNextStateStart(1);
  190. TransportCopyPosition(request, pending);
  191. WriteNextStateStop(1);
  192. }
  193. }
  194. void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
  195. {
  196. UInt16 next_index = GetCurrentIndex();
  197. UInt16 cur_index;
  198. do {
  199. cur_index = next_index;
  200. memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
  201. next_index = GetCurrentIndex();
  202. } while (cur_index != next_index); // Until a coherent state has been read
  203. }
  204. void JackTransportEngine::TransportCopyPosition(jack_position_t* from, jack_position_t* to)
  205. {
  206. int tries = 0;
  207. long timeout = 1000;
  208. do {
  209. /* throttle the busy wait if we don't get the answer
  210. * very quickly. See comment above about this
  211. * design.
  212. */
  213. if (tries > 10) {
  214. JackSleep(20);
  215. tries = 0;
  216. /* debug code to avoid system hangs... */
  217. if (--timeout == 0) {
  218. jack_error("hung in loop copying position B");
  219. abort();
  220. }
  221. }
  222. *to = *from;
  223. tries++;
  224. } while (to->unique_1 != to->unique_2);
  225. }
  226. } // end of namespace