Remove non-jackdbus man pages
[jackdbus.git] / common / JackTransportEngine.cpp
blob09bf44d72e50e757944fe7151085dff61ee01f33
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 static_assert(offsetof(JackTransportEngine, fWriteCounter) % sizeof(fWriteCounter) == 0,
40 "fWriteCounter must be first member of JackTransportEngine to ensure its alignment");
41 fTransportState = JackTransportStopped;
42 fTransportCmd = fPreviousCmd = TransportCommandStop;
43 fSyncTimeout = 10000000; /* 10 seconds default...
44 in case of big netjack1 roundtrip */
45 fSyncTimeLeft = 0;
46 fTimeBaseMaster = -1;
47 fWriteCounter = 0;
48 fConditionnal = false;
49 fPendingPos = false;
50 fNetworkSync = false;
53 // compute the number of cycle for timeout
54 void JackTransportEngine::SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size)
56 long buf_usecs = (long)((buffer_size * (jack_time_t)1000000) / frame_rate);
57 fSyncTimeLeft = fSyncTimeout / buf_usecs;
58 jack_log("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld", (long)fSyncTimeout, (long)fSyncTimeLeft);
61 // Server
62 int JackTransportEngine::ResetTimebase(int refnum)
64 if (fTimeBaseMaster == refnum) {
65 jack_position_t* request = WriteNextStateStart(2); // To check
66 request->valid = (jack_position_bits_t)0;
67 WriteNextStateStop(2);
68 fTimeBaseMaster = -1;
69 return 0;
70 } else {
71 return EINVAL;
75 // Server
76 int JackTransportEngine::SetTimebaseMaster(int refnum, bool conditionnal)
78 if (conditionnal && fTimeBaseMaster > 0) {
79 if (refnum != fTimeBaseMaster) {
80 jack_log("conditional timebase for ref = %ld failed: %ld is already the master", refnum, fTimeBaseMaster);
81 return EBUSY;
82 } else {
83 jack_log("ref = %ld was already timebase master", refnum);
84 return 0;
86 } else {
87 fTimeBaseMaster = refnum;
88 fConditionnal = conditionnal;
89 jack_log("new timebase master: ref = %ld", refnum);
90 return 0;
94 // RT
95 bool JackTransportEngine::CheckAllRolling(JackClientInterface** table)
97 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
98 JackClientInterface* client = table[i];
99 if (client && client->GetClientControl()->fTransportState != JackTransportRolling) {
100 jack_log("CheckAllRolling ref = %ld is not rolling", i);
101 return false;
104 jack_log("CheckAllRolling");
105 return true;
108 // RT
109 void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table)
111 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
112 JackClientInterface* client = table[i];
113 if (client) {
114 JackClientControl* control = client->GetClientControl();
115 // Inactive clients don't have their process function called at all, so they must appear as already "rolling" for the transport....
116 control->fTransportState = (control->fActive && control->fCallback[kRealTimeCallback]) ? JackTransportStarting : JackTransportRolling;
117 control->fTransportSync = true;
118 control->fTransportTimebase = true;
119 jack_log("MakeAllStartingLocating ref = %ld", i);
124 // RT
125 void JackTransportEngine::MakeAllStopping(JackClientInterface** table)
127 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
128 JackClientInterface* client = table[i];
129 if (client) {
130 JackClientControl* control = client->GetClientControl();
131 control->fTransportState = JackTransportStopped;
132 control->fTransportSync = false;
133 control->fTransportTimebase = false;
134 jack_log("MakeAllStopping ref = %ld", i);
139 // RT
140 void JackTransportEngine::MakeAllLocating(JackClientInterface** table)
142 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
143 JackClientInterface* client = table[i];
144 if (client) {
145 JackClientControl* control = client->GetClientControl();
146 control->fTransportState = JackTransportStopped;
147 control->fTransportSync = true;
148 control->fTransportTimebase = true;
149 jack_log("MakeAllLocating ref = %ld", i);
154 // RT
155 void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time)
157 jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
158 pending->usecs = time;
159 pending->frame_rate = frame_rate;
160 WriteNextStateStop(1);
163 // RT
164 void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
166 TrySwitchState(1); // Switch from "pending" to "current", it always works since there is always a pending state
168 /* Handle any new transport command from the last cycle. */
169 transport_command_t cmd = fTransportCmd;
170 if (cmd != fPreviousCmd) {
171 fPreviousCmd = cmd;
172 jack_log("transport command: %s", (cmd == TransportCommandStart ? "Transport start" : "Transport stop"));
173 } else {
174 cmd = TransportCommandNone;
177 /* state transition switch */
178 switch (fTransportState) {
180 case JackTransportStopped:
181 // Set a JackTransportStarting for the current cycle, if all clients are ready (no slow_sync) ==> JackTransportRolling next state
182 if (cmd == TransportCommandStart) {
183 jack_log("transport stopped ==> starting frame = %d", ReadCurrentState()->frame);
184 fTransportState = JackTransportStarting;
185 MakeAllStartingLocating(table);
186 SyncTimeout(frame_rate, buffer_size);
187 } else if (fPendingPos) {
188 jack_log("transport stopped ==> stopped (locating) frame = %d", ReadCurrentState()->frame);
189 MakeAllLocating(table);
191 break;
193 case JackTransportStarting:
194 if (cmd == TransportCommandStop) {
195 jack_log("transport starting ==> stopped frame = %d", ReadCurrentState()->frame);
196 fTransportState = JackTransportStopped;
197 MakeAllStopping(table);
198 } else if (fPendingPos) {
199 jack_log("transport starting ==> starting frame = %d", ReadCurrentState()->frame);
200 fTransportState = JackTransportStarting;
201 MakeAllStartingLocating(table);
202 SyncTimeout(frame_rate, buffer_size);
203 } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) { // Slow clients may still catch up
204 if (fNetworkSync) {
205 jack_log("transport starting ==> netstarting frame = %d");
206 fTransportState = JackTransportNetStarting;
207 } else {
208 jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft);
209 fTransportState = JackTransportRolling;
212 break;
214 case JackTransportRolling:
215 if (cmd == TransportCommandStop) {
216 jack_log("transport rolling ==> stopped");
217 fTransportState = JackTransportStopped;
218 MakeAllStopping(table);
219 } else if (fPendingPos) {
220 jack_log("transport rolling ==> starting");
221 fTransportState = JackTransportStarting;
222 MakeAllStartingLocating(table);
223 SyncTimeout(frame_rate, buffer_size);
225 break;
227 case JackTransportNetStarting:
228 break;
230 default:
231 jack_error("Invalid JACK transport state: %d", fTransportState);
234 /* Update timebase, if needed. */
235 if (fTransportState == JackTransportRolling) {
236 jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
237 pending->frame += buffer_size;
238 WriteNextStateStop(1);
241 /* See if an asynchronous position request arrived during the last cycle. */
242 jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
243 if (fPendingPos) {
244 jack_log("New pos = %ld", request->frame);
245 jack_position_t* pending = WriteNextStateStart(1);
246 CopyPosition(request, pending);
247 WriteNextStateStop(1);
251 // Client
252 void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
254 UInt16 next_index = GetCurrentIndex();
255 UInt16 cur_index;
256 do {
257 cur_index = next_index;
258 memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
259 next_index = GetCurrentIndex();
260 } while (cur_index != next_index); // Until a coherent state has been read
263 void JackTransportEngine::RequestNewPos(jack_position_t* pos)
265 jack_position_t* request = WriteNextStateStart(2);
266 pos->unique_1 = pos->unique_2 = GenerateUniqueID();
267 CopyPosition(pos, request);
268 jack_log("RequestNewPos pos = %ld", pos->frame);
269 WriteNextStateStop(2);
272 jack_transport_state_t JackTransportEngine::Query(jack_position_t* pos)
274 if (pos)
275 ReadCurrentPos(pos);
276 return GetState();
279 jack_nframes_t JackTransportEngine::GetCurrentFrame()
281 jack_position_t pos;
282 ReadCurrentPos(&pos);
284 if (fTransportState == JackTransportRolling) {
285 float usecs = GetMicroSeconds() - pos.usecs;
286 jack_nframes_t elapsed = (jack_nframes_t)floor((((float) pos.frame_rate) / 1000000.0f) * usecs);
287 return pos.frame + elapsed;
288 } else {
289 return pos.frame;
293 // RT, client
294 void JackTransportEngine::CopyPosition(jack_position_t* from, jack_position_t* to)
296 int tries = 0;
297 long timeout = 1000;
299 do {
300 /* throttle the busy wait if we don't get the answer
301 * very quickly. See comment above about this
302 * design.
304 if (tries > 10) {
305 JackSleep(20);
306 tries = 0;
308 /* debug code to avoid system hangs... */
309 if (--timeout == 0) {
310 jack_error("hung in loop copying position B");
311 abort();
314 *to = *from;
315 tries++;
317 } while (to->unique_1 != to->unique_2);
321 } // end of namespace