JackTransportEngine.cpp 10.7 KB
Newer Older
sletz's avatar
sletz committed
1
/*
sletz's avatar
sletz committed
2
Copyright (C) 2001 Paul Davis
sletz's avatar
sletz committed
3
Copyright (C) 2004-2008 Grame
sletz's avatar
sletz committed
4
5

This program is free software; you can redistribute it and/or modify
6
7
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
sletz's avatar
sletz committed
8
9
10
11
12
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
GNU Lesser General Public License for more details.
sletz's avatar
sletz committed
14

15
16
17
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software 
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
sletz's avatar
sletz committed
18
19
20
21

*/

#include "JackTransportEngine.h"
sletz's avatar
sletz committed
22
#include "JackClientInterface.h"
sletz's avatar
sletz committed
23
#include "JackClientControl.h"
sletz's avatar
sletz committed
24
25
#include "JackEngineControl.h"
#include "JackGlobals.h"
sletz's avatar
sletz committed
26
#include "JackError.h"
sletz's avatar
sletz committed
27
#include "JackTime.h"
sletz's avatar
sletz committed
28
#include <assert.h>
29
#include <math.h>
sletz's avatar
sletz committed
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdlib.h>

using namespace std;

namespace Jack
{

JackTransportEngine::JackTransportEngine(): JackAtomicArrayState<jack_position_t>()
{
    fTransportState = JackTransportStopped;
    fTransportCmd = fPreviousCmd = TransportCommandStop;
    fSyncTimeout = 2000000;	/* 2 second default */
    fSyncTimeLeft = 0;
    fTimeBaseMaster = -1;
    fWriteCounter = 0;
45
    fConditionnal = false;
sletz's avatar
sletz committed
46
    fPendingPos = false;
47
    fNetworkSync = false;
sletz's avatar
sletz committed
48
49
50
51
52
}

// compute the number of cycle for timeout
void JackTransportEngine::SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size)
{
sletz's avatar
sletz committed
53
    long buf_usecs = (long)((buffer_size * (jack_time_t)1000000) / frame_rate);
sletz's avatar
sletz committed
54
    fSyncTimeLeft = fSyncTimeout / buf_usecs;
sletz's avatar
sletz committed
55
    jack_log("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld", (long)fSyncTimeout, (long)fSyncTimeLeft);
sletz's avatar
sletz committed
56
57
}

sletz's avatar
sletz committed
58
// Server
sletz's avatar
sletz committed
59
60
61
62
63
64
65
66
67
68
69
70
71
int JackTransportEngine::ResetTimebase(int refnum)
{
    if (fTimeBaseMaster == refnum) {
        jack_position_t* request = WriteNextStateStart(2); // To check
        request->valid = (jack_position_bits_t)0;
        WriteNextStateStop(2);
        fTimeBaseMaster = -1;
        return 0;
    } else {
        return EINVAL;
    }
}

sletz's avatar
sletz committed
72
// Server
73
int JackTransportEngine::SetTimebaseMaster(int refnum, bool conditionnal)
sletz's avatar
sletz committed
74
75
76
{
    if (conditionnal && fTimeBaseMaster > 0) {
        if (refnum != fTimeBaseMaster) {
sletz's avatar
sletz committed
77
            jack_log("conditional timebase for ref = %ld failed: %ld is already the master", refnum, fTimeBaseMaster);
sletz's avatar
sletz committed
78
79
            return EBUSY;
        } else {
sletz's avatar
sletz committed
80
            jack_log("ref = %ld was already timebase master", refnum);
sletz's avatar
sletz committed
81
82
83
84
            return 0;
        }
    } else {
        fTimeBaseMaster = refnum;
85
        fConditionnal = conditionnal;
sletz's avatar
sletz committed
86
        jack_log("new timebase master: ref = %ld", refnum);
sletz's avatar
sletz committed
87
88
89
90
        return 0;
    }
}

sletz's avatar
sletz committed
91
92
// RT
bool JackTransportEngine::CheckAllRolling(JackClientInterface** table)
sletz's avatar
sletz committed
93
{
sletz's avatar
sletz committed
94
    for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
95
        JackClientInterface* client = table[i];
sletz's avatar
sletz committed
96
97
98
        if (client && client->GetClientControl()->fTransportState != JackTransportRolling) {
            jack_log("CheckAllRolling ref = %ld is not rolling", i);
            return false;
sletz's avatar
sletz committed
99
100
        }
    }
sletz's avatar
sletz committed
101
102
    jack_log("CheckAllRolling");
    return true;
sletz's avatar
sletz committed
103
104
}

sletz's avatar
sletz committed
105
106
// RT
void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table)
sletz's avatar
sletz committed
107
{
sletz's avatar
sletz committed
108
    for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
109
        JackClientInterface* client = table[i];
sletz's avatar
sletz committed
110
        if (client) {
111
112
113
114
115
            JackClientControl* control = client->GetClientControl();
            // Inactive clients don't have their process function called at all, so they must appear as already "rolling" for the transport....
            control->fTransportState = (control->fActive && control->fCallback[kRealTimeCallback]) ? JackTransportStarting : JackTransportRolling;
            control->fTransportSync = true; 
            control->fTransportTimebase = true; 
sletz's avatar
sletz committed
116
            jack_log("MakeAllStartingLocating ref = %ld", i);
sletz's avatar
sletz committed
117
118
119
120
        }
    }
}

sletz's avatar
sletz committed
121
122
// RT
void JackTransportEngine::MakeAllStopping(JackClientInterface** table)
sletz's avatar
sletz committed
123
{
sletz's avatar
sletz committed
124
    for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
125
126
        JackClientInterface* client = table[i];
        if (client) {
127
128
129
130
            JackClientControl* control = client->GetClientControl();
            control->fTransportState = JackTransportStopped;
            control->fTransportSync = false; 
            control->fTransportTimebase = false; 
sletz's avatar
sletz committed
131
            jack_log("MakeAllStopping ref = %ld", i);
sletz's avatar
sletz committed
132
133
134
135
        }
    }
}

sletz's avatar
sletz committed
136
137
138
// RT
void JackTransportEngine::MakeAllLocating(JackClientInterface** table)
{
sletz's avatar
sletz committed
139
    for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
sletz's avatar
sletz committed
140
141
        JackClientInterface* client = table[i];
        if (client) {
142
143
            JackClientControl* control = client->GetClientControl();
            control->fTransportState = JackTransportStopped;
sletz's avatar
sletz committed
144
            control->fTransportSync = true; 
145
            control->fTransportTimebase = true; 
sletz's avatar
sletz committed
146
147
148
149
150
            jack_log("MakeAllLocating ref = %ld", i);
        }
    }
}

sletz's avatar
sletz committed
151
// RT
sletz's avatar
Cleanup    
sletz committed
152
void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time)
sletz's avatar
sletz committed
153
154
155
156
157
158
159
{
    jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
    pending->usecs = time;
    pending->frame_rate = frame_rate;
    WriteNextStateStop(1);
}

sletz's avatar
sletz committed
160
// RT
sletz's avatar
sletz committed
161
162
163
164
165
166
167
168
void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
{
    TrySwitchState(1);	// Switch from "pending" to "current", it always works since there is always a pending state

    /* Handle any new transport command from the last cycle. */
    transport_command_t cmd = fTransportCmd;
    if (cmd != fPreviousCmd) {
        fPreviousCmd = cmd;
169
        jack_log("transport command: %s", (cmd == TransportCommandStart ? "Transport start" : "Transport stop"));
sletz's avatar
sletz committed
170
171
172
173
174
175
176
177
    } else {
        cmd = TransportCommandNone;
    }

    /* state transition switch */
    switch (fTransportState) {

        case JackTransportStopped:
sletz's avatar
sletz committed
178
            // Set a JackTransportStarting for the current cycle, if all clients are ready (no slow_sync) ==> JackTransportRolling next state
sletz's avatar
sletz committed
179
            if (cmd == TransportCommandStart) {
sletz's avatar
sletz committed
180
                jack_log("transport stopped ==> starting frame = %d", ReadCurrentState()->frame);
sletz's avatar
sletz committed
181
                fTransportState = JackTransportStarting;
sletz's avatar
sletz committed
182
                MakeAllStartingLocating(table);
sletz's avatar
sletz committed
183
                SyncTimeout(frame_rate, buffer_size);
sletz's avatar
sletz committed
184
            } else if (fPendingPos) {
sletz's avatar
sletz committed
185
                jack_log("transport stopped ==> stopped (locating) frame = %d", ReadCurrentState()->frame);
sletz's avatar
sletz committed
186
187
                MakeAllLocating(table);
            }
sletz's avatar
sletz committed
188
189
190
191
            break;

        case JackTransportStarting:
            if (cmd == TransportCommandStop) {
sletz's avatar
sletz committed
192
                jack_log("transport starting ==> stopped frame = %d", ReadCurrentState()->frame);
sletz's avatar
sletz committed
193
194
                fTransportState = JackTransportStopped;
                MakeAllStopping(table);
sletz's avatar
sletz committed
195
            } else if (fPendingPos) {
sletz's avatar
sletz committed
196
                jack_log("transport starting ==> starting frame = %d"), ReadCurrentState()->frame;
sletz's avatar
sletz committed
197
                fTransportState = JackTransportStarting;
sletz's avatar
sletz committed
198
                MakeAllStartingLocating(table);
sletz's avatar
sletz committed
199
                SyncTimeout(frame_rate, buffer_size);
sletz's avatar
sletz committed
200
            } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) {  // Slow clients may still catch up
201
                if (fNetworkSync) {
sletz's avatar
sletz committed
202
                    jack_log("transport starting ==> netstarting frame = %d");
203
204
205
206
207
                    fTransportState = JackTransportNetStarting;
                } else {
                    jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft);
                    fTransportState = JackTransportRolling;
                }
sletz's avatar
sletz committed
208
209
210
211
212
            }
            break;

        case JackTransportRolling:
            if (cmd == TransportCommandStop) {
sletz's avatar
sletz committed
213
                jack_log("transport rolling ==> stopped");
sletz's avatar
sletz committed
214
215
216
217
                fTransportState = JackTransportStopped;
                MakeAllStopping(table);
            } else if (fPendingPos) {
                jack_log("transport rolling ==> starting");
sletz's avatar
sletz committed
218
                fTransportState = JackTransportStarting;
sletz's avatar
sletz committed
219
                MakeAllStartingLocating(table);
sletz's avatar
sletz committed
220
221
222
223
                SyncTimeout(frame_rate, buffer_size);
            }
            break;

224
225
226
        case JackTransportNetStarting:
            break;

sletz's avatar
sletz committed
227
228
229
230
231
232
233
234
235
236
237
238
239
240
        default:
            jack_error("Invalid JACK transport state: %d", fTransportState);
    }

    /* Update timebase, if needed. */
    if (fTransportState == JackTransportRolling) {
        jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
        pending->frame += buffer_size;
        WriteNextStateStop(1);
    }

    /* See if an asynchronous position request arrived during the last cycle. */
    jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
    if (fPendingPos) {
sletz's avatar
sletz committed
241
        jack_log("New pos = %ld", request->frame);
sletz's avatar
sletz committed
242
        jack_position_t* pending = WriteNextStateStart(1);
sletz's avatar
sletz committed
243
        CopyPosition(request, pending);
sletz's avatar
sletz committed
244
245
246
247
        WriteNextStateStop(1);
    }
}

sletz's avatar
sletz committed
248
// Client
sletz's avatar
sletz committed
249
250
251
252
253
254
255
256
257
258
259
void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
{
    UInt16 next_index = GetCurrentIndex();
    UInt16 cur_index;
    do {
        cur_index = next_index;
        memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
        next_index = GetCurrentIndex();
    } while (cur_index != next_index); // Until a coherent state has been read
}

260
261
262
263
void JackTransportEngine::RequestNewPos(jack_position_t* pos)
{
    jack_position_t* request = WriteNextStateStart(2);
    pos->unique_1 = pos->unique_2 = GenerateUniqueID();
sletz's avatar
sletz committed
264
    CopyPosition(pos, request);
265
266
267
268
269
270
271
272
273
274
275
    jack_log("RequestNewPos pos = %ld", pos->frame);
    WriteNextStateStop(2);
}

jack_transport_state_t JackTransportEngine::Query(jack_position_t* pos)
{
    if (pos)
        ReadCurrentPos(pos);
    return GetState();
}

276
277
278
279
280
281
282
283
284
285
286
287
288
289
jack_nframes_t JackTransportEngine::GetCurrentFrame()
{
    jack_position_t pos;
    ReadCurrentPos(&pos);

    if (fTransportState == JackTransportRolling) {
        float usecs = GetMicroSeconds() - pos.usecs;
        jack_nframes_t elapsed = (jack_nframes_t)floor((((float) pos.frame_rate) / 1000000.0f) * usecs);
        return pos.frame + elapsed;
    } else {
        return pos.frame;
    }
}

sletz's avatar
sletz committed
290
// RT, client
sletz's avatar
sletz committed
291
void JackTransportEngine::CopyPosition(jack_position_t* from, jack_position_t* to)
sletz's avatar
sletz committed
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
{
    int tries = 0;
    long timeout = 1000;

    do {
        /* throttle the busy wait if we don't get the answer
         * very quickly. See comment above about this
         * design.
         */
        if (tries > 10) {
            JackSleep(20);
            tries = 0;

            /* debug code to avoid system hangs... */
            if (--timeout == 0) {
                jack_error("hung in loop copying position B");
                abort();
            }
        }
        *to = *from;
        tries++;

    } while (to->unique_1 != to->unique_2);
}


} // end of namespace