Jack2  1.9.9
JackFrameTimer.cpp
1 /*
2 Copyright (C) 2001 Paul Davis
3 Copyright (C) 2004-2008 Grame
4 
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.
9 
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.
14 
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.
18 
19 */
20 
21 #include "JackFrameTimer.h"
22 #include "JackError.h"
23 #include <math.h>
24 #include <stdio.h>
25 
26 namespace Jack
27 {
28 
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)
32 {
33  double f = floor(nr);
34  double c = ceil(nr);
35  return (((c -nr) >= (nr - f)) ? f : c);
36 }
37 #endif
38 
39 JackTimer::JackTimer()
40 {
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 */
48 }
49 
50 jack_nframes_t JackTimer::Time2Frames(jack_time_t usecs, jack_nframes_t buffer_size)
51 {
52  if (fInitialized) {
53  /*
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
62  */
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;
68  }
69 }
70 
71 jack_time_t JackTimer::Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size)
72 {
73  if (fInitialized) {
74  /*
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
83  */
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;
89  }
90 }
91 
92 int JackTimer::GetCycleTimes(jack_nframes_t* current_frames, jack_time_t* current_usecs, jack_time_t* next_usecs, float* period_usecs)
93 {
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;
102  }
103 }
104 
105 jack_nframes_t JackTimer::FramesSinceCycleStart(jack_time_t cur_time, jack_nframes_t frames_rate)
106 {
107  return (jack_nframes_t) floor((((float)frames_rate) / 1000000.0f) * (cur_time - fCurrentCallback));
108 }
109 
110 void JackFrameTimer::InitFrameTime()
111 {
112  fFirstWakeUp = true;
113 }
114 
115 void JackFrameTimer::IncFrameTime(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
116 {
117  if (fFirstWakeUp) {
118  InitFrameTimeAux(callback_usecs, period_usecs);
119  fFirstWakeUp = false;
120  }
121 
122  IncFrameTimeAux(buffer_size, callback_usecs, period_usecs);
123 }
124 
125 void JackFrameTimer::ResetFrameTime(jack_time_t callback_usecs)
126 {
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
133  }
134 }
135 
136 /*
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.
140 */
141 void JackFrameTimer::ReadFrameTime(JackTimer* timer)
142 {
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
150 }
151 
152 // Internal
153 
154 void JackFrameTimer::InitFrameTimeAux(jack_time_t callback_usecs, jack_time_t period_usecs)
155 {
156  /* the first wakeup or post-freewheeling or post-xrun */
157 
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
164  */
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
169  */
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
181  */
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
187  */
188 
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
196 }
197 
198 void JackFrameTimer::IncFrameTimeAux(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
199 {
200  JackTimer* timer = WriteNextStateStart();
201 
202  /* Modified implementation (the actual result is the same).
203 
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().
213 
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.
220 
221  FA 13/02/2012
222  */
223 
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;
232 
233  WriteNextStateStop();
234  TrySwitchState(); // always succeed since there is only one writer
235 }
236 
237 } // end of namespace
238