merge new MTDM code from Fons' latest release.
[jack2.git] / common / JackTransportEngine.cpp
blobfdcea0c0a1f6dd691f8e293c6407b22a928664ed
1 /*
2 Copyright (C) 2001 Paul Davis
3 Copyright (C) 2004-2008 Grame
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #include "JackTransportEngine.h"
22 #include "JackClientInterface.h"
23 #include "JackClientControl.h"
24 #include "JackEngineControl.h"
25 #include "JackGlobals.h"
26 #include "JackError.h"
27 #include "JackTime.h"
28 #include <assert.h>
29 #include <math.h>
30 #include <stdlib.h>
32 using namespace std;
34 namespace Jack
37 JackTransportEngine::JackTransportEngine(): JackAtomicArrayState<jack_position_t>()
39 fTransportState = JackTransportStopped;
40 fTransportCmd = fPreviousCmd = TransportCommandStop;
41 fSyncTimeout = 10000000; /* 10 seconds default...
42 in case of big netjack1 roundtrip */
43 fSyncTimeLeft = 0;
44 fTimeBaseMaster = -1;
45 fWriteCounter = 0;
46 fConditionnal = false;
47 fPendingPos = false;
48 fNetworkSync = false;
51 // compute the number of cycle for timeout
52 void JackTransportEngine::SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size)
54 long buf_usecs = (long)((buffer_size * (jack_time_t)1000000) / frame_rate);
55 fSyncTimeLeft = fSyncTimeout / buf_usecs;
56 jack_log("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld", (long)fSyncTimeout, (long)fSyncTimeLeft);
59 // Server
60 int JackTransportEngine::ResetTimebase(int refnum)
62 if (fTimeBaseMaster == refnum) {
63 jack_position_t* request = WriteNextStateStart(2); // To check
64 request->valid = (jack_position_bits_t)0;
65 WriteNextStateStop(2);
66 fTimeBaseMaster = -1;
67 return 0;
68 } else {
69 return EINVAL;
73 // Server
74 int JackTransportEngine::SetTimebaseMaster(int refnum, bool conditionnal)
76 if (conditionnal && fTimeBaseMaster > 0) {
77 if (refnum != fTimeBaseMaster) {
78 jack_log("conditional timebase for ref = %ld failed: %ld is already the master", refnum, fTimeBaseMaster);
79 return EBUSY;
80 } else {
81 jack_log("ref = %ld was already timebase master", refnum);
82 return 0;
84 } else {
85 fTimeBaseMaster = refnum;
86 fConditionnal = conditionnal;
87 jack_log("new timebase master: ref = %ld", refnum);
88 return 0;
92 // RT
93 bool JackTransportEngine::CheckAllRolling(JackClientInterface** table)
95 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
96 JackClientInterface* client = table[i];
97 if (client && client->GetClientControl()->fTransportState != JackTransportRolling) {
98 jack_log("CheckAllRolling ref = %ld is not rolling", i);
99 return false;
102 jack_log("CheckAllRolling");
103 return true;
106 // RT
107 void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table)
109 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
110 JackClientInterface* client = table[i];
111 if (client) {
112 JackClientControl* control = client->GetClientControl();
113 // Inactive clients don't have their process function called at all, so they must appear as already "rolling" for the transport....
114 control->fTransportState = (control->fActive && control->fCallback[kRealTimeCallback]) ? JackTransportStarting : JackTransportRolling;
115 control->fTransportSync = true;
116 control->fTransportTimebase = true;
117 jack_log("MakeAllStartingLocating ref = %ld", i);
122 // RT
123 void JackTransportEngine::MakeAllStopping(JackClientInterface** table)
125 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
126 JackClientInterface* client = table[i];
127 if (client) {
128 JackClientControl* control = client->GetClientControl();
129 control->fTransportState = JackTransportStopped;
130 control->fTransportSync = false;
131 control->fTransportTimebase = false;
132 jack_log("MakeAllStopping ref = %ld", i);
137 // RT
138 void JackTransportEngine::MakeAllLocating(JackClientInterface** table)
140 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
141 JackClientInterface* client = table[i];
142 if (client) {
143 JackClientControl* control = client->GetClientControl();
144 control->fTransportState = JackTransportStopped;
145 control->fTransportSync = true;
146 control->fTransportTimebase = true;
147 jack_log("MakeAllLocating ref = %ld", i);
152 // RT
153 void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time)
155 jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
156 pending->usecs = time;
157 pending->frame_rate = frame_rate;
158 WriteNextStateStop(1);
161 // RT
162 void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
164 TrySwitchState(1); // Switch from "pending" to "current", it always works since there is always a pending state
166 /* Handle any new transport command from the last cycle. */
167 transport_command_t cmd = fTransportCmd;
168 if (cmd != fPreviousCmd) {
169 fPreviousCmd = cmd;
170 jack_log("transport command: %s", (cmd == TransportCommandStart ? "Transport start" : "Transport stop"));
171 } else {
172 cmd = TransportCommandNone;
175 /* state transition switch */
176 switch (fTransportState) {
178 case JackTransportStopped:
179 // Set a JackTransportStarting for the current cycle, if all clients are ready (no slow_sync) ==> JackTransportRolling next state
180 if (cmd == TransportCommandStart) {
181 jack_log("transport stopped ==> starting frame = %d", ReadCurrentState()->frame);
182 fTransportState = JackTransportStarting;
183 MakeAllStartingLocating(table);
184 SyncTimeout(frame_rate, buffer_size);
185 } else if (fPendingPos) {
186 jack_log("transport stopped ==> stopped (locating) frame = %d", ReadCurrentState()->frame);
187 MakeAllLocating(table);
189 break;
191 case JackTransportStarting:
192 if (cmd == TransportCommandStop) {
193 jack_log("transport starting ==> stopped frame = %d", ReadCurrentState()->frame);
194 fTransportState = JackTransportStopped;
195 MakeAllStopping(table);
196 } else if (fPendingPos) {
197 jack_log("transport starting ==> starting frame = %d"), ReadCurrentState()->frame;
198 fTransportState = JackTransportStarting;
199 MakeAllStartingLocating(table);
200 SyncTimeout(frame_rate, buffer_size);
201 } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) { // Slow clients may still catch up
202 if (fNetworkSync) {
203 jack_log("transport starting ==> netstarting frame = %d");
204 fTransportState = JackTransportNetStarting;
205 } else {
206 jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft);
207 fTransportState = JackTransportRolling;
210 break;
212 case JackTransportRolling:
213 if (cmd == TransportCommandStop) {
214 jack_log("transport rolling ==> stopped");
215 fTransportState = JackTransportStopped;
216 MakeAllStopping(table);
217 } else if (fPendingPos) {
218 jack_log("transport rolling ==> starting");
219 fTransportState = JackTransportStarting;
220 MakeAllStartingLocating(table);
221 SyncTimeout(frame_rate, buffer_size);
223 break;
225 case JackTransportNetStarting:
226 break;
228 default:
229 jack_error("Invalid JACK transport state: %d", fTransportState);
232 /* Update timebase, if needed. */
233 if (fTransportState == JackTransportRolling) {
234 jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
235 pending->frame += buffer_size;
236 WriteNextStateStop(1);
239 /* See if an asynchronous position request arrived during the last cycle. */
240 jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
241 if (fPendingPos) {
242 jack_log("New pos = %ld", request->frame);
243 jack_position_t* pending = WriteNextStateStart(1);
244 CopyPosition(request, pending);
245 WriteNextStateStop(1);
249 // Client
250 void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
252 UInt16 next_index = GetCurrentIndex();
253 UInt16 cur_index;
254 do {
255 cur_index = next_index;
256 memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
257 next_index = GetCurrentIndex();
258 } while (cur_index != next_index); // Until a coherent state has been read
261 void JackTransportEngine::RequestNewPos(jack_position_t* pos)
263 jack_position_t* request = WriteNextStateStart(2);
264 pos->unique_1 = pos->unique_2 = GenerateUniqueID();
265 CopyPosition(pos, request);
266 jack_log("RequestNewPos pos = %ld", pos->frame);
267 WriteNextStateStop(2);
270 jack_transport_state_t JackTransportEngine::Query(jack_position_t* pos)
272 if (pos)
273 ReadCurrentPos(pos);
274 return GetState();
277 jack_nframes_t JackTransportEngine::GetCurrentFrame()
279 jack_position_t pos;
280 ReadCurrentPos(&pos);
282 if (fTransportState == JackTransportRolling) {
283 float usecs = GetMicroSeconds() - pos.usecs;
284 jack_nframes_t elapsed = (jack_nframes_t)floor((((float) pos.frame_rate) / 1000000.0f) * usecs);
285 return pos.frame + elapsed;
286 } else {
287 return pos.frame;
291 // RT, client
292 void JackTransportEngine::CopyPosition(jack_position_t* from, jack_position_t* to)
294 int tries = 0;
295 long timeout = 1000;
297 do {
298 /* throttle the busy wait if we don't get the answer
299 * very quickly. See comment above about this
300 * design.
302 if (tries > 10) {
303 JackSleep(20);
304 tries = 0;
306 /* debug code to avoid system hangs... */
307 if (--timeout == 0) {
308 jack_error("hung in loop copying position B");
309 abort();
312 *to = *from;
313 tries++;
315 } while (to->unique_1 != to->unique_2);
319 } // end of namespace