Jack2: JackFrameTimer.cpp Source File

Jack2 1.9.12

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
Jack::JackAtomicState< JackTimer >::ReadCurrentState
JackTimer * ReadCurrentState()
Returns the current state : only valid in the RT reader thread.
Definition: JackAtomicState.h:144
Jack::JackAtomicState< JackTimer >::GetCurrentIndex
UInt16 GetCurrentIndex()
Returns the current state index.
Definition: JackAtomicState.h:152
Jack::JackAtomicState< JackTimer >::WriteNextStateStart
JackTimer * WriteNextStateStart()
Start write operation : setup and returns the next state to update, check for recursive write calls.
Definition: JackAtomicState.h:191
Jack::JackAtomicState< JackTimer >::TrySwitchState
JackTimer * TrySwitchState()
Tries to switch to the next state and returns the new current state (either the same as before if cas...
Definition: JackAtomicState.h:160
Jack::JackAtomicState< JackTimer >::WriteNextStateStop
void WriteNextStateStop()
Stop write operation : make the next state ready to be used by the RT thread.
Definition: JackAtomicState.h:202