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 */
rint(double nr)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 
JackTimer()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 
Time2Frames(jack_time_t usecs,jack_nframes_t buffer_size)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 
Frames2Time(jack_nframes_t frames,jack_nframes_t buffer_size)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 
GetCycleTimes(jack_nframes_t * current_frames,jack_time_t * current_usecs,jack_time_t * next_usecs,float * period_usecs)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 
FramesSinceCycleStart(jack_time_t cur_time,jack_nframes_t frames_rate)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 
InitFrameTime()110 void JackFrameTimer::InitFrameTime()
111 {
112     fFirstWakeUp = true;
113 }
114 
IncFrameTime(jack_nframes_t buffer_size,jack_time_t callback_usecs,jack_time_t period_usecs)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 
ResetFrameTime(jack_time_t callback_usecs)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 */
ReadFrameTime(JackTimer * timer)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 
InitFrameTimeAux(jack_time_t callback_usecs,jack_time_t period_usecs)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 
IncFrameTimeAux(jack_nframes_t buffer_size,jack_time_t callback_usecs,jack_time_t period_usecs)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 
239