New jack_get_cycle_times() implementation from Fons Adriennsen.
This commit is contained in:
parent
94cb2ee9a1
commit
6483c0d5eb
|
@ -180,10 +180,15 @@ extern "C"
|
|||
LIB_EXPORT int jack_engine_takeover_timebase(jack_client_t *);
|
||||
LIB_EXPORT jack_nframes_t jack_frames_since_cycle_start(const jack_client_t *);
|
||||
LIB_EXPORT jack_time_t jack_get_time();
|
||||
LIB_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t time);
|
||||
LIB_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usecs);
|
||||
LIB_EXPORT jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames);
|
||||
LIB_EXPORT jack_nframes_t jack_frame_time(const jack_client_t *);
|
||||
LIB_EXPORT jack_nframes_t jack_last_frame_time(const jack_client_t *client);
|
||||
LIB_EXPORT int jack_get_cycle_times(const jack_client_t *client,
|
||||
jack_nframes_t *current_frames,
|
||||
jack_time_t *current_usecs,
|
||||
jack_time_t *next_usecs,
|
||||
float *period_usecs);
|
||||
LIB_EXPORT float jack_cpu_load(jack_client_t *client);
|
||||
LIB_EXPORT jack_native_thread_t jack_client_thread_id(jack_client_t *);
|
||||
LIB_EXPORT void jack_set_error_function(print_function);
|
||||
|
@ -1331,7 +1336,7 @@ LIB_EXPORT jack_time_t jack_frames_to_time(const jack_client_t* ext_client, jack
|
|||
}
|
||||
}
|
||||
|
||||
LIB_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t* ext_client, jack_time_t time)
|
||||
LIB_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t* ext_client, jack_time_t usecs)
|
||||
{
|
||||
JackGlobals::CheckContext("jack_time_to_frames");
|
||||
|
||||
|
@ -1344,7 +1349,7 @@ LIB_EXPORT jack_nframes_t jack_time_to_frames(const jack_client_t* ext_client, j
|
|||
JackEngineControl* control = GetEngineControl();
|
||||
if (control) {
|
||||
control->ReadFrameTime(&timer);
|
||||
return timer.Time2Frames(time, control->fBufferSize);
|
||||
return timer.Time2Frames(usecs, control->fBufferSize);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1366,6 +1371,24 @@ LIB_EXPORT jack_nframes_t jack_last_frame_time(const jack_client_t* ext_client)
|
|||
return (control) ? control->fFrameTimer.ReadCurrentState()->CurFrame() : 0;
|
||||
}
|
||||
|
||||
LIB_EXPORT int jack_get_cycle_times(const jack_client_t *client,
|
||||
jack_nframes_t *current_frames,
|
||||
jack_time_t *current_usecs,
|
||||
jack_time_t *next_usecs,
|
||||
float *period_usecs)
|
||||
{
|
||||
JackGlobals::CheckContext("jack_get_cycle_times");
|
||||
|
||||
JackEngineControl* control = GetEngineControl();
|
||||
if (control) {
|
||||
JackTimer timer;
|
||||
control->ReadFrameTime(&timer);
|
||||
return timer.GetCycleTimes(current_frames, current_usecs, next_usecs, period_usecs);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
LIB_EXPORT float jack_cpu_load(jack_client_t* ext_client)
|
||||
{
|
||||
JackGlobals::CheckContext("jack_cpu_load");
|
||||
|
|
|
@ -97,10 +97,11 @@ void JackEngineControl::ResetRollingUsecs()
|
|||
|
||||
void JackEngineControl::NotifyXRun(jack_time_t callback_usecs, float delayed_usecs)
|
||||
{
|
||||
ResetFrameTime(callback_usecs);
|
||||
ResetFrameTime(callback_usecs); // Is this still necessary? SL 03/21/2012
|
||||
fXrunDelayedUsecs = delayed_usecs;
|
||||
if (delayed_usecs > fMaxDelayedUsecs)
|
||||
if (delayed_usecs > fMaxDelayedUsecs) {
|
||||
fMaxDelayedUsecs = delayed_usecs;
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace
|
||||
|
|
|
@ -43,14 +43,26 @@ JackTimer::JackTimer()
|
|||
fCurrentWakeup = 0;
|
||||
fCurrentCallback = 0;
|
||||
fNextWakeUp = 0;
|
||||
fFilterCoefficient = 0.01f;
|
||||
fSecondOrderIntegrator = 0.0f;
|
||||
fPeriodUsecs = 0.0f;
|
||||
fFilterOmega = 0.0f; /* Initialised later */
|
||||
}
|
||||
|
||||
jack_nframes_t JackTimer::Time2Frames(jack_time_t time, jack_nframes_t buffer_size)
|
||||
jack_nframes_t JackTimer::Time2Frames(jack_time_t usecs, jack_nframes_t buffer_size)
|
||||
{
|
||||
if (fInitialized) {
|
||||
return fFrames + (long)rint(((double) ((long long)(time - fCurrentWakeup)) / ((long long)(fNextWakeUp - fCurrentWakeup))) * buffer_size);
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
@ -59,12 +71,37 @@ jack_nframes_t JackTimer::Time2Frames(jack_time_t time, jack_nframes_t buffer_si
|
|||
jack_time_t JackTimer::Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size)
|
||||
{
|
||||
if (fInitialized) {
|
||||
return fCurrentWakeup + (long)rint(((double) ((long long)(frames - fFrames)) * ((long long)(fNextWakeUp - fCurrentWakeup))) / buffer_size);
|
||||
/*
|
||||
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));
|
||||
|
@ -80,9 +117,9 @@ void JackFrameTimer::IncFrameTime(jack_nframes_t buffer_size, jack_time_t callba
|
|||
if (fFirstWakeUp) {
|
||||
InitFrameTimeAux(callback_usecs, period_usecs);
|
||||
fFirstWakeUp = false;
|
||||
} else {
|
||||
IncFrameTimeAux(buffer_size, callback_usecs, period_usecs);
|
||||
}
|
||||
|
||||
IncFrameTimeAux(buffer_size, callback_usecs, period_usecs);
|
||||
}
|
||||
|
||||
void JackFrameTimer::ResetFrameTime(jack_nframes_t frames_rate, jack_time_t callback_usecs, jack_time_t period_usecs)
|
||||
|
@ -119,10 +156,44 @@ void JackFrameTimer::ReadFrameTime(JackTimer* timer)
|
|||
|
||||
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->fSecondOrderIntegrator = 0.0f;
|
||||
timer->fPeriodUsecs = (float)period_usecs;
|
||||
timer->fCurrentCallback = callback_usecs;
|
||||
timer->fNextWakeUp = callback_usecs + period_usecs;
|
||||
timer->fNextWakeUp = callback_usecs;
|
||||
timer->fFilterOmega = period_usecs * 7.854e-7f;
|
||||
WriteNextStateStop();
|
||||
TrySwitchState(); // always succeed since there is only one writer
|
||||
}
|
||||
|
@ -130,13 +201,38 @@ void JackFrameTimer::InitFrameTimeAux(jack_time_t callback_usecs, jack_time_t pe
|
|||
void JackFrameTimer::IncFrameTimeAux(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
|
||||
{
|
||||
JackTimer* timer = WriteNextStateStart();
|
||||
float delta = (int64_t)callback_usecs - (int64_t)timer->fNextWakeUp;
|
||||
|
||||
/* 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->fSecondOrderIntegrator += 0.5f * timer->fFilterCoefficient * delta;
|
||||
timer->fNextWakeUp = timer->fCurrentWakeup + period_usecs + (int64_t) floorf((timer->fFilterCoefficient * (delta + timer->fSecondOrderIntegrator)));
|
||||
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
|
||||
}
|
||||
|
|
|
@ -44,8 +44,8 @@ class SERVER_EXPORT JackTimer
|
|||
jack_time_t fCurrentWakeup;
|
||||
jack_time_t fCurrentCallback;
|
||||
jack_time_t fNextWakeUp;
|
||||
float fSecondOrderIntegrator;
|
||||
float fFilterCoefficient; /* set once, never altered */
|
||||
float fPeriodUsecs;
|
||||
float fFilterOmega; /* set once, never altered */
|
||||
bool fInitialized;
|
||||
|
||||
public:
|
||||
|
@ -57,6 +57,7 @@ class SERVER_EXPORT JackTimer
|
|||
jack_nframes_t Time2Frames(jack_time_t time, jack_nframes_t buffer_size);
|
||||
jack_time_t Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size);
|
||||
jack_nframes_t FramesSinceCycleStart(jack_time_t cur_time, jack_nframes_t frames_rate);
|
||||
int GetCycleTimes(jack_nframes_t* current_frames, jack_time_t* current_usecs, jack_time_t* next_usecs, float* period_usecs);
|
||||
|
||||
jack_nframes_t CurFrame()
|
||||
{
|
||||
|
|
|
@ -1284,6 +1284,55 @@ jack_nframes_t jack_frame_time (const jack_client_t *) JACK_OPTIONAL_WEAK_EXPORT
|
|||
*/
|
||||
jack_nframes_t jack_last_frame_time (const jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT;
|
||||
|
||||
/**
|
||||
* This function may only be used from the process callback.
|
||||
* It provides the internal cycle timing information as used by
|
||||
* most of the other time related functions. This allows the
|
||||
* caller to map between frame counts and microseconds with full
|
||||
* precision (i.e. without rounding frame times to integers),
|
||||
* and also provides e.g. the microseconds time of the start of
|
||||
* the current cycle directly (it has to be computed otherwise).
|
||||
*
|
||||
* If the return value is zero, the following information is
|
||||
* provided in the variables pointed to by the arguments:
|
||||
*
|
||||
* current_frames: the frame time counter at the start of the
|
||||
* current cycle, same as jack_last_frame_time().
|
||||
* current_usecs: the microseconds time at the start of the
|
||||
* current cycle.
|
||||
* next_usecs: the microseconds time of the start of the next
|
||||
* next cycle as computed by the DLL.
|
||||
* period_usecs: the current best estimate of the period time in
|
||||
* microseconds.
|
||||
*
|
||||
* NOTES:
|
||||
*
|
||||
* Because of the types used, all the returned values except period_usecs
|
||||
* are unsigned. In computations mapping between frames and microseconds
|
||||
* *signed* differences are required. The easiest way is to compute those
|
||||
* separately and assign them to the appropriate signed variables,
|
||||
* int32_t for frames and int64_t for usecs. See the implementation of
|
||||
* jack_frames_to_time() and Jack_time_to_frames() for an example.
|
||||
*
|
||||
* Unless there was an xrun, skipped cycles, or the current cycle is the
|
||||
* first after freewheeling or starting Jack, the value of current_usecs
|
||||
* will always be the value of next_usecs of the previous cycle.
|
||||
*
|
||||
* The value of period_usecs will in general NOT be exactly equal to
|
||||
* the difference of next_usecs and current_usecs. This is because to
|
||||
* ensure stability of the DLL and continuity of the mapping, a fraction
|
||||
* of the loop error must be included in next_usecs. For an accurate
|
||||
* mapping between frames and microseconds, the difference of next_usecs
|
||||
* and current_usecs should be used, and not period_usecs.
|
||||
*
|
||||
* @return zero if OK, non-zero otherwise.
|
||||
*/
|
||||
int jack_get_cycle_times(const jack_client_t *client,
|
||||
jack_nframes_t *current_frames,
|
||||
jack_time_t *current_usecs,
|
||||
jack_time_t *next_usecs,
|
||||
float *period_usecs) JACK_OPTIONAL_WEAK_EXPORT;
|
||||
|
||||
/**
|
||||
* @return the estimated time in microseconds of the specified frame time
|
||||
*/
|
||||
|
|
|
@ -444,7 +444,7 @@ static void* jack_thread(void *arg)
|
|||
jack_nframes_t last_thread_time = jack_frame_time(client);
|
||||
|
||||
while (1) {
|
||||
jack_nframes_t frames = jack_cycle_wait (client);
|
||||
jack_nframes_t frames = jack_cycle_wait(client);
|
||||
jack_nframes_t current_thread_time = jack_frame_time(client);
|
||||
jack_nframes_t delta_time = current_thread_time - last_thread_time;
|
||||
Log("jack_thread : delta_time = %ld\n", delta_time);
|
||||
|
@ -489,6 +489,36 @@ int process4(jack_nframes_t nframes, void *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int process5(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
jack_client_t* client = (jack_client_t*) arg;
|
||||
|
||||
static jack_nframes_t first_current_frames;
|
||||
static jack_time_t first_current_usecs;
|
||||
static jack_time_t first_next_usecs;
|
||||
static float first_period_usecs;
|
||||
static int res1 = jack_get_cycle_times(client, &first_current_frames, &first_current_usecs, &first_next_usecs, &first_period_usecs);
|
||||
|
||||
jack_nframes_t current_frames;
|
||||
jack_time_t current_usecs;
|
||||
jack_time_t next_usecs;
|
||||
float period_usecs;
|
||||
|
||||
int res = jack_get_cycle_times(client, ¤t_frames, ¤t_usecs, &next_usecs, &period_usecs);
|
||||
if (res != 0) {
|
||||
printf("!!! ERROR !!! jack_get_cycle_times fails...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Log("calling process5 callback : jack_get_cycle_times delta current_frames = %ld delta current_usecs = %ld delta next_usecs = %ld period_usecs = %f\n",
|
||||
current_frames - first_current_frames, current_usecs - first_current_usecs, next_usecs - first_next_usecs, period_usecs);
|
||||
|
||||
first_current_frames = current_frames;
|
||||
first_current_usecs = current_usecs;
|
||||
first_next_usecs = next_usecs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void display_transport_state()
|
||||
{
|
||||
jack_transport_state_t ts;
|
||||
|
@ -1967,7 +1997,16 @@ int main (int argc, char *argv[])
|
|||
jack_set_process_callback(client1, process4, client1);
|
||||
jack_activate(client1);
|
||||
jack_sleep(2 * 1000);
|
||||
|
||||
|
||||
/**
|
||||
* Checking jack_get_cycle_times.
|
||||
*/
|
||||
Log("Testing jack_get_cycle_times...\n");
|
||||
jack_deactivate(client1);
|
||||
jack_set_process_callback(client1, process5, client1);
|
||||
jack_activate(client1);
|
||||
jack_sleep(3 * 1000);
|
||||
|
||||
|
||||
/**
|
||||
* Checking alternate thread model
|
||||
|
|
Loading…
Reference in New Issue