Jack2 1.9.6
|
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