239 lines
8.5 KiB
C++
239 lines
8.5 KiB
C++
/*
|
|
Copyright (C) 2001 Paul Davis
|
|
Copyright (C) 2004-2008 Grame
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
#include "JackFrameTimer.h"
|
|
#include "JackError.h"
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
|
|
namespace Jack
|
|
{
|
|
|
|
#if defined(WIN32) && !defined(__MINGW32__)
|
|
/* missing on Windows : see http://bugs.mysql.com/bug.php?id=15936 */
|
|
inline double rint(double nr)
|
|
{
|
|
double f = floor(nr);
|
|
double c = ceil(nr);
|
|
return (((c -nr) >= (nr - f)) ? f : c);
|
|
}
|
|
#endif
|
|
|
|
JackTimer::JackTimer()
|
|
{
|
|
fInitialized = false;
|
|
fFrames = 0;
|
|
fCurrentWakeup = 0;
|
|
fCurrentCallback = 0;
|
|
fNextWakeUp = 0;
|
|
fPeriodUsecs = 0.0f;
|
|
fFilterOmega = 0.0f; /* Initialised later */
|
|
}
|
|
|
|
jack_nframes_t JackTimer::Time2Frames(jack_time_t usecs, jack_nframes_t buffer_size)
|
|
{
|
|
if (fInitialized) {
|
|
/*
|
|
Make sure we have signed differences. It would make a lot of sense
|
|
to use the standard signed intNN_t types everywhere instead of e.g.
|
|
jack_nframes_t and jack_time_t. This would at least ensure that the
|
|
types used below are the correct ones. There is no way to get a type
|
|
that would be 'a signed version of jack_time_t' for example - the
|
|
types below are inherently fragile and there is no automatic way to
|
|
check they are the correct ones. The only way is to check manually
|
|
against jack/types.h. FA - 16/02/2012
|
|
*/
|
|
int64_t du = usecs - fCurrentWakeup;
|
|
int64_t dp = fNextWakeUp - fCurrentWakeup;
|
|
return fFrames + (int32_t)rint((double)du / (double)dp * buffer_size);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
jack_time_t JackTimer::Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size)
|
|
{
|
|
if (fInitialized) {
|
|
/*
|
|
Make sure we have signed differences. It would make a lot of sense
|
|
to use the standard signed intNN_t types everywhere instead of e.g.
|
|
jack_nframes_t and jack_time_t. This would at least ensure that the
|
|
types used below are the correct ones. There is no way to get a type
|
|
that would be 'a signed version of jack_time_t' for example - the
|
|
types below are inherently fragile and there is no automatic way to
|
|
check they are the correct ones. The only way is to check manually
|
|
against jack/types.h. FA - 16/02/2012
|
|
*/
|
|
int32_t df = frames - fFrames;
|
|
int64_t dp = fNextWakeUp - fCurrentWakeup;
|
|
return fCurrentWakeup + (int64_t)rint((double) df * (double) dp / buffer_size);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int JackTimer::GetCycleTimes(jack_nframes_t* current_frames, jack_time_t* current_usecs, jack_time_t* next_usecs, float* period_usecs)
|
|
{
|
|
if (fInitialized) {
|
|
*current_frames = fFrames;
|
|
*current_usecs = fCurrentWakeup;
|
|
*next_usecs = fNextWakeUp;
|
|
*period_usecs = fPeriodUsecs;
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
jack_nframes_t JackTimer::FramesSinceCycleStart(jack_time_t cur_time, jack_nframes_t frames_rate)
|
|
{
|
|
return (jack_nframes_t) floor((((float)frames_rate) / 1000000.0f) * (cur_time - fCurrentCallback));
|
|
}
|
|
|
|
void JackFrameTimer::InitFrameTime()
|
|
{
|
|
fFirstWakeUp = true;
|
|
}
|
|
|
|
void JackFrameTimer::IncFrameTime(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
|
|
{
|
|
if (fFirstWakeUp) {
|
|
InitFrameTimeAux(callback_usecs, period_usecs);
|
|
fFirstWakeUp = false;
|
|
}
|
|
|
|
IncFrameTimeAux(buffer_size, callback_usecs, period_usecs);
|
|
}
|
|
|
|
void JackFrameTimer::ResetFrameTime(jack_time_t callback_usecs)
|
|
{
|
|
if (!fFirstWakeUp) { // ResetFrameTime may be called by a xrun/delayed wakeup on the first cycle
|
|
JackTimer* timer = WriteNextStateStart();
|
|
timer->fCurrentWakeup = callback_usecs;
|
|
timer->fCurrentCallback = callback_usecs;
|
|
WriteNextStateStop();
|
|
TrySwitchState(); // always succeed since there is only one writer
|
|
}
|
|
}
|
|
|
|
/*
|
|
Use the state returned by ReadCurrentState and check that the state was not changed during the read operation.
|
|
The operation is lock-free since there is no intermediate state in the write operation that could cause the
|
|
read to loop forever.
|
|
*/
|
|
void JackFrameTimer::ReadFrameTime(JackTimer* timer)
|
|
{
|
|
UInt16 next_index = GetCurrentIndex();
|
|
UInt16 cur_index;
|
|
do {
|
|
cur_index = next_index;
|
|
memcpy(timer, ReadCurrentState(), sizeof(JackTimer));
|
|
next_index = GetCurrentIndex();
|
|
} while (cur_index != next_index); // Until a coherent state has been read
|
|
}
|
|
|
|
// Internal
|
|
|
|
void JackFrameTimer::InitFrameTimeAux(jack_time_t callback_usecs, jack_time_t period_usecs)
|
|
{
|
|
/* the first wakeup or post-freewheeling or post-xrun */
|
|
|
|
/* There seems to be no significant difference between
|
|
the two conditions OR-ed above. Incrementing the
|
|
frame_time after an xrun shouldn't harm, as there
|
|
will be a discontinuity anyway. So the two are
|
|
combined in this version.
|
|
FA 16/03/2012
|
|
*/
|
|
/* Since the DLL *will* be run, next_wakeup should be the
|
|
current wakeup time *without* adding the period time, as
|
|
if it were computed in the previous period.
|
|
FA 16/03/2012
|
|
*/
|
|
/* Added initialisation of timer->period_usecs, required
|
|
due to the modified implementation of the DLL itself.
|
|
OTOH, this should maybe not be repeated after e.g.
|
|
freewheeling or an xrun, as the current value would be
|
|
more accurate than the nominal one. But it doesn't really
|
|
harm either. Implementing this would require a new flag
|
|
in the engine structure, to be used after freewheeling
|
|
or an xrun instead of first_wakeup. I don't know if this
|
|
can be done without breaking compatibility, so I did not
|
|
add this
|
|
FA 13/02/2012
|
|
*/
|
|
/* Added initialisation of timer->filter_omega. This makes
|
|
the DLL bandwidth independent of the actual period time.
|
|
The bandwidth is now 1/8 Hz in all cases. The value of
|
|
timer->filter_omega is 2 * pi * BW * Tperiod.
|
|
FA 13/02/2012
|
|
*/
|
|
|
|
JackTimer* timer = WriteNextStateStart();
|
|
timer->fPeriodUsecs = (float)period_usecs;
|
|
timer->fCurrentCallback = callback_usecs;
|
|
timer->fNextWakeUp = callback_usecs;
|
|
timer->fFilterOmega = period_usecs * 7.854e-7f;
|
|
WriteNextStateStop();
|
|
TrySwitchState(); // always succeed since there is only one writer
|
|
}
|
|
|
|
void JackFrameTimer::IncFrameTimeAux(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
|
|
{
|
|
JackTimer* timer = WriteNextStateStart();
|
|
|
|
/* Modified implementation (the actual result is the same).
|
|
|
|
'fSecondOrderIntegrator' is renamed to 'fPeriodUsecs'
|
|
and now represents the DLL's best estimate of the
|
|
period time in microseconds (before it was a scaled
|
|
version of the difference w.r.t. the nominal value).
|
|
This allows this value to be made available to clients
|
|
that are interested in it (see jack_get_cycle_times).
|
|
This change also means that 'fPeriodUsecs' must be
|
|
initialised to the nominal period time instead of zero.
|
|
This is done in the first cycle in jack_run_cycle().
|
|
|
|
'fFilterCoefficient' is renamed to 'fFilterOmega'. It
|
|
is now equal to the 'omega' value as defined in the
|
|
'Using a DLL to filter time' paper (before it was a
|
|
scaled version of this value). It is computed once in
|
|
jack_run_cycle() rather than set to a fixed value. This
|
|
makes the DLL bandwidth independent of the period time.
|
|
|
|
FA 13/02/2012
|
|
*/
|
|
|
|
float delta = (float)((int64_t)callback_usecs - (int64_t)timer->fNextWakeUp);
|
|
delta *= timer->fFilterOmega;
|
|
timer->fCurrentWakeup = timer->fNextWakeUp;
|
|
timer->fCurrentCallback = callback_usecs;
|
|
timer->fFrames += buffer_size;
|
|
timer->fPeriodUsecs += timer->fFilterOmega * delta;
|
|
timer->fNextWakeUp += (int64_t)floorf(timer->fPeriodUsecs + 1.41f * delta + 0.5f);
|
|
timer->fInitialized = true;
|
|
|
|
WriteNextStateStop();
|
|
TrySwitchState(); // always succeed since there is only one writer
|
|
}
|
|
|
|
} // end of namespace
|
|
|