Merge branch 'master' into develop
[jack2.git] / common / JackFrameTimer.cpp
blob56cbb6ecff4d5d3f2aabd90075f3ede7b60cd0ad
1 /*
2 Copyright (C) 2001 Paul Davis
3 Copyright (C) 2004-2008 Grame
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #include "JackFrameTimer.h"
22 #include "JackError.h"
23 #include <math.h>
24 #include <stdio.h>
26 namespace Jack
29 #if defined(WIN32) && !defined(__MINGW32__)
30 /* missing on Windows : see http://bugs.mysql.com/bug.php?id=15936 */
31 inline double rint(double nr)
33 double f = floor(nr);
34 double c = ceil(nr);
35 return (((c -nr) >= (nr - f)) ? f : c);
37 #endif
39 JackTimer::JackTimer()
41 fInitialized = false;
42 fFrames = 0;
43 fCurrentWakeup = 0;
44 fCurrentCallback = 0;
45 fNextWakeUp = 0;
46 fPeriodUsecs = 0.0f;
47 fFilterOmega = 0.0f; /* Initialised later */
50 jack_nframes_t JackTimer::Time2Frames(jack_time_t usecs, jack_nframes_t buffer_size)
52 if (fInitialized) {
54 Make sure we have signed differences. It would make a lot of sense
55 to use the standard signed intNN_t types everywhere instead of e.g.
56 jack_nframes_t and jack_time_t. This would at least ensure that the
57 types used below are the correct ones. There is no way to get a type
58 that would be 'a signed version of jack_time_t' for example - the
59 types below are inherently fragile and there is no automatic way to
60 check they are the correct ones. The only way is to check manually
61 against jack/types.h. FA - 16/02/2012
63 int64_t du = usecs - fCurrentWakeup;
64 int64_t dp = fNextWakeUp - fCurrentWakeup;
65 return fFrames + (int32_t)rint((double)du / (double)dp * buffer_size);
66 } else {
67 return 0;
71 jack_time_t JackTimer::Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size)
73 if (fInitialized) {
75 Make sure we have signed differences. It would make a lot of sense
76 to use the standard signed intNN_t types everywhere instead of e.g.
77 jack_nframes_t and jack_time_t. This would at least ensure that the
78 types used below are the correct ones. There is no way to get a type
79 that would be 'a signed version of jack_time_t' for example - the
80 types below are inherently fragile and there is no automatic way to
81 check they are the correct ones. The only way is to check manually
82 against jack/types.h. FA - 16/02/2012
84 int32_t df = frames - fFrames;
85 int64_t dp = fNextWakeUp - fCurrentWakeup;
86 return fCurrentWakeup + (int64_t)rint((double) df * (double) dp / buffer_size);
87 } else {
88 return 0;
92 int JackTimer::GetCycleTimes(jack_nframes_t* current_frames, jack_time_t* current_usecs, jack_time_t* next_usecs, float* period_usecs)
94 if (fInitialized) {
95 *current_frames = fFrames;
96 *current_usecs = fCurrentWakeup;
97 *next_usecs = fNextWakeUp;
98 *period_usecs = fPeriodUsecs;
99 return 0;
100 } else {
101 return -1;
105 jack_nframes_t JackTimer::FramesSinceCycleStart(jack_time_t cur_time, jack_nframes_t frames_rate)
107 return (jack_nframes_t) floor((((float)frames_rate) / 1000000.0f) * (cur_time - fCurrentCallback));
110 void JackFrameTimer::InitFrameTime()
112 fFirstWakeUp = true;
115 void JackFrameTimer::IncFrameTime(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
117 if (fFirstWakeUp) {
118 InitFrameTimeAux(callback_usecs, period_usecs);
119 fFirstWakeUp = false;
122 IncFrameTimeAux(buffer_size, callback_usecs, period_usecs);
125 void JackFrameTimer::ResetFrameTime(jack_time_t callback_usecs)
127 if (!fFirstWakeUp) { // ResetFrameTime may be called by a xrun/delayed wakeup on the first cycle
128 JackTimer* timer = WriteNextStateStart();
129 timer->fCurrentWakeup = callback_usecs;
130 timer->fCurrentCallback = callback_usecs;
131 WriteNextStateStop();
132 TrySwitchState(); // always succeed since there is only one writer
137 Use the state returned by ReadCurrentState and check that the state was not changed during the read operation.
138 The operation is lock-free since there is no intermediate state in the write operation that could cause the
139 read to loop forever.
141 void JackFrameTimer::ReadFrameTime(JackTimer* timer)
143 UInt16 next_index = GetCurrentIndex();
144 UInt16 cur_index;
145 do {
146 cur_index = next_index;
147 memcpy(timer, ReadCurrentState(), sizeof(JackTimer));
148 next_index = GetCurrentIndex();
149 } while (cur_index != next_index); // Until a coherent state has been read
152 // Internal
154 void JackFrameTimer::InitFrameTimeAux(jack_time_t callback_usecs, jack_time_t period_usecs)
156 /* the first wakeup or post-freewheeling or post-xrun */
158 /* There seems to be no significant difference between
159 the two conditions OR-ed above. Incrementing the
160 frame_time after an xrun shouldn't harm, as there
161 will be a discontinuity anyway. So the two are
162 combined in this version.
163 FA 16/03/2012
165 /* Since the DLL *will* be run, next_wakeup should be the
166 current wakeup time *without* adding the period time, as
167 if it were computed in the previous period.
168 FA 16/03/2012
170 /* Added initialisation of timer->period_usecs, required
171 due to the modified implementation of the DLL itself.
172 OTOH, this should maybe not be repeated after e.g.
173 freewheeling or an xrun, as the current value would be
174 more accurate than the nominal one. But it doesn't really
175 harm either. Implementing this would require a new flag
176 in the engine structure, to be used after freewheeling
177 or an xrun instead of first_wakeup. I don't know if this
178 can be done without breaking compatibility, so I did not
179 add this
180 FA 13/02/2012
182 /* Added initialisation of timer->filter_omega. This makes
183 the DLL bandwidth independent of the actual period time.
184 The bandwidth is now 1/8 Hz in all cases. The value of
185 timer->filter_omega is 2 * pi * BW * Tperiod.
186 FA 13/02/2012
189 JackTimer* timer = WriteNextStateStart();
190 timer->fPeriodUsecs = (float)period_usecs;
191 timer->fCurrentCallback = callback_usecs;
192 timer->fNextWakeUp = callback_usecs;
193 timer->fFilterOmega = period_usecs * 7.854e-7f;
194 WriteNextStateStop();
195 TrySwitchState(); // always succeed since there is only one writer
198 void JackFrameTimer::IncFrameTimeAux(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
200 JackTimer* timer = WriteNextStateStart();
202 /* Modified implementation (the actual result is the same).
204 'fSecondOrderIntegrator' is renamed to 'fPeriodUsecs'
205 and now represents the DLL's best estimate of the
206 period time in microseconds (before it was a scaled
207 version of the difference w.r.t. the nominal value).
208 This allows this value to be made available to clients
209 that are interested in it (see jack_get_cycle_times).
210 This change also means that 'fPeriodUsecs' must be
211 initialised to the nominal period time instead of zero.
212 This is done in the first cycle in jack_run_cycle().
214 'fFilterCoefficient' is renamed to 'fFilterOmega'. It
215 is now equal to the 'omega' value as defined in the
216 'Using a DLL to filter time' paper (before it was a
217 scaled version of this value). It is computed once in
218 jack_run_cycle() rather than set to a fixed value. This
219 makes the DLL bandwidth independent of the period time.
221 FA 13/02/2012
224 float delta = (float)((int64_t)callback_usecs - (int64_t)timer->fNextWakeUp);
225 delta *= timer->fFilterOmega;
226 timer->fCurrentWakeup = timer->fNextWakeUp;
227 timer->fCurrentCallback = callback_usecs;
228 timer->fFrames += buffer_size;
229 timer->fPeriodUsecs += timer->fFilterOmega * delta;
230 timer->fNextWakeUp += (int64_t)floorf(timer->fPeriodUsecs + 1.41f * delta + 0.5f);
231 timer->fInitialized = true;
233 WriteNextStateStop();
234 TrySwitchState(); // always succeed since there is only one writer
237 } // end of namespace