Jack2 1.9.6

JackTransportEngine.cpp

00001 /*
00002 Copyright (C) 2001 Paul Davis
00003 Copyright (C) 2004-2008 Grame
00004 
00005 This program is free software; you can redistribute it and/or modify
00006 it under the terms of the GNU Lesser General Public License as published by
00007 the Free Software Foundation; either version 2.1 of the License, or
00008 (at your option) any later version.
00009 
00010 This program is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 GNU Lesser General Public License for more details.
00014 
00015 You should have received a copy of the GNU Lesser General Public License
00016 along with this program; if not, write to the Free Software 
00017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00018 
00019 */
00020 
00021 #include "JackTransportEngine.h"
00022 #include "JackClientInterface.h"
00023 #include "JackClientControl.h"
00024 #include "JackEngineControl.h"
00025 #include "JackGlobals.h"
00026 #include "JackError.h"
00027 #include "JackTime.h"
00028 #include <assert.h>
00029 #include <math.h>
00030 #include <stdlib.h>
00031 
00032 using namespace std;
00033 
00034 namespace Jack
00035 {
00036 
00037 JackTransportEngine::JackTransportEngine(): JackAtomicArrayState<jack_position_t>()
00038 {
00039     fTransportState = JackTransportStopped;
00040     fTransportCmd = fPreviousCmd = TransportCommandStop;
00041     fSyncTimeout = 10000000;    /* 10 seconds default... 
00042                                    in case of big netjack1 roundtrip */
00043     fSyncTimeLeft = 0;
00044     fTimeBaseMaster = -1;
00045     fWriteCounter = 0;
00046     fConditionnal = false;
00047     fPendingPos = false;
00048     fNetworkSync = false;
00049 }
00050 
00051 // compute the number of cycle for timeout
00052 void JackTransportEngine::SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size)
00053 {
00054     long buf_usecs = (long)((buffer_size * (jack_time_t)1000000) / frame_rate);
00055     fSyncTimeLeft = fSyncTimeout / buf_usecs;
00056     jack_log("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld", (long)fSyncTimeout, (long)fSyncTimeLeft);
00057 }
00058 
00059 // Server
00060 int JackTransportEngine::ResetTimebase(int refnum)
00061 {
00062     if (fTimeBaseMaster == refnum) {
00063         jack_position_t* request = WriteNextStateStart(2); // To check
00064         request->valid = (jack_position_bits_t)0;
00065         WriteNextStateStop(2);
00066         fTimeBaseMaster = -1;
00067         return 0;
00068     } else {
00069         return EINVAL;
00070     }
00071 }
00072 
00073 // Server
00074 int JackTransportEngine::SetTimebaseMaster(int refnum, bool conditionnal)
00075 {
00076     if (conditionnal && fTimeBaseMaster > 0) {
00077         if (refnum != fTimeBaseMaster) {
00078             jack_log("conditional timebase for ref = %ld failed: %ld is already the master", refnum, fTimeBaseMaster);
00079             return EBUSY;
00080         } else {
00081             jack_log("ref = %ld was already timebase master", refnum);
00082             return 0;
00083         }
00084     } else {
00085         fTimeBaseMaster = refnum;
00086         fConditionnal = conditionnal;
00087         jack_log("new timebase master: ref = %ld", refnum);
00088         return 0;
00089     }
00090 }
00091 
00092 // RT
00093 bool JackTransportEngine::CheckAllRolling(JackClientInterface** table)
00094 {
00095     for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
00096         JackClientInterface* client = table[i];
00097         if (client && client->GetClientControl()->fTransportState != JackTransportRolling) {
00098             jack_log("CheckAllRolling ref = %ld is not rolling", i);
00099             return false;
00100         }
00101     }
00102     jack_log("CheckAllRolling");
00103     return true;
00104 }
00105 
00106 // RT
00107 void JackTransportEngine::MakeAllStartingLocating(JackClientInterface** table)
00108 {
00109     for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
00110         JackClientInterface* client = table[i];
00111         if (client) {
00112             JackClientControl* control = client->GetClientControl();
00113             // Inactive clients don't have their process function called at all, so they must appear as already "rolling" for the transport....
00114             control->fTransportState = (control->fActive && control->fCallback[kRealTimeCallback]) ? JackTransportStarting : JackTransportRolling;
00115             control->fTransportSync = true; 
00116             control->fTransportTimebase = true; 
00117             jack_log("MakeAllStartingLocating ref = %ld", i);
00118         }
00119     }
00120 }
00121 
00122 // RT
00123 void JackTransportEngine::MakeAllStopping(JackClientInterface** table)
00124 {
00125     for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
00126         JackClientInterface* client = table[i];
00127         if (client) {
00128             JackClientControl* control = client->GetClientControl();
00129             control->fTransportState = JackTransportStopped;
00130             control->fTransportSync = false; 
00131             control->fTransportTimebase = false; 
00132             jack_log("MakeAllStopping ref = %ld", i);
00133         }
00134     }
00135 }
00136 
00137 // RT
00138 void JackTransportEngine::MakeAllLocating(JackClientInterface** table)
00139 {
00140     for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) {
00141         JackClientInterface* client = table[i];
00142         if (client) {
00143             JackClientControl* control = client->GetClientControl();
00144             control->fTransportState = JackTransportStopped;
00145             control->fTransportSync = true; 
00146             control->fTransportTimebase = true; 
00147             jack_log("MakeAllLocating ref = %ld", i);
00148         }
00149     }
00150 }
00151 
00152 // RT
00153 void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time)
00154 {
00155     jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
00156     pending->usecs = time;
00157     pending->frame_rate = frame_rate;
00158     WriteNextStateStop(1);
00159 }
00160 
00161 // RT
00162 void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
00163 {
00164     TrySwitchState(1);  // Switch from "pending" to "current", it always works since there is always a pending state
00165 
00166     /* Handle any new transport command from the last cycle. */
00167     transport_command_t cmd = fTransportCmd;
00168     if (cmd != fPreviousCmd) {
00169         fPreviousCmd = cmd;
00170         jack_log("transport command: %s", (cmd == TransportCommandStart ? "Transport start" : "Transport stop"));
00171     } else {
00172         cmd = TransportCommandNone;
00173     }
00174 
00175     /* state transition switch */
00176     switch (fTransportState) {
00177 
00178         case JackTransportStopped:
00179             // Set a JackTransportStarting for the current cycle, if all clients are ready (no slow_sync) ==> JackTransportRolling next state
00180             if (cmd == TransportCommandStart) {
00181                 jack_log("transport stopped ==> starting frame = %d", ReadCurrentState()->frame);
00182                 fTransportState = JackTransportStarting;
00183                 MakeAllStartingLocating(table);
00184                 SyncTimeout(frame_rate, buffer_size);
00185             } else if (fPendingPos) {
00186                 jack_log("transport stopped ==> stopped (locating) frame = %d", ReadCurrentState()->frame);
00187                 MakeAllLocating(table);
00188             }
00189             break;
00190 
00191         case JackTransportStarting:
00192             if (cmd == TransportCommandStop) {
00193                 jack_log("transport starting ==> stopped frame = %d", ReadCurrentState()->frame);
00194                 fTransportState = JackTransportStopped;
00195                 MakeAllStopping(table);
00196             } else if (fPendingPos) {
00197                 jack_log("transport starting ==> starting frame = %d"), ReadCurrentState()->frame;
00198                 fTransportState = JackTransportStarting;
00199                 MakeAllStartingLocating(table);
00200                 SyncTimeout(frame_rate, buffer_size);
00201             } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) {  // Slow clients may still catch up
00202                 if (fNetworkSync) {
00203                     jack_log("transport starting ==> netstarting frame = %d");
00204                     fTransportState = JackTransportNetStarting;
00205                 } else {
00206                     jack_log("transport starting ==> rolling fSyncTimeLeft = %ld", fSyncTimeLeft);
00207                     fTransportState = JackTransportRolling;
00208                 }
00209             }
00210             break;
00211 
00212         case JackTransportRolling:
00213             if (cmd == TransportCommandStop) {
00214                 jack_log("transport rolling ==> stopped");
00215                 fTransportState = JackTransportStopped;
00216                 MakeAllStopping(table);
00217             } else if (fPendingPos) {
00218                 jack_log("transport rolling ==> starting");
00219                 fTransportState = JackTransportStarting;
00220                 MakeAllStartingLocating(table);
00221                 SyncTimeout(frame_rate, buffer_size);
00222             }
00223             break;
00224 
00225         case JackTransportNetStarting:
00226             break;
00227 
00228         default:
00229             jack_error("Invalid JACK transport state: %d", fTransportState);
00230     }
00231 
00232     /* Update timebase, if needed. */
00233     if (fTransportState == JackTransportRolling) {
00234         jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
00235         pending->frame += buffer_size;
00236         WriteNextStateStop(1);
00237     }
00238 
00239     /* See if an asynchronous position request arrived during the last cycle. */
00240     jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
00241     if (fPendingPos) {
00242         jack_log("New pos = %ld", request->frame);
00243         jack_position_t* pending = WriteNextStateStart(1);
00244         CopyPosition(request, pending);
00245         WriteNextStateStop(1);
00246     }
00247 }
00248 
00249 // Client
00250 void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
00251 {
00252     UInt16 next_index = GetCurrentIndex();
00253     UInt16 cur_index;
00254     do {
00255         cur_index = next_index;
00256         memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
00257         next_index = GetCurrentIndex();
00258     } while (cur_index != next_index); // Until a coherent state has been read
00259 }
00260 
00261 void JackTransportEngine::RequestNewPos(jack_position_t* pos)
00262 {
00263     jack_position_t* request = WriteNextStateStart(2);
00264     pos->unique_1 = pos->unique_2 = GenerateUniqueID();
00265     CopyPosition(pos, request);
00266     jack_log("RequestNewPos pos = %ld", pos->frame);
00267     WriteNextStateStop(2);
00268 }
00269 
00270 jack_transport_state_t JackTransportEngine::Query(jack_position_t* pos)
00271 {
00272     if (pos)
00273         ReadCurrentPos(pos);
00274     return GetState();
00275 }
00276 
00277 jack_nframes_t JackTransportEngine::GetCurrentFrame()
00278 {
00279     jack_position_t pos;
00280     ReadCurrentPos(&pos);
00281 
00282     if (fTransportState == JackTransportRolling) {
00283         float usecs = GetMicroSeconds() - pos.usecs;
00284         jack_nframes_t elapsed = (jack_nframes_t)floor((((float) pos.frame_rate) / 1000000.0f) * usecs);
00285         return pos.frame + elapsed;
00286     } else {
00287         return pos.frame;
00288     }
00289 }
00290 
00291 // RT, client
00292 void JackTransportEngine::CopyPosition(jack_position_t* from, jack_position_t* to)
00293 {
00294     int tries = 0;
00295     long timeout = 1000;
00296 
00297     do {
00298         /* throttle the busy wait if we don't get the answer
00299          * very quickly. See comment above about this
00300          * design.
00301          */
00302         if (tries > 10) {
00303             JackSleep(20);
00304             tries = 0;
00305 
00306             /* debug code to avoid system hangs... */
00307             if (--timeout == 0) {
00308                 jack_error("hung in loop copying position B");
00309                 abort();
00310             }
00311         }
00312         *to = *from;
00313         tries++;
00314 
00315     } while (to->unique_1 != to->unique_2);
00316 }
00317 
00318 
00319 } // end of namespace