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"
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 */
48 fConditionnal
= 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
);
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);
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
);
83 jack_log("ref = %ld was already timebase master", refnum
);
87 fTimeBaseMaster
= refnum
;
88 fConditionnal
= conditionnal
;
89 jack_log("new timebase master: ref = %ld", refnum
);
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
);
104 jack_log("CheckAllRolling");
109 void JackTransportEngine::MakeAllStartingLocating(JackClientInterface
** table
)
111 for (int i
= GetEngineControl()->fDriverNum
; i
< CLIENT_NUM
; i
++) {
112 JackClientInterface
* client
= table
[i
];
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
);
125 void JackTransportEngine::MakeAllStopping(JackClientInterface
** table
)
127 for (int i
= GetEngineControl()->fDriverNum
; i
< CLIENT_NUM
; i
++) {
128 JackClientInterface
* client
= table
[i
];
130 JackClientControl
* control
= client
->GetClientControl();
131 control
->fTransportState
= JackTransportStopped
;
132 control
->fTransportSync
= false;
133 control
->fTransportTimebase
= false;
134 jack_log("MakeAllStopping ref = %ld", i
);
140 void JackTransportEngine::MakeAllLocating(JackClientInterface
** table
)
142 for (int i
= GetEngineControl()->fDriverNum
; i
< CLIENT_NUM
; i
++) {
143 JackClientInterface
* client
= table
[i
];
145 JackClientControl
* control
= client
->GetClientControl();
146 control
->fTransportState
= JackTransportStopped
;
147 control
->fTransportSync
= true;
148 control
->fTransportTimebase
= true;
149 jack_log("MakeAllLocating ref = %ld", i
);
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);
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
) {
172 jack_log("transport command: %s", (cmd
== TransportCommandStart
? "Transport start" : "Transport stop"));
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
);
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
205 jack_log("transport starting ==> netstarting frame = %d");
206 fTransportState
= JackTransportNetStarting
;
208 jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft
);
209 fTransportState
= JackTransportRolling
;
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
);
227 case JackTransportNetStarting
:
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
);
244 jack_log("New pos = %ld", request
->frame
);
245 jack_position_t
* pending
= WriteNextStateStart(1);
246 CopyPosition(request
, pending
);
247 WriteNextStateStop(1);
252 void JackTransportEngine::ReadCurrentPos(jack_position_t
* pos
)
254 UInt16 next_index
= GetCurrentIndex();
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
)
279 jack_nframes_t
JackTransportEngine::GetCurrentFrame()
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
;
294 void JackTransportEngine::CopyPosition(jack_position_t
* from
, jack_position_t
* to
)
300 /* throttle the busy wait if we don't get the answer
301 * very quickly. See comment above about this
308 /* debug code to avoid system hangs... */
309 if (--timeout
== 0) {
310 jack_error("hung in loop copying position B");
317 } while (to
->unique_1
!= to
->unique_2
);
321 } // end of namespace