mirror of
https://github.com/mpv-player/mpv.git
synced 2026-10-16 22:40:53 +08:00
audio/filter: implement lavfi-tempo leveraging lavfi's atempo/ascale
Implement `lavfi-tempo` audio filter, which uses `atempo` or `ascale` form libavfilter for setting the tempo. Users (myself included) seem to be always looking for alternative tempo scalers to try, and this provides a faster implementation (or two), and may prove useful beyond performance/speed. `atempo` is picked as a default, and as a fall-back if `ascale` is not available. Signed-off-by: Mohammad AlSaleh <CE.Mohammad.AlSaleh@gmail.com>
This commit is contained in:
committed by
Dudemanguy
parent
0279c0f07d
commit
dc8101574d
@@ -0,0 +1 @@
|
||||
implement `lavfi-tempo` an alternative speed changing audio filter
|
||||
@@ -189,6 +189,37 @@ Available filters are:
|
||||
``window-size=<amount>``
|
||||
Length in milliseconds of the overlap-and-add window. (default: 12)
|
||||
|
||||
``lavfi-tempo[=[filter=]<filter_name>]``
|
||||
Scales audio tempo using ``atempo`` or ``ascale`` filters from FFmpeg's
|
||||
libavfilter.
|
||||
|
||||
If ``ascale`` (Librempeg only) is not available in the loaded
|
||||
libavfilter, ``atempo`` will be used as a fall-back.
|
||||
|
||||
This can be used in place of ``scaletempo`` and ``scaletempo2``.
|
||||
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
``mpv --af=lavfi-tempo --speed=1.2 media.ogg``
|
||||
Would play media at 1.2x normal speed at normal pitch with tempo
|
||||
scaled by the ``atempo`` filter from lavfi.
|
||||
|
||||
``mpv --af=lavfi-tempo=atempo --speed=1.2 media.ogg``
|
||||
Same as above.
|
||||
|
||||
``mpv --af=lavfi-tempo=filter=atempo --speed=1.2 media.ogg``
|
||||
Same as above.
|
||||
|
||||
``mpv --af=lavfi-tempo=ascale --speed=1.2 media.ogg``
|
||||
Would play media at 1.2x normal speed at normal pitch with tempo
|
||||
scaled by the ``ascale`` filter from lavfi, unless the filter is
|
||||
not available, then ``atempo`` will be used as a fall-back.
|
||||
|
||||
``mpv --af=lavfi-tempo=filter=ascale --speed=1.2 media.ogg``
|
||||
Same as above.
|
||||
|
||||
|
||||
``rubberband``
|
||||
High quality pitch correction with librubberband. This can be used in place
|
||||
of ``scaletempo`` and ``scaletempo2``, and will be used to adjust audio pitch
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv 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.
|
||||
*
|
||||
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/msg.h"
|
||||
|
||||
#include "audio/aframe.h"
|
||||
#include "filters/f_lavfi.h"
|
||||
#include "filters/filter.h"
|
||||
#include "filters/filter_internal.h"
|
||||
#include "filters/frame.h"
|
||||
#include "filters/user_filters.h"
|
||||
#include "options/m_option.h"
|
||||
#include "ta/ta_talloc.h"
|
||||
|
||||
struct f_opts {
|
||||
char *filter;
|
||||
};
|
||||
|
||||
struct priv {
|
||||
struct f_opts *opts;
|
||||
bool initialized;
|
||||
bool fall_backed;
|
||||
bool lavfi_ready;
|
||||
double speed;
|
||||
double set_speed;
|
||||
double in_pts;
|
||||
struct mp_filter *lavfi_filter;
|
||||
const struct mp_filter_info *lavfi_filter_info;
|
||||
};
|
||||
|
||||
static bool set_speed(struct mp_filter *f) {
|
||||
struct priv *p = f->priv;
|
||||
|
||||
char *arg = talloc_asprintf(NULL, "%f", p->speed);
|
||||
struct mp_filter_command cmd = {
|
||||
.type = MP_FILTER_COMMAND_TEXT,
|
||||
.target = p->opts->filter,
|
||||
.cmd = "tempo",
|
||||
.arg = arg,
|
||||
};
|
||||
bool ret = p->lavfi_filter_info->command(p->lavfi_filter, &cmd);
|
||||
talloc_free(arg);
|
||||
if (!ret) {
|
||||
MP_FATAL(f, "failed to set %s=%s for lavfi filter %s\n", cmd.cmd, cmd.arg, p->opts->filter);
|
||||
return false;
|
||||
} else {
|
||||
p->set_speed = p->speed;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static bool init_lavfi_tempo(struct mp_filter *f)
|
||||
{
|
||||
struct priv *p = f->priv;
|
||||
|
||||
if (strcmp(p->opts->filter, "ascale") && strcmp(p->opts->filter, "atempo")) {
|
||||
MP_FATAL(f, "'%s' is not recognized in this context, use 'ascale' or 'atempo'.\n",
|
||||
p->opts->filter);
|
||||
return false;
|
||||
}
|
||||
|
||||
mp_assert(!p->lavfi_filter);
|
||||
|
||||
if (!mp_lavfi_is_usable(p->opts->filter, AVMEDIA_TYPE_AUDIO)) {
|
||||
MP_WARN(f, "%s filter is not available, using atempo instead.\n", p->opts->filter);
|
||||
p->opts->filter = "atempo";
|
||||
p->fall_backed = true;
|
||||
}
|
||||
|
||||
p->lavfi_filter = mp_create_user_filter(f, MP_OUTPUT_CHAIN_AUDIO, p->opts->filter, NULL);
|
||||
if (!p->lavfi_filter) {
|
||||
MP_FATAL(f, "failed to create lavfi %s filter.\n", p->opts->filter);
|
||||
return false;
|
||||
}
|
||||
|
||||
p->lavfi_filter_info = mp_filter_get_info(p->lavfi_filter);
|
||||
p->lavfi_ready = false;
|
||||
p->initialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void af_lavfi_tempo_reset(struct mp_filter *f)
|
||||
{
|
||||
struct priv *p = f->priv;
|
||||
p->in_pts = MP_NOPTS_VALUE;
|
||||
p->lavfi_ready = false;
|
||||
p->set_speed = 1.0;
|
||||
p->lavfi_filter_info->reset(p->lavfi_filter);
|
||||
|
||||
}
|
||||
|
||||
static void af_lavfi_tempo_process(struct mp_filter *f)
|
||||
{
|
||||
struct priv *p = f->priv;
|
||||
|
||||
if (!p->initialized && !init_lavfi_tempo(f))
|
||||
return;
|
||||
|
||||
if (p->lavfi_ready && p->set_speed != p->speed) {
|
||||
set_speed(f);
|
||||
}
|
||||
|
||||
if (mp_pin_can_transfer_data(p->lavfi_filter->pins[0], f->ppins[0])) {
|
||||
struct mp_frame frame = mp_pin_out_read(f->ppins[0]);
|
||||
if (frame.type == MP_FRAME_AUDIO) {
|
||||
struct mp_aframe *aframe = frame.data;
|
||||
p->in_pts = mp_aframe_get_pts(aframe);
|
||||
}
|
||||
if (!mp_pin_in_write(p->lavfi_filter->pins[0], frame)) {
|
||||
MP_FATAL(f, "failed to move frame to internal lavfi filter\n");
|
||||
} else {
|
||||
p->lavfi_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mp_pin_can_transfer_data(f->ppins[1], p->lavfi_filter->pins[1])) {
|
||||
struct mp_frame frame = mp_pin_out_read(p->lavfi_filter->pins[1]);
|
||||
if (frame.type == MP_FRAME_AUDIO) {
|
||||
struct mp_aframe *aframe = frame.data;
|
||||
mp_aframe_set_speed(aframe, p->set_speed);
|
||||
if (p->in_pts != MP_NOPTS_VALUE) {
|
||||
mp_aframe_set_pts(aframe, p->in_pts);
|
||||
}
|
||||
}
|
||||
if (!mp_pin_in_write(f->ppins[1], frame)) {
|
||||
MP_FATAL(f, "failed to move frame to internal lavfi filter\n");
|
||||
} else {
|
||||
p->lavfi_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static bool af_lavfi_tempo_command(struct mp_filter *f, struct mp_filter_command *cmd)
|
||||
{
|
||||
struct priv *p = f->priv;
|
||||
|
||||
switch (cmd->type) {
|
||||
case MP_FILTER_COMMAND_SET_SPEED:
|
||||
if (cmd->speed == p->speed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
p->speed = cmd->speed;
|
||||
return p->lavfi_ready ? set_speed(f) : true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void af_lavfi_tempo_destroy(struct mp_filter *f)
|
||||
{
|
||||
struct priv *p = f->priv;
|
||||
if (p->fall_backed) {
|
||||
// prevent ta_alloc abort since filter name is const
|
||||
p->opts->filter = NULL;
|
||||
}
|
||||
if (p->lavfi_filter_info) {
|
||||
p->lavfi_filter_info->destroy(p->lavfi_filter);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct mp_filter_info af_lavfi_tempo_filter = {
|
||||
.name = "lavfi_tempo",
|
||||
.priv_size = sizeof(struct priv),
|
||||
.process = af_lavfi_tempo_process,
|
||||
.command = af_lavfi_tempo_command,
|
||||
.reset = af_lavfi_tempo_reset,
|
||||
.destroy = af_lavfi_tempo_destroy,
|
||||
};
|
||||
|
||||
static struct mp_filter *af_lavfi_tempo_create(struct mp_filter *parent,
|
||||
void *options)
|
||||
{
|
||||
struct mp_filter *f = mp_filter_create(parent, &af_lavfi_tempo_filter);
|
||||
if (!f) {
|
||||
talloc_free(options);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mp_filter_add_pin(f, MP_PIN_IN, "in");
|
||||
mp_filter_add_pin(f, MP_PIN_OUT, "out");
|
||||
|
||||
|
||||
struct priv *p = f->priv;
|
||||
p->opts = talloc_steal(p, options);
|
||||
p->speed = 1.0;
|
||||
p->set_speed = 1.0;
|
||||
p->in_pts = MP_NOPTS_VALUE;
|
||||
|
||||
if (!init_lavfi_tempo(f)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT struct f_opts
|
||||
|
||||
const struct mp_user_filter_entry af_lavfi_tempo = {
|
||||
.desc = {
|
||||
.description = "Tempo change with lavfi ascale or atempo filters",
|
||||
.name = "lavfi-tempo",
|
||||
.priv_size = sizeof(OPT_BASE_STRUCT),
|
||||
.priv_defaults = &(const OPT_BASE_STRUCT) {
|
||||
.filter = "atempo",
|
||||
},
|
||||
.options = (const struct m_option[]) {
|
||||
{"filter", OPT_STRING(filter)},
|
||||
{0}
|
||||
},
|
||||
},
|
||||
.create = af_lavfi_tempo_create,
|
||||
};
|
||||
@@ -35,6 +35,7 @@ const struct mp_user_filter_entry *af_list[] = {
|
||||
&af_lavfi_bridge,
|
||||
&af_scaletempo,
|
||||
&af_scaletempo2,
|
||||
&af_lavfi_tempo,
|
||||
&af_format,
|
||||
#if HAVE_RUBBERBAND
|
||||
&af_rubberband,
|
||||
|
||||
@@ -22,6 +22,7 @@ extern const struct mp_user_filter_entry af_lavfi;
|
||||
extern const struct mp_user_filter_entry af_lavfi_bridge;
|
||||
extern const struct mp_user_filter_entry af_scaletempo;
|
||||
extern const struct mp_user_filter_entry af_scaletempo2;
|
||||
extern const struct mp_user_filter_entry af_lavfi_tempo;
|
||||
extern const struct mp_user_filter_entry af_format;
|
||||
extern const struct mp_user_filter_entry af_rubberband;
|
||||
extern const struct mp_user_filter_entry af_lavcac3enc;
|
||||
|
||||
@@ -65,6 +65,7 @@ sources = files(
|
||||
'audio/filter/af_drop.c',
|
||||
'audio/filter/af_format.c',
|
||||
'audio/filter/af_lavcac3enc.c',
|
||||
'audio/filter/af_lavfi_tempo.c',
|
||||
'audio/filter/af_scaletempo.c',
|
||||
'audio/filter/af_scaletempo2.c',
|
||||
'audio/filter/af_scaletempo2_internals.c',
|
||||
|
||||
Reference in New Issue
Block a user