** Warning: Cannot open xref database.
1 /*
2 JACK transport engine -- runs in the server process.
3
4 Copyright (C) 2001-2003 Paul Davis
5 Copyright (C) 2003 Jack O'Quin
6
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.
11
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.
16
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.
20 */
21
22 #include <config.h>
23 #include <errno.h>
24 #include <assert.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <jack/internal.h>
28 #include <jack/engine.h>
29 #include "transengine.h"
30
31
32 /********************** internal functions **********************/
33
34 /* initiate polling a new slow-sync client
35 *
36 * precondition: caller holds the graph lock. */
37 static inline void
38 jack_sync_poll_new (jack_engine_t *engine, jack_client_internal_t *client)
39 {
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++;
46 }
47
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\n");
52 }
53
54 VERBOSE (engine, "polling sync client %" PRIu32 "\n",
55 client->control->id);
56 }
57
58 /* stop polling a specific slow-sync client
59 *
60 * precondition: caller holds the graph lock. */
61 static inline void
62 jack_sync_poll_deactivate (jack_engine_t *engine,
63 jack_client_internal_t *client)
64 {
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 "\n", client->control->id);
71 }
72 client->control->active_slowsync = 0;
73 engine->control->sync_clients--;
74 assert(engine->control->sync_clients >= 0);
75 }
76
77 /* stop polling all the slow-sync clients
78 *
79 * precondition: caller holds the graph lock. */
80 static void
81 jack_sync_poll_stop (jack_engine_t *engine)
82 {
83 JSList *node;
84 long poll_count = 0; /* count sync_poll clients */
85
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;
92 poll_count++;
93 }
94 }
95
96 //JOQ: check invariant for debugging...
97 assert (poll_count == engine->control->sync_remain);
98 VERBOSE (engine,
99 "sync poll halted with %" PRIu32
100 " clients and %8.6f secs remaining\n",
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;
105 }
106
107 /* start polling all the slow-sync clients
108 *
109 * precondition: caller holds the graph lock. */
110 static void
111 jack_sync_poll_start (jack_engine_t *engine)
112 {
113 JSList *node;
114 long sync_count = 0; /* count slow-sync clients */
115
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;
121 sync_count++;
122 }
123 }
124
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\n", engine->control->sync_remain,
131 (double) (engine->control->sync_time_left / 1000000.0));
132 }
133
134 /* check for sync timeout */
135 static inline int
136 jack_sync_timeout (jack_engine_t *engine)
137 {
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);
142
143 /* compare carefully, jack_time_t is unsigned */
144 if (ectl->sync_time_left > buf_usecs) {
145 ectl->sync_time_left -= buf_usecs;
146 return FALSE;
147 }
148
149 /* timed out */
150 VERBOSE (engine, "transport sync timeout\n");
151 ectl->sync_time_left = 0;
152 return TRUE;
153 }
154
155
156 /**************** subroutines used by engine.c ****************/
157
158 /* driver callback */
159 int
160 jack_set_sample_rate (jack_engine_t *engine, jack_nframes_t nframes)
161 {
162 jack_control_t *ectl = engine->control;
163
164 ectl->current_time.frame_rate = nframes;
165 ectl->pending_time.frame_rate = nframes;
166 return 0;
167 }
168
169 /* on ResetTimeBaseClient request */
170 int
171 jack_timebase_reset (jack_engine_t *engine, jack_client_id_t client_id)
172 {
173 int ret;
174 struct _jack_client_internal *client;
175 jack_control_t *ectl = engine->control;
176
177 jack_lock_graph (engine);
178
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\n",
186 client->control->name);
187 ret = 0;
188 } else
189 ret = EINVAL;
190
191 jack_unlock_graph (engine);
192
193 return ret;
194 }
195
196 /* on SetTimeBaseClient request */
197 int
198 jack_timebase_set (jack_engine_t *engine,
199 jack_client_id_t client_id, int conditional)
200 {
201 int ret = 0;
202 struct _jack_client_internal *client;
203
204 jack_lock_graph (engine);
205
206 client = jack_client_internal_by_id (engine, client_id);
207
208 if (client == NULL) {
209 VERBOSE (engine, " %" PRIu32 " no longer exists\n", client_id);
210 jack_unlock_graph (engine);
211 return EINVAL;
212 }
213
214 if (conditional && engine->timebase_client) {
215
216 /* see if timebase master is someone else */
217 if (client != engine->timebase_client) {
218 VERBOSE (engine, "conditional timebase for %s failed\n"
219 " %s is already the master\n",
220 client->control->name,
221 engine->timebase_client->control->name);
222 ret = EBUSY;
223 } else
224 VERBOSE (engine, " %s was already timebase master:\n",
225 client->control->name);
226
227 } else {
228
229 if (engine->timebase_client) {
230 engine->timebase_client->control->is_timebase = 0;
231 engine->timebase_client->control->timebase_new = 0;
232 }
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\n",
238 client->control->name);
239 }
240
241 jack_unlock_graph (engine);
242
243 return ret;
244 }
245
246 /* for client activation
247 *
248 * precondition: caller holds the graph lock. */
249 void
250 jack_transport_activate (jack_engine_t *engine, jack_client_internal_t *client)
251 {
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);
257 }
258
259 if (client->control->is_timebase) {
260 client->control->timebase_new = 1;
261 }
262 }
263
264 /* for engine initialization */
265 void
266 jack_transport_init (jack_engine_t *engine)
267 {
268 jack_control_t *ectl = engine->control;
269
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 */
279 ectl->new_pos = 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;
286 }
287
288 /* when any client exits the graph (either dead or not active)
289 *
290 * precondition: caller holds the graph lock */
291 void
292 jack_transport_client_exit (jack_engine_t *engine,
293 jack_client_internal_t *client)
294 {
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\n");
301 }
302 engine->control->current_time.valid = 0;
303 engine->control->pending_time.valid = 0;
304 }
305
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;
311 }
312 }
313
314 /* when a new client is being created */
315 void
316 jack_transport_client_new (jack_client_internal_t *client)
317 {
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;
328 }
329
330 /* on ResetSyncClient request */
331 int
332 jack_transport_client_reset_sync (jack_engine_t *engine,
333 jack_client_id_t client_id)
334 {
335 int ret;
336 jack_client_internal_t *client;
337
338 jack_lock_graph (engine);
339
340 client = jack_client_internal_by_id (engine, client_id);
341
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;
346 ret = 0;
347 } else
348 ret = EINVAL;
349
350 jack_unlock_graph (engine);
351
352 return ret;
353 }
354
355 /* on SetSyncClient request */
356 int
357 jack_transport_client_set_sync (jack_engine_t *engine,
358 jack_client_id_t client_id)
359 {
360 int ret;
361 jack_client_internal_t *client;
362
363 /* The process cycle runs with this lock. */
364 jack_lock_graph (engine);
365
366 client = jack_client_internal_by_id (engine, client_id);
367
368 if (client) {
369 if (!client->control->is_slowsync) {
370 client->control->is_slowsync = 1;
371 if (client->control->active) {
372 client->control->active_slowsync = 1;
373 engine->control->sync_clients++;
374 }
375 }
376
377 /* force poll of the new slow-sync client, if active */
378 if (client->control->active_slowsync)
379 jack_sync_poll_new (engine, client);
380 ret = 0;
381 } else
382 ret = EINVAL;
383
384 jack_unlock_graph (engine);
385
386 return ret;
387 }
388
389 /* at process cycle end, set transport parameters for the next cycle
390 *
391 * precondition: caller holds the graph lock.
392 */
393 void
394 jack_transport_cycle_end (jack_engine_t *engine)
395 {
396 jack_control_t *ectl = engine->control;
397 transport_command_t cmd; /* latest transport command */
398
399 /* Promote pending_time to current_time. Maintain the usecs,
400 * frame_rate and frame values, clients may not set them. */
401 ectl->pending_time.usecs = ectl->current_time.usecs;
402 ectl->pending_time.frame_rate = ectl->current_time.frame_rate;
403 ectl->pending_time.frame = ectl->pending_frame;
404 ectl->current_time = ectl->pending_time;
405 ectl->new_pos = ectl->pending_pos;
406
407 /* check sync results from previous cycle */
408 if (ectl->transport_state == JackTransportStarting) {
409 if ((ectl->sync_remain == 0) ||
410 (jack_sync_timeout(engine))) {
411 ectl->transport_state = JackTransportRolling;
412 VERBOSE (engine, "transport Rolling, %8.6f sec"
413 " left for poll\n",
414 (double) (ectl->sync_time_left / 1000000.0));
415 }
416 }
417
418 /* Handle any new transport command from the last cycle. */
419 cmd = ectl->transport_cmd;
420 if (cmd != ectl->previous_cmd) {
421 ectl->previous_cmd = cmd;
422 VERBOSE (engine, "transport command: %s\n",
423 (cmd == TransportCommandStart? "START": "STOP"));
424 } else
425 cmd = TransportCommandNone;
426
427 /* state transition switch */
428 switch (ectl->transport_state) {
429
430 case JackTransportStopped:
431 if (cmd == TransportCommandStart) {
432 if (ectl->sync_clients) {
433 ectl->transport_state = JackTransportStarting;
434 jack_sync_poll_start(engine);
435 } else {
436 ectl->transport_state = JackTransportRolling;
437 VERBOSE (engine, "transport Rolling\n");
438 }
439 }
440 break;
441
442 case JackTransportStarting:
443 if (cmd == TransportCommandStop) {
444 ectl->transport_state = JackTransportStopped;
445 VERBOSE (engine, "transport Stopped\n");
446 if (ectl->sync_remain)
447 jack_sync_poll_stop(engine);
448 } else if (ectl->new_pos) {
449 if (ectl->sync_clients) {
450 ectl->transport_state = JackTransportStarting;
451 jack_sync_poll_start(engine);
452 } else {
453 ectl->transport_state = JackTransportRolling;
454 VERBOSE (engine, "transport Rolling\n");
455 }
456 }
457 break;
458
459 case JackTransportRolling:
460 if (cmd == TransportCommandStop) {
461 ectl->transport_state = JackTransportStopped;
462 VERBOSE (engine, "transport Stopped\n");
463 if (ectl->sync_remain)
464 jack_sync_poll_stop(engine);
465 } else if (ectl->new_pos) {
466 if (ectl->sync_clients) {
467 ectl->transport_state = JackTransportStarting;
468 jack_sync_poll_start(engine);
469 }
470 }
471 break;
472
473 default:
474 jack_error ("invalid JACK transport state: %d",
475 ectl->transport_state);
476 }
477
478 /* update timebase, if needed */
479 if (ectl->transport_state == JackTransportRolling) {
480 ectl->pending_time.frame =
481 ectl->current_time.frame + ectl->buffer_size;
482 }
483
484 /* See if an asynchronous position request arrived during the
485 * last cycle. The request_time could change during the
486 * guarded copy. If so, we use the newest request. */
487 ectl->pending_pos = 0;
488 if (ectl->request_time.unique_1 != ectl->prev_request) {
489 jack_transport_copy_position(&ectl->request_time,
490 &ectl->pending_time);
491 VERBOSE (engine, "new transport position: %" PRIu32
492 ", id=0x%" PRIx64 "\n", ectl->pending_time.frame,
493 ectl->pending_time.unique_1);
494 ectl->prev_request = ectl->pending_time.unique_1;
495 ectl->pending_pos = 1;
496 }
497
498 /* clients can't set pending frame number, so save it here */
499 ectl->pending_frame = ectl->pending_time.frame;
500 }
501
502 /* driver callback at start of cycle */
503 void
504 jack_transport_cycle_start (jack_engine_t *engine, jack_time_t time)
505 {
506 engine->control->current_time.usecs = time;
507 }
508
509 /* on SetSyncTimeout request */
510 int
511 jack_transport_set_sync_timeout (jack_engine_t *engine,
512 jack_time_t usecs)
513 {
514 engine->control->sync_timeout = usecs;
515 VERBOSE (engine, "new sync timeout: %8.6f secs\n",
516 (double) (usecs / 1000000.0));
517 return 0;
518 }
519
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.