Correct jackdmp.cpp (failures case were not correct..). Improve JackCoreAudioDriver...
[jack2.git] / common / JackTransportEngine.cpp
blob418d1180fbdc9a858264dbd81e594e9ecf60af0d
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 = 2000000; /* 2 second default */
42 fSyncTimeLeft = 0;
43 fTimeBaseMaster = -1;
44 fWriteCounter = 0;
45 fConditionnal = false;
46 fPendingPos = false;
47 fNetworkSync = false;
50 // compute the number of cycle for timeout
51 void JackTransportEngine::SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size)
53 long buf_usecs = (long)((buffer_size * (jack_time_t)1000000) / frame_rate);
54 fSyncTimeLeft = fSyncTimeout / buf_usecs;
55 jack_log("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld", (long)fSyncTimeout, (long)fSyncTimeLeft);
58 // Server
59 int JackTransportEngine::ResetTimebase(int refnum)
61 if (fTimeBaseMaster == refnum) {
62 jack_position_t* request = WriteNextStateStart(2); // To check
63 request->valid = (jack_position_bits_t)0;
64 WriteNextStateStop(2);
65 fTimeBaseMaster = -1;
66 return 0;
67 } else {
68 return EINVAL;
72 // Server
73 int JackTransportEngine::SetTimebaseMaster(int refnum, bool conditionnal)
75 if (conditionnal && fTimeBaseMaster > 0) {
76 if (refnum != fTimeBaseMaster) {
77 jack_log("conditional timebase for ref = %ld failed: %ld is already the master", refnum, fTimeBaseMaster);
78 return EBUSY;
79 } else {
80 jack_log("ref = %ld was already timebase master", refnum);
81 return 0;
83 } else {
84 fTimeBaseMaster = refnum;
85 fConditionnal = conditionnal;
86 jack_log("new timebase master: ref = %ld", refnum);
87 return 0;
91 // RT
92 bool JackTransportEngine::CheckAllRolling(JackClientInterface** table)
94 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
95 JackClientInterface* client = table[i];
96 if (client && client->GetClientControl()->fTransportState != JackTransportRolling) {
97 jack_log("CheckAllRolling ref = %ld is not rolling", i);
98 return false;
101 jack_log("CheckAllRolling");
102 return true;
105 // RT
106 void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table)
108 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
109 JackClientInterface* client = table[i];
110 if (client) {
111 JackClientControl* control = client->GetClientControl();
112 // Inactive clients don't have their process function called at all, so they must appear as already "rolling" for the transport....
113 control->fTransportState = (control->fActive && control->fCallback[kRealTimeCallback]) ? JackTransportStarting : JackTransportRolling;
114 control->fTransportSync = true;
115 control->fTransportTimebase = true;
116 jack_log("MakeAllStartingLocating ref = %ld", i);
121 // RT
122 void JackTransportEngine::MakeAllStopping(JackClientInterface** table)
124 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
125 JackClientInterface* client = table[i];
126 if (client) {
127 JackClientControl* control = client->GetClientControl();
128 control->fTransportState = JackTransportStopped;
129 control->fTransportSync = false;
130 control->fTransportTimebase = false;
131 jack_log("MakeAllStopping ref = %ld", i);
136 // RT
137 void JackTransportEngine::MakeAllLocating(JackClientInterface** table)
139 for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
140 JackClientInterface* client = table[i];
141 if (client) {
142 JackClientControl* control = client->GetClientControl();
143 control->fTransportState = JackTransportStopped;
144 control->fTransportSync = true;
145 control->fTransportTimebase = true;
146 jack_log("MakeAllLocating ref = %ld", i);
151 // RT
152 void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time)
154 jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
155 pending->usecs = time;
156 pending->frame_rate = frame_rate;
157 WriteNextStateStop(1);
160 // RT
161 void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
163 TrySwitchState(1); // Switch from "pending" to "current", it always works since there is always a pending state
165 /* Handle any new transport command from the last cycle. */
166 transport_command_t cmd = fTransportCmd;
167 if (cmd != fPreviousCmd) {
168 fPreviousCmd = cmd;
169 jack_log("transport command: %s", (cmd == TransportCommandStart ? "Transport start" : "Transport stop"));
170 } else {
171 cmd = TransportCommandNone;
174 /* state transition switch */
175 switch (fTransportState) {
177 case JackTransportStopped:
178 // Set a JackTransportStarting for the current cycle, if all clients are ready (no slow_sync) ==> JackTransportRolling next state
179 if (cmd == TransportCommandStart) {
180 jack_log("transport stopped ==> starting frame = %d", ReadCurrentState()->frame);
181 fTransportState = JackTransportStarting;
182 MakeAllStartingLocating(table);
183 SyncTimeout(frame_rate, buffer_size);
184 } else if (fPendingPos) {
185 jack_log("transport stopped ==> stopped (locating) frame = %d", ReadCurrentState()->frame);
186 MakeAllLocating(table);
188 break;
190 case JackTransportStarting:
191 if (cmd == TransportCommandStop) {
192 jack_log("transport starting ==> stopped frame = %d", ReadCurrentState()->frame);
193 fTransportState = JackTransportStopped;
194 MakeAllStopping(table);
195 } else if (fPendingPos) {
196 jack_log("transport starting ==> starting frame = %d"), ReadCurrentState()->frame;
197 fTransportState = JackTransportStarting;
198 MakeAllStartingLocating(table);
199 SyncTimeout(frame_rate, buffer_size);
200 } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) { // Slow clients may still catch up
201 if (fNetworkSync) {
202 jack_log("transport starting ==> netstarting frame = %d");
203 fTransportState = JackTransportNetStarting;
204 } else {
205 jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft);
206 fTransportState = JackTransportRolling;
209 break;
211 case JackTransportRolling:
212 if (cmd == TransportCommandStop) {
213 jack_log("transport rolling ==> stopped");
214 fTransportState = JackTransportStopped;
215 MakeAllStopping(table);
216 } else if (fPendingPos) {
217 jack_log("transport rolling ==> starting");
218 fTransportState = JackTransportStarting;
219 MakeAllStartingLocating(table);
220 SyncTimeout(frame_rate, buffer_size);
222 break;
224 case JackTransportNetStarting:
225 break;
227 default:
228 jack_error("Invalid JACK transport state: %d", fTransportState);
231 /* Update timebase, if needed. */
232 if (fTransportState == JackTransportRolling) {
233 jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
234 pending->frame += buffer_size;
235 WriteNextStateStop(1);
238 /* See if an asynchronous position request arrived during the last cycle. */
239 jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
240 if (fPendingPos) {
241 jack_log("New pos = %ld", request->frame);
242 jack_position_t* pending = WriteNextStateStart(1);
243 CopyPosition(request, pending);
244 WriteNextStateStop(1);
248 // Client
249 void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
251 UInt16 next_index = GetCurrentIndex();
252 UInt16 cur_index;
253 do {
254 cur_index = next_index;
255 memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
256 next_index = GetCurrentIndex();
257 } while (cur_index != next_index); // Until a coherent state has been read
260 void JackTransportEngine::RequestNewPos(jack_position_t* pos)
262 jack_position_t* request = WriteNextStateStart(2);
263 pos->unique_1 = pos->unique_2 = GenerateUniqueID();
264 CopyPosition(pos, request);
265 jack_log("RequestNewPos pos = %ld", pos->frame);
266 WriteNextStateStop(2);
269 jack_transport_state_t JackTransportEngine::Query(jack_position_t* pos)
271 if (pos)
272 ReadCurrentPos(pos);
273 return GetState();
276 jack_nframes_t JackTransportEngine::GetCurrentFrame()
278 jack_position_t pos;
279 ReadCurrentPos(&pos);
281 if (fTransportState == JackTransportRolling) {
282 float usecs = GetMicroSeconds() - pos.usecs;
283 jack_nframes_t elapsed = (jack_nframes_t)floor((((float) pos.frame_rate) / 1000000.0f) * usecs);
284 return pos.frame + elapsed;
285 } else {
286 return pos.frame;
290 // RT, client
291 void JackTransportEngine::CopyPosition(jack_position_t* from, jack_position_t* to)
293 int tries = 0;
294 long timeout = 1000;
296 do {
297 /* throttle the busy wait if we don't get the answer
298 * very quickly. See comment above about this
299 * design.
301 if (tries > 10) {
302 JackSleep(20);
303 tries = 0;
305 /* debug code to avoid system hangs... */
306 if (--timeout == 0) {
307 jack_error("hung in loop copying position B");
308 abort();
311 *to = *from;
312 tries++;
314 } while (to->unique_1 != to->unique_2);
318 } // end of namespace