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 General Public License as published by
00007 the Free Software Foundation; either version 2 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 General Public License for more details.
00014 
00015 You should have received a copy of the GNU General Public License
00016 along with this program; if not, write to the Free Software
00017 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018 
00019 */
00020 
00021 #include "JackTransportEngine.h"
00022 #include "JackClientControl.h"
00023 #include "JackError.h"
00024 #include "JackTime.h" 
00025 #include <assert.h>
00026 #include <stdlib.h>
00027 
00028 using namespace std;
00029 
00030 namespace Jack
00031 {
00032 
00033 JackTransportEngine::JackTransportEngine(): JackAtomicArrayState<jack_position_t>()
00034 {
00035     fTransportState = JackTransportStopped;
00036     fTransportCmd = fPreviousCmd = TransportCommandStop;
00037     fSyncTimeout = 2000000;     /* 2 second default */
00038     fSyncTimeLeft = 0;
00039     fTimeBaseMaster = -1;
00040     fWriteCounter = 0;
00041     fPendingPos = false;
00042 }
00043 
00044 // compute the number of cycle for timeout
00045 void JackTransportEngine::SyncTimeout(jack_nframes_t frame_rate, jack_nframes_t buffer_size)
00046 {
00047     long buf_usecs = (long)((buffer_size * (jack_time_t) 1000000) / frame_rate);
00048     fSyncTimeLeft = fSyncTimeout / buf_usecs;
00049     JackLog("SyncTimeout fSyncTimeout = %ld fSyncTimeLeft = %ld\n", (long)fSyncTimeout, (long)fSyncTimeLeft);
00050 }
00051 
00052 int JackTransportEngine::ResetTimebase(int refnum)
00053 {
00054     if (fTimeBaseMaster == refnum) {
00055         jack_position_t* request = WriteNextStateStart(2); // To check
00056         request->valid = (jack_position_bits_t)0;
00057         WriteNextStateStop(2);
00058         fTimeBaseMaster = -1;
00059         return 0;
00060     } else {
00061         return EINVAL;
00062     }
00063 }
00064 
00065 int JackTransportEngine::SetTimebase(int refnum, bool conditionnal)
00066 {
00067     if (conditionnal && fTimeBaseMaster > 0) {
00068         if (refnum != fTimeBaseMaster) {
00069             JackLog("conditional timebase for ref = %ld failed: %ld is already the master\n", refnum, fTimeBaseMaster);
00070             return EBUSY;
00071         } else {
00072             JackLog("ref = %ld was already timebase master\n", refnum);
00073             return 0;
00074         }
00075     } else {
00076         fTimeBaseMaster = refnum;
00077         JackLog("new timebase master: ref = %ld\n", refnum);
00078         return 0;
00079     }
00080 }
00081 
00082 bool JackTransportEngine::CheckOneSynching(JackClientInterface** table)
00083 {
00084     for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
00085         JackClientInterface* client = table[i];
00086         if (client && client->GetClientControl()->fTransportState == JackTransportSynching) {
00087             JackLog("CheckOneSynching\n");
00088             return true;
00089         }
00090     }
00091     return false;
00092 }
00093 
00094 bool JackTransportEngine::CheckAllRolling(JackClientInterface** table)
00095 {
00096     for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
00097         JackClientInterface* client = table[i];
00098         if (client && client->GetClientControl()->fTransportState != JackTransportRolling) {
00099             JackLog("CheckAllRolling refnum = %ld is not rolling\n", i);
00100             return false;
00101         }
00102     }
00103     JackLog("CheckAllRolling\n");
00104     return true;
00105 }
00106 
00107 void JackTransportEngine::MakeAllStarting(JackClientInterface** table)
00108 {
00109     for (int i = REAL_REFNUM; i < CLIENT_NUM; i++) {
00110         JackClientInterface* client = table[i];
00111         if (client) {
00112             // Unactive clients don't have their process function called at all, they appear as already "rolling" for the transport....
00113             client->GetClientControl()->fTransportState = (client->GetClientControl()->fActive) ? JackTransportStarting : JackTransportRolling;
00114             JackLog("MakeAllStarting refnum = %ld \n", i);
00115         }
00116     }
00117     JackLog("MakeAllStarting\n");
00118 }
00119 
00120 void JackTransportEngine::CycleBegin(jack_nframes_t frame_rate, jack_time_t time) // really needed?? (would be done in CycleEnd...)
00121 {
00122     jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
00123     pending->usecs = time;
00124     pending->frame_rate = frame_rate;
00125     WriteNextStateStop(1);
00126 }
00127 
00128 void JackTransportEngine::CycleEnd(JackClientInterface** table, jack_nframes_t frame_rate, jack_nframes_t buffer_size)
00129 {
00130     TrySwitchState(1);  // Switch from "pending" to "current", it always works since there is always a pending state
00131 
00132     /* Handle any new transport command from the last cycle. */
00133     transport_command_t cmd = fTransportCmd;
00134     if (cmd != fPreviousCmd) {
00135         fPreviousCmd = cmd;
00136         JackLog("transport command: %s\n", (cmd == TransportCommandStart ? "START" : "STOP"));
00137     } else {
00138         cmd = TransportCommandNone;
00139     }
00140 
00141     /* state transition switch */
00142     switch (fTransportState) {
00143 
00144         case JackTransportSynching:
00145             if (cmd == TransportCommandStart) {
00146                 fTransportState = JackTransportStarting;
00147                 MakeAllStarting(table);
00148                 SyncTimeout(frame_rate, buffer_size);
00149                 JackLog("transport locate ==> starting....\n");
00150             } else if (fPendingPos) {
00151                 fTransportState = JackTransportSynching;
00152                 JackLog("transport locate ==> locate....\n");
00153             } else {
00154                 fTransportState = JackTransportStopped;
00155                 JackLog("transport locate ==> stopped....\n");
00156             }
00157             break;
00158 
00159         case JackTransportStopped:
00160             // Set a JackTransportStarting for the current cycle, if all clients are ready (now slow_sync) ==> JackTransportRolling next state
00161             if (cmd == TransportCommandStart) {
00162                 fTransportState = JackTransportStarting;
00163                 MakeAllStarting(table);
00164                 SyncTimeout(frame_rate, buffer_size);
00165                 JackLog("transport stopped ==> starting....\n");
00166             } else if (fPendingPos || CheckOneSynching(table)) {
00167                 fTransportState = JackTransportSynching;
00168                 JackLog("transport stopped ==> locate....\n");
00169             }
00170             break;
00171 
00172         case JackTransportStarting:
00173             JackLog("transport starting fSyncTimeLeft %ld\n", fSyncTimeLeft);
00174 
00175             if (cmd == TransportCommandStop) {
00176                 fTransportState = JackTransportStopped;
00177                 JackLog("transport starting ==> stopped\n");
00178             } else if (fPendingPos) {
00179                 fTransportState = JackTransportStarting;
00180                 MakeAllStarting(table);
00181                 SyncTimeout(frame_rate, buffer_size);
00182             } else if (--fSyncTimeLeft == 0 || CheckAllRolling(table)) {
00183                 fTransportState = JackTransportRolling;
00184                 JackLog("transport starting ==> rolling.... fSyncTimeLeft %ld\n", fSyncTimeLeft);
00185             }
00186             break;
00187 
00188         case JackTransportRolling:
00189             if (cmd == TransportCommandStop) {
00190                 fTransportState = JackTransportStopped;
00191                 JackLog("transport rolling ==> stopped\n");
00192             } else if (fPendingPos || CheckOneSynching(table)) {
00193                 fTransportState = JackTransportStarting;
00194                 MakeAllStarting(table);
00195                 SyncTimeout(frame_rate, buffer_size);
00196                 JackLog("transport rolling ==> starting....\n");
00197             }
00198             break;
00199 
00200         default:
00201             jack_error("Invalid JACK transport state: %d", fTransportState);
00202     }
00203 
00204     /* Update timebase, if needed. */
00205     if (fTransportState == JackTransportRolling) {
00206         jack_position_t* pending = WriteNextStateStart(1); // Update "pending" state
00207         pending->frame += buffer_size;
00208         WriteNextStateStop(1);
00209     }
00210 
00211     /* See if an asynchronous position request arrived during the last cycle. */
00212     jack_position_t* request = WriteNextStateStart(2, &fPendingPos);
00213     if (fPendingPos) {
00214         JackLog("New pos = %ld\n", request->frame);
00215         jack_position_t* pending = WriteNextStateStart(1);
00216         TransportCopyPosition(request, pending);
00217         WriteNextStateStop(1);
00218     }
00219 }
00220 
00221 void JackTransportEngine::ReadCurrentPos(jack_position_t* pos)
00222 {
00223     UInt16 next_index = GetCurrentIndex();
00224     UInt16 cur_index;
00225     do {
00226         cur_index = next_index;
00227         memcpy(pos, ReadCurrentState(), sizeof(jack_position_t));
00228         next_index = GetCurrentIndex();
00229     } while (cur_index != next_index); // Until a coherent state has been read
00230 }
00231 
00232 void JackTransportEngine::TransportCopyPosition(jack_position_t* from, jack_position_t* to)
00233 {
00234     int tries = 0;
00235     long timeout = 1000;
00236 
00237     do {
00238         /* throttle the busy wait if we don't get the answer
00239          * very quickly. See comment above about this
00240          * design.
00241          */
00242         if (tries > 10) {
00243             JackSleep(20);
00244             tries = 0;
00245 
00246             /* debug code to avoid system hangs... */
00247             if (--timeout == 0) {
00248                 jack_error("hung in loop copying position B");
00249                 abort();
00250             }
00251         }
00252         *to = *from;
00253         tries++;
00254 
00255     } while (to->unique_1 != to->unique_2);
00256 }
00257 
00258 
00259 } // end of namespace

Generated on Thu Feb 14 11:16:02 2008 for Jackdmp by  doxygen 1.5.1