summaryrefslogtreecommitdiff
path: root/doc/transport.dox
blob: 68fe8b664bb2bc51267f91a679753c3f0b9cb7bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/*
 * This file documents the JACK transport design.  It is part of the
 * JACK reference manual, built using doxygen.
 */

/**

@page transport-design JACK Transport Design

The @ref index provides simple transport interfaces for starting,
stopping and repositioning a set of clients.  This document describes
the overall design of these interfaces, their detailed specifications
are in <jack/transport.h>

  - @ref requirements
  - @ref overview
  - @ref timebase
  - @ref transportcontrol
  - @ref transportclients
  - @ref compatibility
  - @ref issues


@section requirements Requirements

  - We need sample-level accuracy for transport control.  This implies
  that the transport client logic has to be part of the realtime
  process chain.

  - We don't want to add another context switch.  So, the transport
  client logic has to run in the context of the client's process
  thread.  To avoid making an extra pass through the process graph, no
  transport changes take effect until the following process cycle.
  That way, the transport info is stable throughout each cycle.

  - We want to allow multiple clients to change the transport state.
  This is mostly a usability issue.  Any client can start or stop
  playback, or seek to a new location.  The user need not switch
  windows to accomplish these tasks.

  - We want a way for clients with heavyweight state to sync up when
  the user presses "play", before the transport starts rolling.

  - We want to provide for ongoing binary compatibility as the
  transport design evolves.


@section overview Overview

The former transport master role has been divided into two layers:

  - @ref timebase - counting beats, frames, etc. on every cycle.
  - @ref transportcontrol - start, stop and reposition the playback.  

Existing transport clients continue to work in compatibility mode.
But, old-style timebase masters will no longer control the transport.


@section timebase Timebase Master

The timebase master continuously updates extended position
information, counting beats, timecode, etc.  Without this extended
information, there is no need for this function.  There is at most one
master active at a time.  If no client is registered as timebase
master, frame numbers will be the only position information available.

The timebase master registers a callback that updates position
information while the transport is rolling.  Its output affects the
following process cycle.  This function is called immediately after
the process callback in the same thread whenever the transport is
rolling, or when any client has set a new position in the previous
cycle.  The first cycle after jack_set_timebase_callback() is also
treated as a new position, or the first cycle after jack_activate() if
the client had been inactive.

@code
  typedef int  (*JackTimebaseCallback)(jack_transport_state_t state,
                                       jack_nframes_t nframes,
                                       jack_position_t *pos,
                                       int new_pos,
                                       void *arg);
@endcode

When a new client takes over, the former timebase callback is no
longer called.  Taking over the timebase may be done conditionally, in
which case the takeover fails when there is a master already.  The
existing master can release it voluntarily, if desired.

@code
  int  jack_set_timebase_callback (jack_client_t *client,
                                   int conditional,
                                   JackTimebaseCallback timebase_callback,
                                   void *arg);

  int  jack_release_timebase(jack_client_t *client);
@endcode

If the timebase master releases the timebase or exits the JACK graph
for any reason, the JACK engine takes over at the start of the next
process cycle.  The transport state does not change.  If rolling, it
continues to play, with frame numbers as the only available position
information.


@section transportcontrol Transport Control

The JACK engine itself manages stopping and starting of the transport.
Any client can make transport control requests at any time.  These
requests take effect no sooner than the next process cycle, sometimes
later.  The transport state is always valid, initially it is
::JackTransportStopped.

@code
  void jack_transport_start (jack_client_t *client);
  void jack_transport_stop (jack_client_t *client);
@endcode

@subsection slowsyncclients Slow-sync clients

The engine handles polling of slow-sync clients.  When someone calls
jack_transport_start(), the engine resets the poll bits and changes to
a new state, ::JackTransportStarting.  The @a sync_callback function
for each slow-sync client will be invoked in the JACK process thread
while the transport is starting.  If it has not already done so, the
client needs to initiate a seek to reach the starting position.  The
@a sync_callback returns false until the seek completes and the client
is ready to play.  When all slow-sync clients are ready, the state
changes to ::JackTransportRolling.

@code
  typedef int  (*JackSyncCallback)(jack_transport_state_t state,
                                   jack_position_t *pos, void *arg);
@endcode

This callback is a realtime function that runs in the JACK process
thread.

@code
  int  jack_set_sync_callback (jack_client_t *client,
                               JackSyncCallback sync_callback, void *arg);
@endcode

Clients that don't declare a @a sync_callback are assumed to be ready
immediately, any time the transport wants to start.  If a client no
longer requires slow-sync processing, it can set its @a sync_callback
to NULL.

@code
  int  jack_set_sync_timeout (jack_client_t *client,
                              jack_time_t usecs);
@endcode

There must be a @a timeout to prevent unresponsive slow-sync clients
from completely halting the transport mechanism.  Two seconds is the
default.  When this @a timeout expires, the transport will start
rolling, even if some slow-sync clients are still unready.  The @a
sync_callback for these clients continues being invoked, giving them
an opportunity to catch up.

@subsection repositioning Repositioning

@code
  int  jack_transport_reposition (jack_client_t *client,
                                  jack_position_t *pos);
  int  jack_transport_locate (jack_client_t *client,
                              jack_nframes_t frame);
@endcode

These request a new transport position.  They can be called at any
time by any client.  Even the timebase master must use them.  If the
request is valid, it goes into effect in two process cycles.  If there
are slow-sync clients and the transport is already rolling, it will
enter the ::JackTransportStarting state and begin invoking their @a
sync_callbacks until ready.

@subsection transportstatetransitiondiagram Transport State Transition Diagram

@image html	fsm.png "Transport State Transition Diagram"
@image latex    fsm.eps "Transport State Transition Diagram"


@section transportclients Transport Clients

Transport clients were formerly known as "transport slaves".  We want
to make it easy for almost every JACK client to be a transport client.

@code
   jack_transport_state_t jack_transport_query (jack_client_t *client,
		                                jack_position_t *pos);
@endcode

This function can be called from any thread.  If called from the
process thread, @a pos corresponds to the first frame of the current
cycle and the state returned is valid for the entire cycle.


@section compatibility Compatibility

During the transition period we will support the old-style interfaces
in compatibility mode as deprecated interfaces.  This compatibility is
not 100%, there are limitations.  

The main reasons for doing this are:

  - facilitate testing with clients that already have transport
  support
  - provide a clean migration path, so application developers are
  not discouraged from supporting the transport interface

These deprecated interfaces continue to work:

@code
  typedef struct jack_transport_info_t;

  void jack_get_transport_info (jack_client_t *client,
                                jack_transport_info_t *tinfo);
@endcode

Unfortunately, the old-style timebase master interface cannot coexist
cleanly with such new features as jack_transport_locate() and
slow-sync clients.  So, these interfaces are only present as stubs:

@code
  void jack_set_transport_info (jack_client_t *client,
                                jack_transport_info_t *tinfo);
  int  jack_engine_takeover_timebase (jack_client_t *);
@endcode

For compatibility with future changes, it would be good to avoid
structures entirely.  Nevertheless, the jack_position_t structure
provides a convenient way to collect timebase information in several
formats that clearly all refer to a single moment.  To minimize future
binary compatibility problems, this structure has some padding at the
end, making it possible to extend it without necessarily breaking
compatibility.  New fields can be allocated from the padding area,
with access controlled by newly defined valid bits, all of which are
currently forced to zero.  That allows the structure size and offsets
to remain constant.


@section issues Issues Not Addressed

This design currently does not address several issues. This means they
will probably not be included in JACK release 1.0.

  - variable speed
  - reverse play
  - looping
*/