int64_t start_sample;
int64_t duration;
int64_t start_time;
+ double silence;
+ double unity;
int overlap;
- int cf0_eof;
- int crossfade_is_over;
+ int status[2];
+ int passthrough;
int64_t pts;
void (*fade_samples)(uint8_t **dst, uint8_t * const *src,
int nb_samples, int channels, int direction,
- int64_t start, int64_t range, int curve);
+ int64_t start, int64_t range, int curve,
+ double silence, double unity);
+ void (*scale_samples)(uint8_t **dst, uint8_t * const *src,
+ int nb_samples, int channels, double unity);
void (*crossfade_samples)(uint8_t **dst, uint8_t * const *cf0,
uint8_t * const *cf1,
int nb_samples, int channels,
int curve0, int curve1);
} AudioFadeContext;
-enum CurveType { NONE = -1, TRI, QSIN, ESIN, HSIN, LOG, IPAR, QUA, CUB, SQU, CBR, PAR, EXP, IQSIN, IHSIN, DESE, DESI, LOSI, SINC, ISINC, NB_CURVES };
+enum CurveType { NONE = -1, TRI, QSIN, ESIN, HSIN, LOG, IPAR, QUA, CUB, SQU, CBR, PAR, EXP, IQSIN, IHSIN, DESE, DESI, LOSI, SINC, ISINC, QUAT, QUATR, QSIN2, HSIN2, NB_CURVES };
#define OFFSET(x) offsetof(AudioFadeContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
AV_SAMPLE_FMT_NONE
};
-static double fade_gain(int curve, int64_t index, int64_t range)
+static double fade_gain(int curve, int64_t index, int64_t range, double silence, double unity)
{
#define CUBE(a) ((a)*(a)*(a))
double gain;
case ISINC:
gain = gain <= 0.0 ? 0.0 : 1.0 - sin(M_PI * gain) / (M_PI * gain);
break;
+ case QUAT:
+ gain = gain * gain * gain * gain;
+ break;
+ case QUATR:
+ gain = pow(gain, 0.25);
+ break;
+ case QSIN2:
+ gain = sin(gain * M_PI / 2.0) * sin(gain * M_PI / 2.0);
+ break;
+ case HSIN2:
+ gain = pow((1.0 - cos(gain * M_PI)) / 2.0, 2.0);
+ break;
case NONE:
gain = 1.0;
break;
}
- return gain;
+ return silence + (unity - silence) * gain;
}
#define FADE_PLANAR(name, type) \
static void fade_samples_## name ##p(uint8_t **dst, uint8_t * const *src, \
int nb_samples, int channels, int dir, \
- int64_t start, int64_t range, int curve) \
+ int64_t start, int64_t range,int curve,\
+ double silence, double unity) \
{ \
int i, c; \
\
for (i = 0; i < nb_samples; i++) { \
- double gain = fade_gain(curve, start + i * dir, range); \
+ double gain = fade_gain(curve, start + i * dir,range,silence,unity);\
for (c = 0; c < channels; c++) { \
type *d = (type *)dst[c]; \
const type *s = (type *)src[c]; \
#define FADE(name, type) \
static void fade_samples_## name (uint8_t **dst, uint8_t * const *src, \
int nb_samples, int channels, int dir, \
- int64_t start, int64_t range, int curve) \
+ int64_t start, int64_t range, int curve, \
+ double silence, double unity) \
{ \
type *d = (type *)dst[0]; \
const type *s = (type *)src[0]; \
int i, c, k = 0; \
\
for (i = 0; i < nb_samples; i++) { \
- double gain = fade_gain(curve, start + i * dir, range); \
+ double gain = fade_gain(curve, start + i * dir,range,silence,unity);\
for (c = 0; c < channels; c++, k++) \
d[k] = s[k] * gain; \
} \
FADE(s16, int16_t)
FADE(s32, int32_t)
+#define SCALE_PLANAR(name, type) \
+static void scale_samples_## name ##p(uint8_t **dst, uint8_t * const *src, \
+ int nb_samples, int channels, \
+ double gain) \
+{ \
+ int i, c; \
+ \
+ for (i = 0; i < nb_samples; i++) { \
+ for (c = 0; c < channels; c++) { \
+ type *d = (type *)dst[c]; \
+ const type *s = (type *)src[c]; \
+ \
+ d[i] = s[i] * gain; \
+ } \
+ } \
+}
+
+#define SCALE(name, type) \
+static void scale_samples_## name (uint8_t **dst, uint8_t * const *src, \
+ int nb_samples, int channels, double gain)\
+{ \
+ type *d = (type *)dst[0]; \
+ const type *s = (type *)src[0]; \
+ int i, c, k = 0; \
+ \
+ for (i = 0; i < nb_samples; i++) { \
+ for (c = 0; c < channels; c++, k++) \
+ d[k] = s[k] * gain; \
+ } \
+}
+
+SCALE_PLANAR(dbl, double)
+SCALE_PLANAR(flt, float)
+SCALE_PLANAR(s16, int16_t)
+SCALE_PLANAR(s32, int32_t)
+
+SCALE(dbl, double)
+SCALE(flt, float)
+SCALE(s16, int16_t)
+SCALE(s32, int32_t)
+
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AudioFadeContext *s = ctx->priv;
switch (outlink->format) {
- case AV_SAMPLE_FMT_DBL: s->fade_samples = fade_samples_dbl; break;
- case AV_SAMPLE_FMT_DBLP: s->fade_samples = fade_samples_dblp; break;
- case AV_SAMPLE_FMT_FLT: s->fade_samples = fade_samples_flt; break;
- case AV_SAMPLE_FMT_FLTP: s->fade_samples = fade_samples_fltp; break;
- case AV_SAMPLE_FMT_S16: s->fade_samples = fade_samples_s16; break;
- case AV_SAMPLE_FMT_S16P: s->fade_samples = fade_samples_s16p; break;
- case AV_SAMPLE_FMT_S32: s->fade_samples = fade_samples_s32; break;
- case AV_SAMPLE_FMT_S32P: s->fade_samples = fade_samples_s32p; break;
+ case AV_SAMPLE_FMT_DBL: s->fade_samples = fade_samples_dbl;
+ s->scale_samples = scale_samples_dbl;
+ break;
+ case AV_SAMPLE_FMT_DBLP: s->fade_samples = fade_samples_dblp;
+ s->scale_samples = scale_samples_dblp;
+ break;
+ case AV_SAMPLE_FMT_FLT: s->fade_samples = fade_samples_flt;
+ s->scale_samples = scale_samples_flt;
+ break;
+ case AV_SAMPLE_FMT_FLTP: s->fade_samples = fade_samples_fltp;
+ s->scale_samples = scale_samples_fltp;
+ break;
+ case AV_SAMPLE_FMT_S16: s->fade_samples = fade_samples_s16;
+ s->scale_samples = scale_samples_s16;
+ break;
+ case AV_SAMPLE_FMT_S16P: s->fade_samples = fade_samples_s16p;
+ s->scale_samples = scale_samples_s16p;
+ break;
+ case AV_SAMPLE_FMT_S32: s->fade_samples = fade_samples_s32;
+ s->scale_samples = scale_samples_s32;
+ break;
+ case AV_SAMPLE_FMT_S32P: s->fade_samples = fade_samples_s32p;
+ s->scale_samples = scale_samples_s32p;
+ break;
}
if (s->duration)
{ "losi", "logistic sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = LOSI }, 0, 0, TFLAGS, "curve" },
{ "sinc", "sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = SINC }, 0, 0, TFLAGS, "curve" },
{ "isinc", "inverted sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = ISINC}, 0, 0, TFLAGS, "curve" },
+ { "quat", "quartic", 0, AV_OPT_TYPE_CONST, {.i64 = QUAT }, 0, 0, TFLAGS, "curve" },
+ { "quatr", "quartic root", 0, AV_OPT_TYPE_CONST, {.i64 = QUATR}, 0, 0, TFLAGS, "curve" },
+ { "qsin2", "squared quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN2}, 0, 0, TFLAGS, "curve" },
+ { "hsin2", "squared half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN2}, 0, 0, TFLAGS, "curve" },
+ { "silence", "set the silence gain", OFFSET(silence), AV_OPT_TYPE_DOUBLE, {.dbl = 0 }, 0, 1, TFLAGS },
+ { "unity", "set the unity gain", OFFSET(unity), AV_OPT_TYPE_DOUBLE, {.dbl = 1 }, 0, 1, TFLAGS },
{ NULL }
};
AVFrame *out_buf;
int64_t cur_sample = av_rescale_q(buf->pts, inlink->time_base, (AVRational){1, inlink->sample_rate});
- if ((!s->type && (s->start_sample + s->nb_samples < cur_sample)) ||
- ( s->type && (cur_sample + nb_samples < s->start_sample)))
+ if (s->unity == 1.0 &&
+ ((!s->type && (s->start_sample + s->nb_samples < cur_sample)) ||
+ ( s->type && (cur_sample + nb_samples < s->start_sample))))
return ff_filter_frame(outlink, buf);
if (av_frame_is_writable(buf)) {
if ((!s->type && (cur_sample + nb_samples < s->start_sample)) ||
( s->type && (s->start_sample + s->nb_samples < cur_sample))) {
- av_samples_set_silence(out_buf->extended_data, 0, nb_samples,
- out_buf->ch_layout.nb_channels, out_buf->format);
+ if (s->silence == 0.) {
+ av_samples_set_silence(out_buf->extended_data, 0, nb_samples,
+ out_buf->ch_layout.nb_channels, out_buf->format);
+ } else {
+ s->scale_samples(out_buf->extended_data, buf->extended_data,
+ nb_samples, buf->ch_layout.nb_channels,
+ s->silence);
+ }
+ } else if (( s->type && (cur_sample + nb_samples < s->start_sample)) ||
+ (!s->type && (s->start_sample + s->nb_samples < cur_sample))) {
+ s->scale_samples(out_buf->extended_data, buf->extended_data,
+ nb_samples, buf->ch_layout.nb_channels,
+ s->unity);
} else {
int64_t start;
s->fade_samples(out_buf->extended_data, buf->extended_data,
nb_samples, buf->ch_layout.nb_channels,
s->type ? -1 : 1, start,
- s->nb_samples, s->curve);
+ s->nb_samples, s->curve, s->silence, s->unity);
}
if (buf != out_buf)
{ "losi", "logistic sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = LOSI }, 0, 0, FLAGS, "curve" },
{ "sinc", "sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = SINC }, 0, 0, FLAGS, "curve" },
{ "isinc", "inverted sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = ISINC}, 0, 0, FLAGS, "curve" },
+ { "quat", "quartic", 0, AV_OPT_TYPE_CONST, {.i64 = QUAT }, 0, 0, FLAGS, "curve" },
+ { "quatr", "quartic root", 0, AV_OPT_TYPE_CONST, {.i64 = QUATR}, 0, 0, FLAGS, "curve" },
+ { "qsin2", "squared quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN2}, 0, 0, FLAGS, "curve" },
+ { "hsin2", "squared half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN2}, 0, 0, FLAGS, "curve" },
{ "curve2", "set fade curve type for 2nd stream", OFFSET(curve2), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, FLAGS, "curve" },
{ "c2", "set fade curve type for 2nd stream", OFFSET(curve2), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, FLAGS, "curve" },
{ NULL }
int i, c; \
\
for (i = 0; i < nb_samples; i++) { \
- double gain0 = fade_gain(curve0, nb_samples - 1 - i, nb_samples); \
- double gain1 = fade_gain(curve1, i, nb_samples); \
+ double gain0 = fade_gain(curve0, nb_samples - 1 - i, nb_samples,0.,1.);\
+ double gain1 = fade_gain(curve1, i, nb_samples, 0., 1.); \
for (c = 0; c < channels; c++) { \
type *d = (type *)dst[c]; \
const type *s0 = (type *)cf0[c]; \
int i, c, k = 0; \
\
for (i = 0; i < nb_samples; i++) { \
- double gain0 = fade_gain(curve0, nb_samples - 1 - i, nb_samples); \
- double gain1 = fade_gain(curve1, i, nb_samples); \
+ double gain0 = fade_gain(curve0, nb_samples - 1-i,nb_samples,0.,1.);\
+ double gain1 = fade_gain(curve1, i, nb_samples, 0., 1.); \
for (c = 0; c < channels; c++, k++) \
d[k] = s0[k] * gain0 + s1[k] * gain1; \
} \
CROSSFADE(s16, int16_t)
CROSSFADE(s32, int32_t)
+static int check_input(AVFilterLink *inlink)
+{
+ const int queued_samples = ff_inlink_queued_samples(inlink);
+
+ return ff_inlink_check_available_samples(inlink, queued_samples + 1) == 1;
+}
+
static int activate(AVFilterContext *ctx)
{
AudioFadeContext *s = ctx->priv;
FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx);
- if (s->crossfade_is_over) {
+ if (s->passthrough && s->status[0]) {
ret = ff_inlink_consume_frame(ctx->inputs[1], &in);
if (ret > 0) {
in->pts = s->pts;
} else if (ret < 0) {
return ret;
} else if (ff_inlink_acknowledge_status(ctx->inputs[1], &status, &pts)) {
- ff_outlink_set_status(ctx->outputs[0], status, pts);
+ ff_outlink_set_status(outlink, status, pts);
return 0;
} else if (!ret) {
- if (ff_outlink_frame_wanted(ctx->outputs[0])) {
+ if (ff_outlink_frame_wanted(outlink)) {
ff_inlink_request_frame(ctx->inputs[1]);
return 0;
}
nb_samples = ff_inlink_queued_samples(ctx->inputs[0]);
if (nb_samples > s->nb_samples) {
nb_samples -= s->nb_samples;
+ s->passthrough = 1;
ret = ff_inlink_consume_samples(ctx->inputs[0], nb_samples, nb_samples, &in);
if (ret < 0)
return ret;
s->pts += av_rescale_q(in->nb_samples,
(AVRational){ 1, outlink->sample_rate }, outlink->time_base);
return ff_filter_frame(outlink, in);
- } else if (s->cf0_eof && nb_samples >= s->nb_samples &&
+ } else if (s->status[0] && nb_samples >= s->nb_samples &&
ff_inlink_queued_samples(ctx->inputs[1]) >= s->nb_samples) {
if (s->overlap) {
out = ff_get_audio_buffer(outlink, s->nb_samples);
out->pts = s->pts;
s->pts += av_rescale_q(s->nb_samples,
(AVRational){ 1, outlink->sample_rate }, outlink->time_base);
- s->crossfade_is_over = 1;
+ s->passthrough = 1;
av_frame_free(&cf[0]);
av_frame_free(&cf[1]);
return ff_filter_frame(outlink, out);
}
s->fade_samples(out->extended_data, cf[0]->extended_data, s->nb_samples,
- outlink->ch_layout.nb_channels, -1, s->nb_samples - 1, s->nb_samples, s->curve);
+ outlink->ch_layout.nb_channels, -1, s->nb_samples - 1, s->nb_samples, s->curve, 0., 1.);
out->pts = s->pts;
s->pts += av_rescale_q(s->nb_samples,
(AVRational){ 1, outlink->sample_rate }, outlink->time_base);
}
s->fade_samples(out->extended_data, cf[1]->extended_data, s->nb_samples,
- outlink->ch_layout.nb_channels, 1, 0, s->nb_samples, s->curve2);
+ outlink->ch_layout.nb_channels, 1, 0, s->nb_samples, s->curve2, 0., 1.);
out->pts = s->pts;
s->pts += av_rescale_q(s->nb_samples,
(AVRational){ 1, outlink->sample_rate }, outlink->time_base);
- s->crossfade_is_over = 1;
+ s->passthrough = 1;
av_frame_free(&cf[1]);
return ff_filter_frame(outlink, out);
}
- } else if (ff_outlink_frame_wanted(ctx->outputs[0])) {
- if (!s->cf0_eof && ff_outlink_get_status(ctx->inputs[0])) {
- s->cf0_eof = 1;
- }
- if (ff_outlink_get_status(ctx->inputs[1])) {
- ff_outlink_set_status(ctx->outputs[0], AVERROR_EOF, AV_NOPTS_VALUE);
+ } else if (ff_outlink_frame_wanted(outlink)) {
+ if (!s->status[0] && check_input(ctx->inputs[0]))
+ s->status[0] = AVERROR_EOF;
+ s->passthrough = !s->status[0];
+ if (check_input(ctx->inputs[1])) {
+ s->status[1] = AVERROR_EOF;
+ ff_outlink_set_status(outlink, AVERROR_EOF, AV_NOPTS_VALUE);
return 0;
}
- if (!s->cf0_eof)
+ if (!s->status[0])
ff_inlink_request_frame(ctx->inputs[0]);
else
ff_inlink_request_frame(ctx->inputs[1]);
return 0;
}
+static AVFrame *get_audio_buffer(AVFilterLink *inlink, int nb_samples)
+{
+ AVFilterContext *ctx = inlink->dst;
+ AudioFadeContext *s = ctx->priv;
+
+ return s->passthrough ?
+ ff_null_get_audio_buffer (inlink, nb_samples) :
+ ff_default_get_audio_buffer(inlink, nb_samples);
+}
+
static const AVFilterPad avfilter_af_acrossfade_inputs[] = {
{
.name = "crossfade0",
.type = AVMEDIA_TYPE_AUDIO,
+ .get_buffer.audio = get_audio_buffer,
},
{
.name = "crossfade1",
.type = AVMEDIA_TYPE_AUDIO,
+ .get_buffer.audio = get_audio_buffer,
},
};