2 JACK transport engine -- runs in the server process.
4 Copyright (C) 2001-2003 Paul Davis
5 Copyright (C) 2003 Jack O'Quin
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <jack/internal.h>
28 #include <jack/engine.h>
29 #include <jack/messagebuffer.h>
30 #include "transengine.h"
32 /********************** internal functions **********************/
34 /* initiate polling a new slow-sync client
36 * precondition: caller holds the graph lock. */
38 jack_sync_poll_new (jack_engine_t
*engine
, jack_client_internal_t
*client
)
40 /* force sync_cb callback to run in its first cycle */
41 engine
->control
->sync_time_left
= engine
->control
->sync_timeout
;
42 client
->control
->sync_new
= 1;
43 if (!client
->control
->sync_poll
) {
44 client
->control
->sync_poll
= 1;
45 engine
->control
->sync_remain
++;
48 // JOQ: I don't like doing this here...
49 if (engine
->control
->transport_state
== JackTransportRolling
) {
50 engine
->control
->transport_state
= JackTransportStarting
;
51 VERBOSE (engine
, "force transport state to Starting");
54 VERBOSE (engine
, "polling sync client %" PRIu32
,
58 /* stop polling a specific slow-sync client
60 * precondition: caller holds the graph lock. */
62 jack_sync_poll_deactivate (jack_engine_t
*engine
,
63 jack_client_internal_t
*client
)
65 if (client
->control
->sync_poll
) {
66 client
->control
->sync_poll
= 0;
67 client
->control
->sync_new
= 0;
68 engine
->control
->sync_remain
--;
69 VERBOSE (engine
, "sync poll interrupted for client %"
70 PRIu32
, client
->control
->id
);
72 client
->control
->active_slowsync
= 0;
73 engine
->control
->sync_clients
--;
74 assert(engine
->control
->sync_clients
>= 0);
77 /* stop polling all the slow-sync clients
79 * precondition: caller holds the graph lock. */
81 jack_sync_poll_stop (jack_engine_t
*engine
)
84 long poll_count
= 0; /* count sync_poll clients */
86 for (node
= engine
->clients
; node
; node
= jack_slist_next (node
)) {
87 jack_client_internal_t
*client
=
88 (jack_client_internal_t
*) node
->data
;
89 if (client
->control
->active_slowsync
&&
90 client
->control
->sync_poll
) {
91 client
->control
->sync_poll
= 0;
96 //JOQ: check invariant for debugging...
97 assert (poll_count
== engine
->control
->sync_remain
);
99 "sync poll halted with %" PRIu32
100 " clients and %8.6f secs remaining",
101 engine
->control
->sync_remain
,
102 (double) (engine
->control
->sync_time_left
/ 1000000.0));
103 engine
->control
->sync_remain
= 0;
104 engine
->control
->sync_time_left
= 0;
107 /* start polling all the slow-sync clients
109 * precondition: caller holds the graph lock. */
111 jack_sync_poll_start (jack_engine_t
*engine
)
114 long sync_count
= 0; /* count slow-sync clients */
116 for (node
= engine
->clients
; node
; node
= jack_slist_next (node
)) {
117 jack_client_internal_t
*client
=
118 (jack_client_internal_t
*) node
->data
;
119 if (client
->control
->active_slowsync
) {
120 client
->control
->sync_poll
= 1;
125 //JOQ: check invariant for debugging...
126 assert (sync_count
== engine
->control
->sync_clients
);
127 engine
->control
->sync_remain
= sync_count
;
128 engine
->control
->sync_time_left
= engine
->control
->sync_timeout
;
129 VERBOSE (engine
, "transport Starting, sync poll of %" PRIu32
130 " clients for %8.6f secs", engine
->control
->sync_remain
,
131 (double) (engine
->control
->sync_time_left
/ 1000000.0));
134 /* check for sync timeout */
136 jack_sync_timeout (jack_engine_t
*engine
)
138 jack_control_t
*ectl
= engine
->control
;
139 jack_time_t buf_usecs
=
140 ((ectl
->buffer_size
* (jack_time_t
) 1000000) /
141 ectl
->current_time
.frame_rate
);
143 /* compare carefully, jack_time_t is unsigned */
144 if (ectl
->sync_time_left
> buf_usecs
) {
145 ectl
->sync_time_left
-= buf_usecs
;
150 VERBOSE (engine
, "transport sync timeout");
151 ectl
->sync_time_left
= 0;
156 /**************** subroutines used by engine.c ****************/
158 /* driver callback */
160 jack_set_sample_rate (jack_engine_t
*engine
, jack_nframes_t nframes
)
162 jack_control_t
*ectl
= engine
->control
;
164 ectl
->current_time
.frame_rate
= nframes
;
165 ectl
->pending_time
.frame_rate
= nframes
;
169 /* on ResetTimeBaseClient request */
171 jack_timebase_reset (jack_engine_t
*engine
, jack_client_id_t client_id
)
174 struct _jack_client_internal
*client
;
175 jack_control_t
*ectl
= engine
->control
;
177 jack_lock_graph (engine
);
179 client
= jack_client_internal_by_id (engine
, client_id
);
180 if (client
&& (client
== engine
->timebase_client
)) {
181 client
->control
->is_timebase
= 0;
182 client
->control
->timebase_new
= 0;
183 engine
->timebase_client
= NULL
;
184 ectl
->pending_time
.valid
= 0;
185 VERBOSE (engine
, "%s resigned as timebase master",
186 client
->control
->name
);
191 jack_unlock_graph (engine
);
196 /* on SetTimeBaseClient request */
198 jack_timebase_set (jack_engine_t
*engine
,
199 jack_client_id_t client_id
, int conditional
)
202 struct _jack_client_internal
*client
;
204 jack_lock_graph (engine
);
206 client
= jack_client_internal_by_id (engine
, client_id
);
208 if (client
== NULL
) {
209 VERBOSE (engine
, " %" PRIu32
" no longer exists", client_id
);
210 jack_unlock_graph (engine
);
214 if (conditional
&& engine
->timebase_client
) {
216 /* see if timebase master is someone else */
217 if (client
!= engine
->timebase_client
) {
218 VERBOSE (engine
, "conditional timebase for %s failed",
219 client
->control
->name
);
220 VERBOSE (engine
, " %s is already the master",
221 engine
->timebase_client
->control
->name
);
224 VERBOSE (engine
, " %s was already timebase master:",
225 client
->control
->name
);
229 if (engine
->timebase_client
) {
230 engine
->timebase_client
->control
->is_timebase
= 0;
231 engine
->timebase_client
->control
->timebase_new
= 0;
233 engine
->timebase_client
= client
;
234 client
->control
->is_timebase
= 1;
235 if (client
->control
->active
)
236 client
->control
->timebase_new
= 1;
237 VERBOSE (engine
, "new timebase master: %s",
238 client
->control
->name
);
241 jack_unlock_graph (engine
);
246 /* for client activation
248 * precondition: caller holds the graph lock. */
250 jack_transport_activate (jack_engine_t
*engine
, jack_client_internal_t
*client
)
252 if (client
->control
->is_slowsync
) {
253 assert(!client
->control
->active_slowsync
);
254 client
->control
->active_slowsync
= 1;
255 engine
->control
->sync_clients
++;
256 jack_sync_poll_new (engine
, client
);
259 if (client
->control
->is_timebase
) {
260 client
->control
->timebase_new
= 1;
264 /* for engine initialization */
266 jack_transport_init (jack_engine_t
*engine
)
268 jack_control_t
*ectl
= engine
->control
;
270 engine
->timebase_client
= NULL
;
271 ectl
->transport_state
= JackTransportStopped
;
272 ectl
->transport_cmd
= TransportCommandStop
;
273 ectl
->previous_cmd
= TransportCommandStop
;
274 memset (&ectl
->current_time
, 0, sizeof(ectl
->current_time
));
275 memset (&ectl
->pending_time
, 0, sizeof(ectl
->pending_time
));
276 memset (&ectl
->request_time
, 0, sizeof(ectl
->request_time
));
277 ectl
->prev_request
= 0;
278 ectl
->seq_number
= 1; /* can't start at 0 */
280 ectl
->pending_pos
= 0;
281 ectl
->pending_frame
= 0;
282 ectl
->sync_clients
= 0;
283 ectl
->sync_remain
= 0;
284 ectl
->sync_timeout
= 2000000; /* 2 second default */
285 ectl
->sync_time_left
= 0;
288 /* when any client exits the graph (either dead or not active)
290 * precondition: caller holds the graph lock */
292 jack_transport_client_exit (jack_engine_t
*engine
,
293 jack_client_internal_t
*client
)
295 if (client
== engine
->timebase_client
) {
296 if (client
->control
->dead
) {
297 engine
->timebase_client
->control
->is_timebase
= 0;
298 engine
->timebase_client
->control
->timebase_new
= 0;
299 engine
->timebase_client
= NULL
;
300 VERBOSE (engine
, "timebase master exit");
302 engine
->control
->current_time
.valid
= 0;
303 engine
->control
->pending_time
.valid
= 0;
306 if (client
->control
->is_slowsync
) {
307 if (client
->control
->active_slowsync
)
308 jack_sync_poll_deactivate (engine
, client
);
309 if (client
->control
->dead
)
310 client
->control
->is_slowsync
= 0;
314 /* when a new client is being created */
316 jack_transport_client_new (jack_client_internal_t
*client
)
318 client
->control
->is_timebase
= 0;
319 client
->control
->timebase_new
= 0;
320 client
->control
->is_slowsync
= 0;
321 client
->control
->active_slowsync
= 0;
322 client
->control
->sync_poll
= 0;
323 client
->control
->sync_new
= 0;
324 client
->control
->sync_cb
= NULL
;
325 client
->control
->sync_arg
= NULL
;
326 client
->control
->timebase_cb
= NULL
;
327 client
->control
->timebase_arg
= NULL
;
330 /* on ResetSyncClient request */
332 jack_transport_client_reset_sync (jack_engine_t
*engine
,
333 jack_client_id_t client_id
)
336 jack_client_internal_t
*client
;
338 jack_lock_graph (engine
);
340 client
= jack_client_internal_by_id (engine
, client_id
);
342 if (client
&& (client
->control
->is_slowsync
)) {
343 if (client
->control
->active_slowsync
)
344 jack_sync_poll_deactivate (engine
, client
);
345 client
->control
->is_slowsync
= 0;
350 jack_unlock_graph (engine
);
355 /* on SetSyncClient request */
357 jack_transport_client_set_sync (jack_engine_t
*engine
,
358 jack_client_id_t client_id
)
361 jack_client_internal_t
*client
;
363 DEBUG ("set sync client");
365 /* The process cycle runs with this lock. */
366 jack_lock_graph (engine
);
368 DEBUG ("got write lock");
370 client
= jack_client_internal_by_id (engine
, client_id
);
372 DEBUG ("client was %p");
375 if (!client
->control
->is_slowsync
) {
376 client
->control
->is_slowsync
= 1;
377 if (client
->control
->active
) {
378 client
->control
->active_slowsync
= 1;
379 engine
->control
->sync_clients
++;
383 /* force poll of the new slow-sync client, if active */
384 if (client
->control
->active_slowsync
) {
385 DEBUG ("sync poll new");
386 jack_sync_poll_new (engine
, client
);
392 DEBUG ("unlocking write lock for set_sync");
393 jack_unlock_graph (engine
);
399 /* at process cycle end, set transport parameters for the next cycle
401 * precondition: caller holds the graph lock.
404 jack_transport_cycle_end (jack_engine_t
*engine
)
406 jack_control_t
*ectl
= engine
->control
;
407 transport_command_t cmd
; /* latest transport command */
409 /* Promote pending_time to current_time. Maintain the usecs,
410 * frame_rate and frame values, clients may not set them. */
411 ectl
->pending_time
.usecs
= ectl
->current_time
.usecs
;
412 ectl
->pending_time
.frame_rate
= ectl
->current_time
.frame_rate
;
413 ectl
->pending_time
.frame
= ectl
->pending_frame
;
414 ectl
->current_time
= ectl
->pending_time
;
415 ectl
->new_pos
= ectl
->pending_pos
;
417 /* check sync results from previous cycle */
418 if (ectl
->transport_state
== JackTransportStarting
) {
419 if ((ectl
->sync_remain
== 0) ||
420 (jack_sync_timeout(engine
))) {
421 ectl
->transport_state
= JackTransportRolling
;
422 VERBOSE (engine
, "transport Rolling, %8.6f sec"
424 (double) (ectl
->sync_time_left
/ 1000000.0));
428 /* Handle any new transport command from the last cycle. */
429 cmd
= ectl
->transport_cmd
;
430 if (cmd
!= ectl
->previous_cmd
) {
431 ectl
->previous_cmd
= cmd
;
432 VERBOSE (engine
, "transport command: %s",
433 (cmd
== TransportCommandStart
? "START": "STOP"));
435 cmd
= TransportCommandNone
;
437 /* state transition switch */
439 switch (ectl
->transport_state
) {
441 case JackTransportStopped
:
442 if (cmd
== TransportCommandStart
) {
443 if (ectl
->sync_clients
) {
444 ectl
->transport_state
= JackTransportStarting
;
445 jack_sync_poll_start(engine
);
447 ectl
->transport_state
= JackTransportRolling
;
448 VERBOSE (engine
, "transport Rolling");
453 case JackTransportStarting
:
454 if (cmd
== TransportCommandStop
) {
455 ectl
->transport_state
= JackTransportStopped
;
456 VERBOSE (engine
, "transport Stopped");
457 if (ectl
->sync_remain
)
458 jack_sync_poll_stop(engine
);
459 } else if (ectl
->new_pos
) {
460 if (ectl
->sync_clients
) {
461 ectl
->transport_state
= JackTransportStarting
;
462 jack_sync_poll_start(engine
);
464 ectl
->transport_state
= JackTransportRolling
;
465 VERBOSE (engine
, "transport Rolling");
470 case JackTransportRolling
:
471 if (cmd
== TransportCommandStop
) {
472 ectl
->transport_state
= JackTransportStopped
;
473 VERBOSE (engine
, "transport Stopped");
474 if (ectl
->sync_remain
)
475 jack_sync_poll_stop(engine
);
476 } else if (ectl
->new_pos
) {
477 if (ectl
->sync_clients
) {
478 ectl
->transport_state
= JackTransportStarting
;
479 jack_sync_poll_start(engine
);
485 jack_error ("invalid JACK transport state: %d",
486 ectl
->transport_state
);
489 /* Update timebase, if needed. */
490 if (ectl
->transport_state
== JackTransportRolling
) {
491 ectl
->pending_time
.frame
=
492 ectl
->current_time
.frame
+ ectl
->buffer_size
;
495 /* See if an asynchronous position request arrived during the
496 * last cycle. The request_time could change during the
497 * guarded copy. If so, we use the newest request. */
498 ectl
->pending_pos
= 0;
499 if (ectl
->request_time
.unique_1
!= ectl
->prev_request
) {
500 jack_transport_copy_position(&ectl
->request_time
,
501 &ectl
->pending_time
);
502 VERBOSE (engine
, "new transport position: %" PRIu32
503 ", id=0x%" PRIx64
, ectl
->pending_time
.frame
,
504 ectl
->pending_time
.unique_1
);
505 ectl
->prev_request
= ectl
->pending_time
.unique_1
;
506 ectl
->pending_pos
= 1;
509 /* clients can't set pending frame number, so save it here */
510 ectl
->pending_frame
= ectl
->pending_time
.frame
;
513 /* driver callback at start of cycle */
515 jack_transport_cycle_start (jack_engine_t
*engine
, jack_time_t time
)
517 engine
->control
->current_time
.usecs
= time
;
520 /* on SetSyncTimeout request */
522 jack_transport_set_sync_timeout (jack_engine_t
*engine
,
525 engine
->control
->sync_timeout
= usecs
;
526 VERBOSE (engine
, "new sync timeout: %8.6f secs",
527 (double) (usecs
/ 1000000.0));