mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
libavfilter/af_atempo: Avoid round-off error build-up, ticket #2484
Current method for constraining fragment position drift suffers from round-off error build up. Instead of calculating cumulative drift as a sum of input fragment position corrections, it is more accurate to calculate drift as the difference between current fragment position and the ideal position specified by the tempo scale factor. Signed-off-by: Pavel Koshevoy <pkoshevoy@gmail.com> Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
parent
5a2a060378
commit
0c77cdb491
@ -123,8 +123,9 @@ typedef struct {
|
|||||||
// tempo scaling factor:
|
// tempo scaling factor:
|
||||||
double tempo;
|
double tempo;
|
||||||
|
|
||||||
// cumulative alignment drift:
|
// a snapshot of previous fragment input and output position values
|
||||||
int drift;
|
// captured when the tempo scale factor was set most recently:
|
||||||
|
int64_t origin[2];
|
||||||
|
|
||||||
// current/previous fragment ring-buffer:
|
// current/previous fragment ring-buffer:
|
||||||
AudioFragment frag[2];
|
AudioFragment frag[2];
|
||||||
@ -159,6 +160,16 @@ static const AVOption atempo_options[] = {
|
|||||||
|
|
||||||
AVFILTER_DEFINE_CLASS(atempo);
|
AVFILTER_DEFINE_CLASS(atempo);
|
||||||
|
|
||||||
|
inline static AudioFragment *yae_curr_frag(ATempoContext *atempo)
|
||||||
|
{
|
||||||
|
return &atempo->frag[atempo->nfrag % 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static AudioFragment *yae_prev_frag(ATempoContext *atempo)
|
||||||
|
{
|
||||||
|
return &atempo->frag[(atempo->nfrag + 1) % 2];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset filter to initial state, do not deallocate existing local buffers.
|
* Reset filter to initial state, do not deallocate existing local buffers.
|
||||||
*/
|
*/
|
||||||
@ -168,13 +179,15 @@ static void yae_clear(ATempoContext *atempo)
|
|||||||
atempo->head = 0;
|
atempo->head = 0;
|
||||||
atempo->tail = 0;
|
atempo->tail = 0;
|
||||||
|
|
||||||
atempo->drift = 0;
|
|
||||||
atempo->nfrag = 0;
|
atempo->nfrag = 0;
|
||||||
atempo->state = YAE_LOAD_FRAGMENT;
|
atempo->state = YAE_LOAD_FRAGMENT;
|
||||||
|
|
||||||
atempo->position[0] = 0;
|
atempo->position[0] = 0;
|
||||||
atempo->position[1] = 0;
|
atempo->position[1] = 0;
|
||||||
|
|
||||||
|
atempo->origin[0] = 0;
|
||||||
|
atempo->origin[1] = 0;
|
||||||
|
|
||||||
atempo->frag[0].position[0] = 0;
|
atempo->frag[0].position[0] = 0;
|
||||||
atempo->frag[0].position[1] = 0;
|
atempo->frag[0].position[1] = 0;
|
||||||
atempo->frag[0].nsamples = 0;
|
atempo->frag[0].nsamples = 0;
|
||||||
@ -308,6 +321,7 @@ static int yae_reset(ATempoContext *atempo,
|
|||||||
|
|
||||||
static int yae_set_tempo(AVFilterContext *ctx, const char *arg_tempo)
|
static int yae_set_tempo(AVFilterContext *ctx, const char *arg_tempo)
|
||||||
{
|
{
|
||||||
|
const AudioFragment *prev;
|
||||||
ATempoContext *atempo = ctx->priv;
|
ATempoContext *atempo = ctx->priv;
|
||||||
char *tail = NULL;
|
char *tail = NULL;
|
||||||
double tempo = av_strtod(arg_tempo, &tail);
|
double tempo = av_strtod(arg_tempo, &tail);
|
||||||
@ -323,20 +337,13 @@ static int yae_set_tempo(AVFilterContext *ctx, const char *arg_tempo)
|
|||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prev = yae_prev_frag(atempo);
|
||||||
|
atempo->origin[0] = prev->position[0] + atempo->window / 2;
|
||||||
|
atempo->origin[1] = prev->position[1] + atempo->window / 2;
|
||||||
atempo->tempo = tempo;
|
atempo->tempo = tempo;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static AudioFragment *yae_curr_frag(ATempoContext *atempo)
|
|
||||||
{
|
|
||||||
return &atempo->frag[atempo->nfrag % 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static AudioFragment *yae_prev_frag(ATempoContext *atempo)
|
|
||||||
{
|
|
||||||
return &atempo->frag[(atempo->nfrag + 1) % 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper macro for initializing complex data buffer with scalar data
|
* A helper macro for initializing complex data buffer with scalar data
|
||||||
* of a given type.
|
* of a given type.
|
||||||
@ -689,12 +696,21 @@ static int yae_adjust_position(ATempoContext *atempo)
|
|||||||
const AudioFragment *prev = yae_prev_frag(atempo);
|
const AudioFragment *prev = yae_prev_frag(atempo);
|
||||||
AudioFragment *frag = yae_curr_frag(atempo);
|
AudioFragment *frag = yae_curr_frag(atempo);
|
||||||
|
|
||||||
|
const double prev_output_position =
|
||||||
|
(double)(prev->position[1] - atempo->origin[1] + atempo->window / 2);
|
||||||
|
|
||||||
|
const double ideal_output_position =
|
||||||
|
(double)(prev->position[0] - atempo->origin[0] + atempo->window / 2) /
|
||||||
|
atempo->tempo;
|
||||||
|
|
||||||
|
const int drift = (int)(prev_output_position - ideal_output_position);
|
||||||
|
|
||||||
const int delta_max = atempo->window / 2;
|
const int delta_max = atempo->window / 2;
|
||||||
const int correction = yae_align(frag,
|
const int correction = yae_align(frag,
|
||||||
prev,
|
prev,
|
||||||
atempo->window,
|
atempo->window,
|
||||||
delta_max,
|
delta_max,
|
||||||
atempo->drift,
|
drift,
|
||||||
atempo->correlation,
|
atempo->correlation,
|
||||||
atempo->complex_to_real);
|
atempo->complex_to_real);
|
||||||
|
|
||||||
@ -704,9 +720,6 @@ static int yae_adjust_position(ATempoContext *atempo)
|
|||||||
|
|
||||||
// clear so that the fragment can be reloaded:
|
// clear so that the fragment can be reloaded:
|
||||||
frag->nsamples = 0;
|
frag->nsamples = 0;
|
||||||
|
|
||||||
// update cumulative correction drift counter:
|
|
||||||
atempo->drift += correction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return correction;
|
return correction;
|
||||||
|
Loading…
Reference in New Issue
Block a user