Jack2
1.9.10
|
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 "JackFrameTimer.h" 00022 #include "JackError.h" 00023 #include <math.h> 00024 #include <stdio.h> 00025 00026 namespace Jack 00027 { 00028 00029 #if defined(WIN32) && !defined(__MINGW32__) 00030 /* missing on Windows : see http://bugs.mysql.com/bug.php?id=15936 */ 00031 inline double rint(double nr) 00032 { 00033 double f = floor(nr); 00034 double c = ceil(nr); 00035 return (((c -nr) >= (nr - f)) ? f : c); 00036 } 00037 #endif 00038 00039 JackTimer::JackTimer() 00040 { 00041 fInitialized = false; 00042 fFrames = 0; 00043 fCurrentWakeup = 0; 00044 fCurrentCallback = 0; 00045 fNextWakeUp = 0; 00046 fPeriodUsecs = 0.0f; 00047 fFilterOmega = 0.0f; /* Initialised later */ 00048 } 00049 00050 jack_nframes_t JackTimer::Time2Frames(jack_time_t usecs, jack_nframes_t buffer_size) 00051 { 00052 if (fInitialized) { 00053 /* 00054 Make sure we have signed differences. It would make a lot of sense 00055 to use the standard signed intNN_t types everywhere instead of e.g. 00056 jack_nframes_t and jack_time_t. This would at least ensure that the 00057 types used below are the correct ones. There is no way to get a type 00058 that would be 'a signed version of jack_time_t' for example - the 00059 types below are inherently fragile and there is no automatic way to 00060 check they are the correct ones. The only way is to check manually 00061 against jack/types.h. FA - 16/02/2012 00062 */ 00063 int64_t du = usecs - fCurrentWakeup; 00064 int64_t dp = fNextWakeUp - fCurrentWakeup; 00065 return fFrames + (int32_t)rint((double)du / (double)dp * buffer_size); 00066 } else { 00067 return 0; 00068 } 00069 } 00070 00071 jack_time_t JackTimer::Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size) 00072 { 00073 if (fInitialized) { 00074 /* 00075 Make sure we have signed differences. It would make a lot of sense 00076 to use the standard signed intNN_t types everywhere instead of e.g. 00077 jack_nframes_t and jack_time_t. This would at least ensure that the 00078 types used below are the correct ones. There is no way to get a type 00079 that would be 'a signed version of jack_time_t' for example - the 00080 types below are inherently fragile and there is no automatic way to 00081 check they are the correct ones. The only way is to check manually 00082 against jack/types.h. FA - 16/02/2012 00083 */ 00084 int32_t df = frames - fFrames; 00085 int64_t dp = fNextWakeUp - fCurrentWakeup; 00086 return fCurrentWakeup + (int64_t)rint((double) df * (double) dp / buffer_size); 00087 } else { 00088 return 0; 00089 } 00090 } 00091 00092 int JackTimer::GetCycleTimes(jack_nframes_t* current_frames, jack_time_t* current_usecs, jack_time_t* next_usecs, float* period_usecs) 00093 { 00094 if (fInitialized) { 00095 *current_frames = fFrames; 00096 *current_usecs = fCurrentWakeup; 00097 *next_usecs = fNextWakeUp; 00098 *period_usecs = fPeriodUsecs; 00099 return 0; 00100 } else { 00101 return -1; 00102 } 00103 } 00104 00105 jack_nframes_t JackTimer::FramesSinceCycleStart(jack_time_t cur_time, jack_nframes_t frames_rate) 00106 { 00107 return (jack_nframes_t) floor((((float)frames_rate) / 1000000.0f) * (cur_time - fCurrentCallback)); 00108 } 00109 00110 void JackFrameTimer::InitFrameTime() 00111 { 00112 fFirstWakeUp = true; 00113 } 00114 00115 void JackFrameTimer::IncFrameTime(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs) 00116 { 00117 if (fFirstWakeUp) { 00118 InitFrameTimeAux(callback_usecs, period_usecs); 00119 fFirstWakeUp = false; 00120 } 00121 00122 IncFrameTimeAux(buffer_size, callback_usecs, period_usecs); 00123 } 00124 00125 void JackFrameTimer::ResetFrameTime(jack_time_t callback_usecs) 00126 { 00127 if (!fFirstWakeUp) { // ResetFrameTime may be called by a xrun/delayed wakeup on the first cycle 00128 JackTimer* timer = WriteNextStateStart(); 00129 timer->fCurrentWakeup = callback_usecs; 00130 timer->fCurrentCallback = callback_usecs; 00131 WriteNextStateStop(); 00132 TrySwitchState(); // always succeed since there is only one writer 00133 } 00134 } 00135 00136 /* 00137 Use the state returned by ReadCurrentState and check that the state was not changed during the read operation. 00138 The operation is lock-free since there is no intermediate state in the write operation that could cause the 00139 read to loop forever. 00140 */ 00141 void JackFrameTimer::ReadFrameTime(JackTimer* timer) 00142 { 00143 UInt16 next_index = GetCurrentIndex(); 00144 UInt16 cur_index; 00145 do { 00146 cur_index = next_index; 00147 memcpy(timer, ReadCurrentState(), sizeof(JackTimer)); 00148 next_index = GetCurrentIndex(); 00149 } while (cur_index != next_index); // Until a coherent state has been read 00150 } 00151 00152 // Internal 00153 00154 void JackFrameTimer::InitFrameTimeAux(jack_time_t callback_usecs, jack_time_t period_usecs) 00155 { 00156 /* the first wakeup or post-freewheeling or post-xrun */ 00157 00158 /* There seems to be no significant difference between 00159 the two conditions OR-ed above. Incrementing the 00160 frame_time after an xrun shouldn't harm, as there 00161 will be a discontinuity anyway. So the two are 00162 combined in this version. 00163 FA 16/03/2012 00164 */ 00165 /* Since the DLL *will* be run, next_wakeup should be the 00166 current wakeup time *without* adding the period time, as 00167 if it were computed in the previous period. 00168 FA 16/03/2012 00169 */ 00170 /* Added initialisation of timer->period_usecs, required 00171 due to the modified implementation of the DLL itself. 00172 OTOH, this should maybe not be repeated after e.g. 00173 freewheeling or an xrun, as the current value would be 00174 more accurate than the nominal one. But it doesn't really 00175 harm either. Implementing this would require a new flag 00176 in the engine structure, to be used after freewheeling 00177 or an xrun instead of first_wakeup. I don't know if this 00178 can be done without breaking compatibility, so I did not 00179 add this 00180 FA 13/02/2012 00181 */ 00182 /* Added initialisation of timer->filter_omega. This makes 00183 the DLL bandwidth independent of the actual period time. 00184 The bandwidth is now 1/8 Hz in all cases. The value of 00185 timer->filter_omega is 2 * pi * BW * Tperiod. 00186 FA 13/02/2012 00187 */ 00188 00189 JackTimer* timer = WriteNextStateStart(); 00190 timer->fPeriodUsecs = (float)period_usecs; 00191 timer->fCurrentCallback = callback_usecs; 00192 timer->fNextWakeUp = callback_usecs; 00193 timer->fFilterOmega = period_usecs * 7.854e-7f; 00194 WriteNextStateStop(); 00195 TrySwitchState(); // always succeed since there is only one writer 00196 } 00197 00198 void JackFrameTimer::IncFrameTimeAux(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs) 00199 { 00200 JackTimer* timer = WriteNextStateStart(); 00201 00202 /* Modified implementation (the actual result is the same). 00203 00204 'fSecondOrderIntegrator' is renamed to 'fPeriodUsecs' 00205 and now represents the DLL's best estimate of the 00206 period time in microseconds (before it was a scaled 00207 version of the difference w.r.t. the nominal value). 00208 This allows this value to be made available to clients 00209 that are interested in it (see jack_get_cycle_times). 00210 This change also means that 'fPeriodUsecs' must be 00211 initialised to the nominal period time instead of zero. 00212 This is done in the first cycle in jack_run_cycle(). 00213 00214 'fFilterCoefficient' is renamed to 'fFilterOmega'. It 00215 is now equal to the 'omega' value as defined in the 00216 'Using a DLL to filter time' paper (before it was a 00217 scaled version of this value). It is computed once in 00218 jack_run_cycle() rather than set to a fixed value. This 00219 makes the DLL bandwidth independent of the period time. 00220 00221 FA 13/02/2012 00222 */ 00223 00224 float delta = (float)((int64_t)callback_usecs - (int64_t)timer->fNextWakeUp); 00225 delta *= timer->fFilterOmega; 00226 timer->fCurrentWakeup = timer->fNextWakeUp; 00227 timer->fCurrentCallback = callback_usecs; 00228 timer->fFrames += buffer_size; 00229 timer->fPeriodUsecs += timer->fFilterOmega * delta; 00230 timer->fNextWakeUp += (int64_t)floorf(timer->fPeriodUsecs + 1.41f * delta + 0.5f); 00231 timer->fInitialized = true; 00232 00233 WriteNextStateStop(); 00234 TrySwitchState(); // always succeed since there is only one writer 00235 } 00236 00237 } // end of namespace 00238