Jack2 1.9.6

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     fFilterCoefficient = 0.01f;
00047     fSecondOrderIntegrator = 0.0f;
00048 }
00049 
00050 jack_nframes_t JackTimer::Time2Frames(jack_time_t time, jack_nframes_t buffer_size)
00051 {
00052     if (fInitialized) {
00053         return fFrames + (long)rint(((double) ((long long)(time - fCurrentWakeup)) / ((long long)(fNextWakeUp - fCurrentWakeup))) * buffer_size);
00054     } else {
00055         return 0;
00056     }
00057 }
00058 
00059 jack_time_t JackTimer::Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size)
00060 {
00061     if (fInitialized) {
00062         return fCurrentWakeup + (long)rint(((double) ((long long)(frames - fFrames)) * ((long long)(fNextWakeUp - fCurrentWakeup))) / buffer_size);
00063     } else {
00064         return 0;
00065     }
00066 }
00067 
00068 jack_nframes_t JackTimer::FramesSinceCycleStart(jack_time_t cur_time, jack_nframes_t frames_rate)
00069 {
00070     return (jack_nframes_t) floor((((float)frames_rate) / 1000000.0f) * (cur_time - fCurrentCallback));
00071 }
00072 
00073 void JackFrameTimer::InitFrameTime()
00074 {
00075     fFirstWakeUp = true;
00076 }
00077 
00078 void JackFrameTimer::IncFrameTime(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
00079 {
00080     if (fFirstWakeUp) {
00081         InitFrameTimeAux(callback_usecs, period_usecs);
00082         fFirstWakeUp = false;
00083     } else {
00084         IncFrameTimeAux(buffer_size, callback_usecs, period_usecs);
00085     }
00086 }
00087 
00088 void JackFrameTimer::ResetFrameTime(jack_nframes_t frames_rate, jack_time_t callback_usecs, jack_time_t period_usecs)
00089 {
00090     if (!fFirstWakeUp) { // ResetFrameTime may be called by a xrun/delayed wakeup on the first cycle
00091         JackTimer* timer = WriteNextStateStart();
00092         jack_nframes_t period_size_guess = (jack_nframes_t)(frames_rate * ((timer->fNextWakeUp - timer->fCurrentWakeup) / 1000000.0));
00093         timer->fFrames += ((callback_usecs - timer->fNextWakeUp) / period_size_guess) * period_size_guess;
00094         timer->fCurrentWakeup = callback_usecs;
00095         timer->fCurrentCallback = callback_usecs;
00096         timer->fNextWakeUp = callback_usecs + period_usecs;
00097         WriteNextStateStop();
00098         TrySwitchState(); // always succeed since there is only one writer
00099     }
00100 }
00101 
00102 /*
00103         Use the state returned by ReadCurrentState and check that the state was not changed during the read operation.
00104         The operation is lock-free since there is no intermediate state in the write operation that could cause the
00105         read to loop forever.
00106 */
00107 void JackFrameTimer::ReadFrameTime(JackTimer* timer)
00108 {
00109     UInt16 next_index = GetCurrentIndex();
00110     UInt16 cur_index;
00111     do {
00112         cur_index = next_index;
00113         memcpy(timer, ReadCurrentState(), sizeof(JackTimer));
00114         next_index = GetCurrentIndex();
00115     } while (cur_index != next_index); // Until a coherent state has been read
00116 }
00117 
00118 // Internal
00119 
00120 void JackFrameTimer::InitFrameTimeAux(jack_time_t callback_usecs, jack_time_t period_usecs)
00121 {
00122     JackTimer* timer = WriteNextStateStart();
00123     timer->fSecondOrderIntegrator = 0.0f;
00124     timer->fCurrentCallback = callback_usecs;
00125     timer->fNextWakeUp = callback_usecs + period_usecs;
00126     WriteNextStateStop();
00127     TrySwitchState(); // always succeed since there is only one writer
00128 }
00129 
00130 void JackFrameTimer::IncFrameTimeAux(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
00131 {
00132     JackTimer* timer = WriteNextStateStart();
00133     float delta = (int64_t)callback_usecs - (int64_t)timer->fNextWakeUp;
00134     timer->fCurrentWakeup = timer->fNextWakeUp;
00135     timer->fCurrentCallback = callback_usecs;
00136     timer->fFrames += buffer_size;
00137     timer->fSecondOrderIntegrator += 0.5f * timer->fFilterCoefficient * delta;
00138     timer->fNextWakeUp = timer->fCurrentWakeup + period_usecs + (int64_t) floorf((timer->fFilterCoefficient * (delta + timer->fSecondOrderIntegrator)));
00139     timer->fInitialized = true;
00140     WriteNextStateStop();
00141     TrySwitchState(); // always succeed since there is only one writer
00142 }
00143 
00144 } // end of namespace
00145