Jack2 1.9.10

JackFrameTimer.cpp

00001 /*
00002 Copyright (C) 2001 Paul Davis
00003 Copyright (C) 2004-2008 Grame
00004 
00005 This program is free software; you can redistribute it and/or modify
00006 it under the terms of the GNU Lesser General Public License as published by
00007 the Free Software Foundation; either version 2.1 of the License, or
00008 (at your option) any later version.
00009 
00010 This program is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 GNU Lesser General Public License for more details.
00014 
00015 You should have received a copy of the GNU Lesser General Public License
00016 along with this program; if not, write to the Free Software
00017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00018 
00019 */
00020 
00021 #include "JackFrameTimer.h"
00022 #include "JackError.h"
00023 #include <math.h>
00024 #include <stdio.h>
00025 
00026 namespace Jack
00027 {
00028 
00029 #if defined(WIN32) && !defined(__MINGW32__)
00030 /* missing on Windows : see http://bugs.mysql.com/bug.php?id=15936 */
00031 inline double rint(double nr)
00032 {
00033     double f = floor(nr);
00034     double c = ceil(nr);
00035     return (((c -nr) >= (nr - f)) ? f : c);
00036 }
00037 #endif
00038 
00039 JackTimer::JackTimer()
00040 {
00041     fInitialized = false;
00042     fFrames = 0;
00043     fCurrentWakeup = 0;
00044     fCurrentCallback = 0;
00045     fNextWakeUp = 0;
00046     fPeriodUsecs = 0.0f;
00047     fFilterOmega = 0.0f; /* Initialised later */
00048 }
00049 
00050 jack_nframes_t JackTimer::Time2Frames(jack_time_t usecs, jack_nframes_t buffer_size)
00051 {
00052     if (fInitialized) {
00053         /*
00054         Make sure we have signed differences. It would make a lot of sense
00055         to use the standard signed intNN_t types everywhere  instead of e.g.
00056         jack_nframes_t and jack_time_t. This would at least ensure that the
00057         types used below are the correct ones. There is no way to get a type
00058         that would be 'a signed version of jack_time_t' for example - the
00059         types below are inherently fragile and there is no automatic way to
00060         check they are the correct ones. The only way is to check manually
00061         against jack/types.h.  FA - 16/02/2012
00062         */
00063         int64_t du = usecs - fCurrentWakeup;
00064         int64_t dp = fNextWakeUp - fCurrentWakeup;
00065         return fFrames + (int32_t)rint((double)du / (double)dp * buffer_size);
00066     } else {
00067         return 0;
00068     }
00069 }
00070 
00071 jack_time_t JackTimer::Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size)
00072 {
00073     if (fInitialized) {
00074         /*
00075         Make sure we have signed differences. It would make a lot of sense
00076         to use the standard signed intNN_t types everywhere  instead of e.g.
00077         jack_nframes_t and jack_time_t. This would at least ensure that the
00078         types used below are the correct ones. There is no way to get a type
00079         that would be 'a signed version of jack_time_t' for example - the
00080         types below are inherently fragile and there is no automatic way to
00081         check they are the correct ones. The only way is to check manually
00082         against jack/types.h.  FA - 16/02/2012
00083         */
00084         int32_t df = frames - fFrames;
00085         int64_t dp = fNextWakeUp - fCurrentWakeup;
00086         return fCurrentWakeup + (int64_t)rint((double) df * (double) dp / buffer_size);
00087     } else {
00088         return 0;
00089     }
00090 }
00091 
00092 int JackTimer::GetCycleTimes(jack_nframes_t* current_frames, jack_time_t* current_usecs, jack_time_t* next_usecs, float* period_usecs)
00093 {
00094     if (fInitialized) {
00095         *current_frames  = fFrames;
00096         *current_usecs = fCurrentWakeup;
00097         *next_usecs = fNextWakeUp;
00098         *period_usecs = fPeriodUsecs;
00099         return 0;
00100     } else {
00101         return -1;
00102     }
00103 }
00104 
00105 jack_nframes_t JackTimer::FramesSinceCycleStart(jack_time_t cur_time, jack_nframes_t frames_rate)
00106 {
00107     return (jack_nframes_t) floor((((float)frames_rate) / 1000000.0f) * (cur_time - fCurrentCallback));
00108 }
00109 
00110 void JackFrameTimer::InitFrameTime()
00111 {
00112     fFirstWakeUp = true;
00113 }
00114 
00115 void JackFrameTimer::IncFrameTime(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
00116 {
00117     if (fFirstWakeUp) {
00118         InitFrameTimeAux(callback_usecs, period_usecs);
00119         fFirstWakeUp = false;
00120     }
00121     
00122     IncFrameTimeAux(buffer_size, callback_usecs, period_usecs);
00123 }
00124 
00125 void JackFrameTimer::ResetFrameTime(jack_time_t callback_usecs)
00126 {
00127     if (!fFirstWakeUp) { // ResetFrameTime may be called by a xrun/delayed wakeup on the first cycle
00128         JackTimer* timer = WriteNextStateStart();
00129         timer->fCurrentWakeup = callback_usecs;
00130         timer->fCurrentCallback = callback_usecs;
00131         WriteNextStateStop();
00132         TrySwitchState(); // always succeed since there is only one writer
00133     }
00134 }
00135 
00136 /*
00137         Use the state returned by ReadCurrentState and check that the state was not changed during the read operation.
00138         The operation is lock-free since there is no intermediate state in the write operation that could cause the
00139         read to loop forever.
00140 */
00141 void JackFrameTimer::ReadFrameTime(JackTimer* timer)
00142 {
00143     UInt16 next_index = GetCurrentIndex();
00144     UInt16 cur_index;
00145     do {
00146         cur_index = next_index;
00147         memcpy(timer, ReadCurrentState(), sizeof(JackTimer));
00148         next_index = GetCurrentIndex();
00149     } while (cur_index != next_index); // Until a coherent state has been read
00150 }
00151 
00152 // Internal
00153 
00154 void JackFrameTimer::InitFrameTimeAux(jack_time_t callback_usecs, jack_time_t period_usecs)
00155 {
00156     /* the first wakeup or post-freewheeling or post-xrun */
00157 
00158     /* There seems to be no significant difference between
00159        the two conditions OR-ed above. Incrementing the
00160        frame_time after an xrun shouldn't harm, as there 
00161        will be a discontinuity anyway. So the two are
00162        combined in this version.
00163        FA 16/03/2012 
00164     */
00165     /* Since the DLL *will* be run, next_wakeup should be the
00166        current wakeup time *without* adding the period time, as
00167        if it were computed in the previous period.
00168        FA 16/03/2012 
00169     */
00170     /* Added initialisation of timer->period_usecs, required
00171        due to the modified implementation of the DLL itself. 
00172        OTOH, this should maybe not be repeated after e.g.
00173        freewheeling or an xrun, as the current value would be
00174        more accurate than the nominal one. But it doesn't really
00175        harm either. Implementing this would require a new flag
00176        in the engine structure, to be used after freewheeling 
00177        or an xrun instead of first_wakeup. I don't know if this
00178        can be done without breaking compatibility, so I did not
00179        add this
00180        FA 13/02/2012
00181     */
00182     /* Added initialisation of timer->filter_omega. This makes 
00183        the DLL bandwidth independent of the actual period time.
00184        The bandwidth is now 1/8 Hz in all cases. The value of
00185        timer->filter_omega is 2 * pi * BW * Tperiod.
00186        FA 13/02/2012
00187     */
00188     
00189     JackTimer* timer = WriteNextStateStart();
00190     timer->fPeriodUsecs = (float)period_usecs;
00191     timer->fCurrentCallback = callback_usecs;
00192     timer->fNextWakeUp = callback_usecs;
00193     timer->fFilterOmega = period_usecs * 7.854e-7f;
00194     WriteNextStateStop();
00195     TrySwitchState(); // always succeed since there is only one writer
00196 }
00197 
00198 void JackFrameTimer::IncFrameTimeAux(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
00199 {
00200     JackTimer* timer = WriteNextStateStart();
00201     
00202     /* Modified implementation (the actual result is the same).
00203 
00204     'fSecondOrderIntegrator' is renamed to 'fPeriodUsecs'
00205     and now represents the DLL's best estimate of the 
00206     period time in microseconds (before it was a scaled
00207     version of the difference w.r.t. the nominal value).
00208     This allows this value to be made available to clients
00209     that are interested in it (see jack_get_cycle_times).
00210     This change also means that 'fPeriodUsecs' must be
00211     initialised to the nominal period time instead of zero.
00212     This is done in the first cycle in jack_run_cycle().
00213 
00214    'fFilterCoefficient' is renamed to 'fFilterOmega'. It
00215     is now equal to the 'omega' value as defined in the
00216     'Using a DLL to filter time' paper (before it was a
00217     scaled version of this value). It is computed once in
00218     jack_run_cycle() rather than set to a fixed value. This
00219     makes the DLL bandwidth independent of the period time.
00220 
00221     FA 13/02/2012
00222     */
00223     
00224     float delta = (float)((int64_t)callback_usecs - (int64_t)timer->fNextWakeUp);
00225     delta *= timer->fFilterOmega;
00226     timer->fCurrentWakeup = timer->fNextWakeUp;
00227     timer->fCurrentCallback = callback_usecs;
00228     timer->fFrames += buffer_size;
00229     timer->fPeriodUsecs += timer->fFilterOmega * delta; 
00230     timer->fNextWakeUp += (int64_t)floorf(timer->fPeriodUsecs + 1.41f * delta + 0.5f);
00231     timer->fInitialized = true;
00232     
00233     WriteNextStateStop();
00234     TrySwitchState(); // always succeed since there is only one writer
00235 }
00236 
00237 } // end of namespace
00238