Jack2  1.9.8
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  fFilterCoefficient = 0.01f;
47  fSecondOrderIntegrator = 0.0f;
48 }
49 
50 jack_nframes_t JackTimer::Time2Frames(jack_time_t time, jack_nframes_t buffer_size)
51 {
52  if (fInitialized) {
53  return fFrames + (long)rint(((double) ((long long)(time - fCurrentWakeup)) / ((long long)(fNextWakeUp - fCurrentWakeup))) * buffer_size);
54  } else {
55  return 0;
56  }
57 }
58 
59 jack_time_t JackTimer::Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size)
60 {
61  if (fInitialized) {
62  return fCurrentWakeup + (long)rint(((double) ((long long)(frames - fFrames)) * ((long long)(fNextWakeUp - fCurrentWakeup))) / buffer_size);
63  } else {
64  return 0;
65  }
66 }
67 
68 jack_nframes_t JackTimer::FramesSinceCycleStart(jack_time_t cur_time, jack_nframes_t frames_rate)
69 {
70  return (jack_nframes_t) floor((((float)frames_rate) / 1000000.0f) * (cur_time - fCurrentCallback));
71 }
72 
73 void JackFrameTimer::InitFrameTime()
74 {
75  fFirstWakeUp = true;
76 }
77 
78 void JackFrameTimer::IncFrameTime(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
79 {
80  if (fFirstWakeUp) {
81  InitFrameTimeAux(callback_usecs, period_usecs);
82  fFirstWakeUp = false;
83  } else {
84  IncFrameTimeAux(buffer_size, callback_usecs, period_usecs);
85  }
86 }
87 
88 void JackFrameTimer::ResetFrameTime(jack_nframes_t frames_rate, jack_time_t callback_usecs, jack_time_t period_usecs)
89 {
90  if (!fFirstWakeUp) { // ResetFrameTime may be called by a xrun/delayed wakeup on the first cycle
91  JackTimer* timer = WriteNextStateStart();
92  jack_nframes_t period_size_guess = (jack_nframes_t)(frames_rate * ((timer->fNextWakeUp - timer->fCurrentWakeup) / 1000000.0));
93  timer->fFrames += ((callback_usecs - timer->fNextWakeUp) / period_size_guess) * period_size_guess;
94  timer->fCurrentWakeup = callback_usecs;
95  timer->fCurrentCallback = callback_usecs;
96  timer->fNextWakeUp = callback_usecs + period_usecs;
98  TrySwitchState(); // always succeed since there is only one writer
99  }
100 }
101 
102 /*
103  Use the state returned by ReadCurrentState and check that the state was not changed during the read operation.
104  The operation is lock-free since there is no intermediate state in the write operation that could cause the
105  read to loop forever.
106 */
107 void JackFrameTimer::ReadFrameTime(JackTimer* timer)
108 {
109  UInt16 next_index = GetCurrentIndex();
110  UInt16 cur_index;
111  do {
112  cur_index = next_index;
113  memcpy(timer, ReadCurrentState(), sizeof(JackTimer));
114  next_index = GetCurrentIndex();
115  } while (cur_index != next_index); // Until a coherent state has been read
116 }
117 
118 // Internal
119 
120 void JackFrameTimer::InitFrameTimeAux(jack_time_t callback_usecs, jack_time_t period_usecs)
121 {
122  JackTimer* timer = WriteNextStateStart();
123  timer->fSecondOrderIntegrator = 0.0f;
124  timer->fCurrentCallback = callback_usecs;
125  timer->fNextWakeUp = callback_usecs + period_usecs;
127  TrySwitchState(); // always succeed since there is only one writer
128 }
129 
130 void JackFrameTimer::IncFrameTimeAux(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
131 {
132  JackTimer* timer = WriteNextStateStart();
133  float delta = (int64_t)callback_usecs - (int64_t)timer->fNextWakeUp;
134  timer->fCurrentWakeup = timer->fNextWakeUp;
135  timer->fCurrentCallback = callback_usecs;
136  timer->fFrames += buffer_size;
137  timer->fSecondOrderIntegrator += 0.5f * timer->fFilterCoefficient * delta;
138  timer->fNextWakeUp = timer->fCurrentWakeup + period_usecs + (int64_t) floorf((timer->fFilterCoefficient * (delta + timer->fSecondOrderIntegrator)));
139  timer->fInitialized = true;
141  TrySwitchState(); // always succeed since there is only one writer
142 }
143 
144 } // end of namespace
145