summaryrefslogtreecommitdiffstats
path: root/audio/filter/af_scaletempo.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/filter/af_scaletempo.c')
-rw-r--r--audio/filter/af_scaletempo.c577
1 files changed, 344 insertions, 233 deletions
diff --git a/audio/filter/af_scaletempo.c b/audio/filter/af_scaletempo.c
index 0499631ea9..4e48e6168b 100644
--- a/audio/filter/af_scaletempo.c
+++ b/audio/filter/af_scaletempo.c
@@ -35,14 +35,32 @@
#include <limits.h>
#include <assert.h>
+#include "audio/aframe.h"
+#include "audio/format.h"
#include "common/common.h"
-
-#include "af.h"
+#include "filters/f_autoconvert.h"
+#include "filters/filter_internal.h"
+#include "filters/user_filters.h"
#include "options/m_option.h"
-// Data for specific instances of this filter
-typedef struct af_scaletempo_s
-{
+struct f_opts {
+ float scale_nominal;
+ float ms_stride;
+ float ms_search;
+ float percent_overlap;
+#define SCALE_TEMPO 1
+#define SCALE_PITCH 2
+ int speed_opt;
+};
+
+struct priv {
+ struct f_opts *opts;
+
+ struct mp_pin *in_pin;
+ struct mp_aframe *cur_format;
+ struct mp_aframe_pool *out_pool;
+ double current_pts;
+
// stride
float scale;
float speed;
@@ -62,28 +80,21 @@ typedef struct af_scaletempo_s
int bytes_standing;
void *buf_overlap;
void *table_blend;
- void (*output_overlap)(struct af_scaletempo_s *s, void *out_buf,
+ void (*output_overlap)(struct priv *s, void *out_buf,
int bytes_off);
// best overlap
int frames_search;
int num_channels;
void *buf_pre_corr;
void *table_window;
- int (*best_overlap_offset)(struct af_scaletempo_s *s);
- // command line
- float scale_nominal;
- float ms_stride;
- float percent_overlap;
- float ms_search;
-#define SCALE_TEMPO 1
-#define SCALE_PITCH 2
- int speed_opt;
-} af_scaletempo_t;
+ int (*best_overlap_offset)(struct priv *s);
+};
-static int fill_queue(struct af_instance *af, struct mp_audio *data, int offset)
+static bool reinit(struct mp_filter *f, struct mp_aframe *in);
+
+static int fill_queue(struct priv *s, struct mp_aframe *in, int offset)
{
- af_scaletempo_t *s = af->priv;
- int bytes_in = (data ? mp_audio_psize(data) : 0) - offset;
+ int bytes_in = in ? mp_aframe_get_size(in) * s->bytes_per_frame - offset : 0;
int offset_unchanged = offset;
if (s->bytes_to_slide > 0) {
@@ -106,8 +117,8 @@ static int fill_queue(struct af_instance *af, struct mp_audio *data, int offset)
if (bytes_in > 0) {
int bytes_copy = MPMIN(s->bytes_queue - s->bytes_queued, bytes_in);
assert(bytes_copy >= 0);
- memcpy(s->buf_queue + s->bytes_queued,
- (int8_t *)data->planes[0] + offset, bytes_copy);
+ uint8_t **planes = mp_aframe_get_data_ro(in);
+ memcpy(s->buf_queue + s->bytes_queued, planes[0] + offset, bytes_copy);
s->bytes_queued += bytes_copy;
offset += bytes_copy;
}
@@ -117,7 +128,7 @@ static int fill_queue(struct af_instance *af, struct mp_audio *data, int offset)
#define UNROLL_PADDING (4 * 4)
-static int best_overlap_offset_float(af_scaletempo_t *s)
+static int best_overlap_offset_float(struct priv *s)
{
float best_corr = INT_MIN;
int best_off = 0;
@@ -146,7 +157,7 @@ static int best_overlap_offset_float(af_scaletempo_t *s)
return best_off * 4 * s->num_channels;
}
-static int best_overlap_offset_s16(af_scaletempo_t *s)
+static int best_overlap_offset_s16(struct priv *s)
{
int64_t best_corr = INT64_MIN;
int best_off = 0;
@@ -183,7 +194,7 @@ static int best_overlap_offset_s16(af_scaletempo_t *s)
return best_off * 2 * s->num_channels;
}
-static void output_overlap_float(af_scaletempo_t *s, void *buf_out,
+static void output_overlap_float(struct priv *s, void *buf_out,
int bytes_off)
{
float *pout = buf_out;
@@ -196,7 +207,7 @@ static void output_overlap_float(af_scaletempo_t *s, void *buf_out,
}
}
-static void output_overlap_s16(af_scaletempo_t *s, void *buf_out,
+static void output_overlap_s16(struct priv *s, void *buf_out,
int bytes_off)
{
int16_t *pout = buf_out;
@@ -209,28 +220,65 @@ static void output_overlap_s16(af_scaletempo_t *s, void *buf_out,
}
}
-static int filter(struct af_instance *af, struct mp_audio *data)
+static void process(struct mp_filter *f)
{
- af_scaletempo_t *s = af->priv;
+ struct priv *s = f->priv;
+
+ if (!mp_pin_can_transfer_data(f->ppins[1], s->in_pin))
+ return;
- if (s->scale == 1.0) {
- af->delay = 0;
- af_add_output_frame(af, data);
- return 0;
+ struct mp_aframe *in = NULL, *out = NULL;
+
+ struct mp_frame frame = mp_pin_out_read(s->in_pin);
+ if (frame.type != MP_FRAME_AUDIO && frame.type != MP_FRAME_EOF) {
+ MP_ERR(f, "unexpected frame type\n");
+ goto error;
+ }
+
+ in = frame.type == MP_FRAME_AUDIO ? frame.data : NULL;
+ bool is_eof = !in;
+
+ // EOF before it was even initialized once.
+ if (is_eof && !mp_aframe_config_is_valid(s->cur_format)) {
+ mp_pin_in_write(f->ppins[1], MP_EOF_FRAME);
+ return;
+ }
+
+ if (in && !mp_aframe_config_equals(in, s->cur_format)) {
+ if (s->bytes_queued) {
+ // Drain remaining data before executing the format change.
+ MP_VERBOSE(f, "draining\n");
+ mp_pin_out_unread(s->in_pin, frame);
+ in = NULL;
+ } else {
+ if (!reinit(f, in)) {
+ MP_ERR(f, "initialization failed\n");
+ goto error;
+ }
+ }
}
- int in_samples = data ? data->samples : 0;
- struct mp_audio *out = mp_audio_pool_get(af->out_pool, af->data,
- ((int)(in_samples / s->frames_stride_scaled) + 1) * s->frames_stride);
- if (!out) {
- talloc_free(data);
- return -1;
+ int in_samples = in ? mp_aframe_get_size(in) : 0;
+
+ int max_out_samples =
+ ((int)(in_samples / s->frames_stride_scaled) + 1) * s->frames_stride;
+ if (!in)
+ max_out_samples += s->bytes_queued;
+ out = mp_aframe_new_ref(s->cur_format);
+ if (mp_aframe_pool_allocate(s->out_pool, out, max_out_samples) < 0)
+ goto error;
+
+ if (in) {
+ mp_aframe_copy_attributes(out, in);
+ s->current_pts = mp_aframe_end_pts(in);
}
- if (data)
- mp_audio_copy_attributes(out, data);
- int offset_in = fill_queue(af, data, 0);
- int8_t *pout = out->planes[0];
+ int offset_in = fill_queue(s, in, 0);
+ uint8_t **out_planes = mp_aframe_get_data_rw(out);
+ if (!out_planes)
+ goto error;
+ int8_t *pout = out_planes[0];
+ int out_offset = 0;
while (s->bytes_queued >= s->bytes_queue) {
int ti;
float tf;
@@ -240,12 +288,12 @@ static int filter(struct af_instance *af, struct mp_audio *data)
if (s->output_overlap) {
if (s->best_overlap_offset)
bytes_off = s->best_overlap_offset(s);
- s->output_overlap(s, pout, bytes_off);
+ s->output_overlap(s, pout + out_offset, bytes_off);
}
- memcpy(pout + s->bytes_overlap,
+ memcpy(pout + out_offset + s->bytes_overlap,
s->buf_queue + bytes_off + s->bytes_overlap,
s->bytes_standing);
- pout += s->bytes_stride;
+ out_offset += s->bytes_stride;
// input stride
memcpy(s->buf_overlap,
@@ -256,239 +304,302 @@ static int filter(struct af_instance *af, struct mp_audio *data)
s->frames_stride_error = tf - ti;
s->bytes_to_slide = ti * s->bytes_per_frame;
- offset_in += fill_queue(af, data, offset_in);
+ offset_in += fill_queue(s, in, offset_in);
}
+ // Drain remaining buffered data.
+ if (!in && s->bytes_queued) {
+ memcpy(pout + out_offset, s->buf_queue, s->bytes_queued);
+ out_offset += s->bytes_queued;
+ s->bytes_queued = 0;
+ }
+ mp_aframe_set_size(out, out_offset / s->bytes_per_frame);
// This filter can have a negative delay when scale > 1:
// output corresponding to some length of input can be decided and written
// after receiving only a part of that input.
- af->delay = (s->bytes_queued - s->bytes_to_slide) / s->scale
- / out->sstride / out->rate;
+ double delay = (out_offset * s->speed + s->bytes_queued - s->bytes_to_slide) /
+ s->bytes_per_frame / mp_aframe_get_effective_rate(out);
- out->samples = (pout - (int8_t *)out->planes[0]) / out->sstride;
- talloc_free(data);
- if (out->samples) {
- af_add_output_frame(af, out);
- } else {
- talloc_free(out);
+ if (s->current_pts != MP_NOPTS_VALUE)
+ mp_aframe_set_pts(out, s->current_pts - delay);
+
+ mp_aframe_mul_speed(out, s->speed);
+
+ if (!mp_aframe_get_size(out))
+ TA_FREEP(&out);
+
+ if (is_eof && out) {
+ mp_pin_out_repeat_eof(s->in_pin);
+ } else if (is_eof && !out) {
+ mp_pin_in_write(f->ppins[1], MP_EOF_FRAME);
+ } else if (!is_eof && !out) {
+ mp_pin_out_request_data(s->in_pin);
}
- return 0;
+
+ if (out)
+ mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out));
+
+ talloc_free(in);
+ return;
+
+error:
+ talloc_free(in);
+ talloc_free(out);
+ mp_filter_internal_mark_failed(f);
}
-static void update_speed(struct af_instance *af, float speed)
+static void update_speed(struct priv *s, float speed)
{
- af_scaletempo_t *s = af->priv;
-
s->speed = speed;
- double factor = (s->speed_opt & SCALE_PITCH) ? 1.0 / s->speed : s->speed;
- s->scale = factor * s->scale_nominal;
+ double factor = (s->opts->speed_opt & SCALE_PITCH) ? 1.0 / s->speed : s->speed;
+ s->scale = factor * s->opts->scale_nominal;
s->frames_stride_scaled = s->scale * s->frames_stride;
s->frames_stride_error = MPMIN(s->frames_stride_error, s->frames_stride_scaled);
}
-// Initialization and runtime control
-static int control(struct af_instance *af, int cmd, void *arg)
+static bool reinit(struct mp_filter *f, struct mp_aframe *in)
{
- af_scaletempo_t *s = af->priv;
- switch (cmd) {
- case AF_CONTROL_REINIT: {
- struct mp_audio *data = (struct mp_audio *)arg;
- float srate = data->rate / 1000.0;
- int nch = data->nch;
- int use_int = 0;
-
- mp_audio_force_interleaved_format(data);
- mp_audio_copy_config(af->data, data);
-
- if (data->format == AF_FORMAT_S16) {
- use_int = 1;
- } else {
- mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
- }
- int bps = af->data->bps;
+ struct priv *s = f->priv;
- s->frames_stride = srate * s->ms_stride;
- s->bytes_stride = s->frames_stride * bps * nch;
- af->delay = 0;
+ mp_aframe_reset(s->cur_format);
- update_speed(af, s->speed);
+ float srate = mp_aframe_get_rate(in) / 1000.0;
+ int nch = mp_aframe_get_channels(in);
+ int format = mp_aframe_get_format(in);
- int frames_overlap = s->frames_stride * s->percent_overlap;
- if (frames_overlap <= 0) {
- s->bytes_standing = s->bytes_stride;
- s->samples_standing = s->bytes_standing / bps;
- s->output_overlap = NULL;
- s->bytes_overlap = 0;
- } else {
- s->samples_overlap = frames_overlap * nch;
- s->bytes_overlap = frames_overlap * nch * bps;
- s->bytes_standing = s->bytes_stride - s->bytes_overlap;
- s->samples_standing = s->bytes_standing / bps;
- s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap);
- s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4);
- if (!s->buf_overlap || !s->table_blend) {
- MP_FATAL(af, "Out of memory\n");
- return AF_ERROR;
+ int use_int = 0;
+ if (format == AF_FORMAT_S16) {
+ use_int = 1;
+ } else if (format != AF_FORMAT_FLOAT) {
+ return false;
+ }
+ int bps = use_int ? 2 : 4;
+
+ s->frames_stride = srate * s->opts->ms_stride;
+ s->bytes_stride = s->frames_stride * bps * nch;
+
+ update_speed(s, s->speed);
+
+ int frames_overlap = s->frames_stride * s->opts->percent_overlap;
+ if (frames_overlap <= 0) {
+ s->bytes_standing = s->bytes_stride;
+ s->samples_standing = s->bytes_standing / bps;
+ s->output_overlap = NULL;
+ s->bytes_overlap = 0;
+ } else {
+ s->samples_overlap = frames_overlap * nch;
+ s->bytes_overlap = frames_overlap * nch * bps;
+ s->bytes_standing = s->bytes_stride - s->bytes_overlap;
+ s->samples_standing = s->bytes_standing / bps;
+ s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap);
+ s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4);
+ if (!s->buf_overlap || !s->table_blend) {
+ MP_FATAL(f, "Out of memory\n");
+ return false;
+ }
+ memset(s->buf_overlap, 0, s->bytes_overlap);
+ if (use_int) {
+ int32_t *pb = s->table_blend;
+ int64_t blend = 0;
+ for (int i = 0; i < frames_overlap; i++) {
+ int32_t v = blend / frames_overlap;
+ for (int j = 0; j < nch; j++)
+ *pb++ = v;
+ blend += 65536; // 2^16
}
- memset(s->buf_overlap, 0, s->bytes_overlap);
- if (use_int) {
- int32_t *pb = s->table_blend;
- int64_t blend = 0;
- for (int i = 0; i < frames_overlap; i++) {
- int32_t v = blend / frames_overlap;
- for (int j = 0; j < nch; j++)
- *pb++ = v;
- blend += 65536; // 2^16
- }
- s->output_overlap = output_overlap_s16;
- } else {
- float *pb = s->table_blend;
- for (int i = 0; i < frames_overlap; i++) {
- float v = i / (float)frames_overlap;
- for (int j = 0; j < nch; j++)
- *pb++ = v;
- }
- s->output_overlap = output_overlap_float;
+ s->output_overlap = output_overlap_s16;
+ } else {
+ float *pb = s->table_blend;
+ for (int i = 0; i < frames_overlap; i++) {
+ float v = i / (float)frames_overlap;
+ for (int j = 0; j < nch; j++)
+ *pb++ = v;
}
+ s->output_overlap = output_overlap_float;
}
+ }
- s->frames_search = (frames_overlap > 1) ? srate * s->ms_search : 0;
- if (s->frames_search <= 0)
- s->best_overlap_offset = NULL;
- else {
- if (use_int) {
- int64_t t = frames_overlap;
- int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2
- s->buf_pre_corr = realloc(s->buf_pre_corr,
- s->bytes_overlap * 2 + UNROLL_PADDING);
- s->table_window = realloc(s->table_window,
- s->bytes_overlap * 2 - nch * bps * 2);
- if (!s->buf_pre_corr || !s->table_window) {
- MP_FATAL(af, "Out of memory\n");
- return AF_ERROR;
- }
- memset((char *)s->buf_pre_corr + s->bytes_overlap * 2, 0,
- UNROLL_PADDING);
- int32_t *pw = s->table_window;
- for (int i = 1; i < frames_overlap; i++) {
- int32_t v = (i * (t - i) * n) >> 15;
- for (int j = 0; j < nch; j++)
- *pw++ = v;
- }
- s->best_overlap_offset = best_overlap_offset_s16;
- } else {
- s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap);
- s->table_window = realloc(s->table_window,
- s->bytes_overlap - nch * bps);
- if (!s->buf_pre_corr || !s->table_window) {
- MP_FATAL(af, "Out of memory\n");
- return AF_ERROR;
- }
- float *pw = s->table_window;
- for (int i = 1; i < frames_overlap; i++) {
- float v = i * (frames_overlap - i);
- for (int j = 0; j < nch; j++)
- *pw++ = v;
- }
- s->best_overlap_offset = best_overlap_offset_float;
+ s->frames_search = (frames_overlap > 1) ? srate * s->opts->ms_search : 0;
+ if (s->frames_search <= 0)
+ s->best_overlap_offset = NULL;
+ else {
+ if (use_int) {
+ int64_t t = frames_overlap;
+ int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2
+ s->buf_pre_corr = realloc(s->buf_pre_corr,
+ s->bytes_overlap * 2 + UNROLL_PADDING);
+ s->table_window = realloc(s->table_window,
+ s->bytes_overlap * 2 - nch * bps * 2);
+ if (!s->buf_pre_corr || !s->table_window) {
+ MP_FATAL(f, "Out of memory\n");
+ return false;
+ }
+ memset((char *)s->buf_pre_corr + s->bytes_overlap * 2, 0,
+ UNROLL_PADDING);
+ int32_t *pw = s->table_window;
+ for (int i = 1; i < frames_overlap; i++) {
+ int32_t v = (i * (t - i) * n) >> 15;
+ for (int j = 0; j < nch; j++)
+ *pw++ = v;
}
+ s->best_overlap_offset = best_overlap_offset_s16;
+ } else {
+ s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap);
+ s->table_window = realloc(s->table_window,
+ s->bytes_overlap - nch * bps);
+ if (!s->buf_pre_corr || !s->table_window) {
+ MP_FATAL(f, "Out of memory\n");
+ return false;
+ }
+ float *pw = s->table_window;
+ for (int i = 1; i < frames_overlap; i++) {
+ float v = i * (frames_overlap - i);
+ for (int j = 0; j < nch; j++)
+ *pw++ = v;
+ }
+ s->best_overlap_offset = best_overlap_offset_float;
}
+ }
- s->bytes_per_frame = bps * nch;
- s->num_channels = nch;
-
- s->bytes_queue = (s->frames_search + s->frames_stride + frames_overlap)
- * bps * nch;
- s->buf_queue = realloc(s->buf_queue, s->bytes_queue + UNROLL_PADDING);
- if (!s->buf_queue) {
- MP_FATAL(af, "Out of memory\n");
- return AF_ERROR;
- }
+ s->bytes_per_frame = bps * nch;
+ s->num_channels = nch;
- s->bytes_queued = 0;
- s->bytes_to_slide = 0;
-
- MP_DBG(af, ""
- "%.2f stride_in, %i stride_out, %i standing, "
- "%i overlap, %i search, %i queue, %s mode\n",
- s->frames_stride_scaled,
- (int)(s->bytes_stride / nch / bps),
- (int)(s->bytes_standing / nch / bps),
- (int)(s->bytes_overlap / nch / bps),
- s->frames_search,
- (int)(s->bytes_queue / nch / bps),
- (use_int ? "s16" : "float"));
-
- return af_test_output(af, (struct mp_audio *)arg);
+ s->bytes_queue = (s->frames_search + s->frames_stride + frames_overlap)
+ * bps * nch;
+ s->buf_queue = realloc(s->buf_queue, s->bytes_queue + UNROLL_PADDING);
+ if (!s->buf_queue) {
+ MP_FATAL(f, "Out of memory\n");
+ return false;
}
- case AF_CONTROL_SET_PLAYBACK_SPEED: {
- double speed = *(double *)arg;
- if (s->speed_opt & SCALE_TEMPO) {
- if (s->speed_opt & SCALE_PITCH)
- break;
- update_speed(af, speed);
- } else if (s->speed_opt & SCALE_PITCH) {
- update_speed(af, speed);
- break; // do not signal OK
+
+ s->bytes_queued = 0;
+ s->bytes_to_slide = 0;
+
+ MP_DBG(f, ""
+ "%.2f stride_in, %i stride_out, %i standing, "
+ "%i overlap, %i search, %i queue, %s mode\n",
+ s->frames_stride_scaled,
+ (int)(s->bytes_stride / nch / bps),
+ (int)(s->bytes_standing / nch / bps),
+ (int)(s->bytes_overlap / nch / bps),
+ s->frames_search,
+ (int)(s->bytes_queue / nch / bps),
+ (use_int ? "s16" : "float"));
+
+ mp_aframe_config_copy(s->cur_format, in);
+
+ return true;
+}
+
+static bool command(struct mp_filter *f, struct mp_filter_command *cmd)
+{
+ struct priv *s = f->priv;
+
+ if (cmd->type == MP_FILTER_COMMAND_SET_SPEED) {
+ if (s->opts->speed_opt & SCALE_TEMPO) {
+ if (s->opts->speed_opt & SCALE_PITCH)
+ return false;
+ update_speed(s, cmd->speed);
+ return true;
+ } else if (s->opts->speed_opt & SCALE_PITCH) {
+ update_speed(s, cmd->speed);
+ return false; // do not signal OK
}
- return AF_OK;
- }
- case AF_CONTROL_RESET:
- s->bytes_queued = 0;
- s->bytes_to_slide = 0;
- s->frames_stride_error = 0;
- memset(s->buf_overlap, 0, s->bytes_overlap);
}
- return AF_UNKNOWN;
+
+ return false;
}
-// Deallocate memory
-static void uninit(struct af_instance *af)
+static void reset(struct mp_filter *f)
{
- af_scaletempo_t *s = af->priv;
+ struct priv *s = f->priv;
+
+ s->current_pts = MP_NOPTS_VALUE;
+ s->bytes_queued = 0;
+ s->bytes_to_slide = 0;
+ s->frames_stride_error = 0;
+ memset(s->buf_overlap, 0, s->bytes_overlap);
+}
+
+static void destroy(struct mp_filter *f)
+{
+ struct priv *s = f->priv;
free(s->buf_queue);
free(s->buf_overlap);
free(s->buf_pre_corr);
free(s->table_blend);
free(s->table_window);
+ mp_filter_free_children(f);
}
-// Allocate memory and set function pointers
-static int af_open(struct af_instance *af)
+static const struct mp_filter_info af_scaletempo_filter = {
+ .name = "scaletempo",
+ .priv_size = sizeof(struct priv),
+ .process = process,
+ .command = command,
+ .reset = reset,
+ .destroy = destroy,
+};
+
+static struct mp_filter *af_scaletempo_create(struct mp_filter *parent,
+ void *options)
{
- af->control = control;
- af->uninit = uninit;
- af->filter_frame = filter;
- return AF_OK;
-}
+ struct mp_filter *f = mp_filter_create(parent, &af_scaletempo_filter);
+ if (!f) {
+ talloc_free(options);
+ return NULL;
+ }
-#define OPT_BASE_STRUCT af_scaletempo_t
+ mp_filter_add_pin(f, MP_PIN_IN, "in");
+ mp_filter_add_pin(f, MP_PIN_OUT, "out");
-const struct af_info af_info_scaletempo = {
- .info = "Scale audio tempo while maintaining pitch",
- .name = "scaletempo",
- .open = af_open,
- .priv_size = sizeof(af_scaletempo_t),
- .priv_defaults = &(const af_scaletempo_t) {
- .ms_stride = 60,
- .percent_overlap = .20,
- .ms_search = 14,
- .speed_opt = SCALE_TEMPO,
- .speed = 1.0,
- .scale_nominal = 1.0,
- },
- .options = (const struct m_option[]) {
- OPT_FLOAT("scale", scale_nominal, M_OPT_MIN, .min = 0.01),
- OPT_FLOAT("stride", ms_stride, M_OPT_MIN, .min = 0.01),
- OPT_FLOAT("overlap", percent_overlap, M_OPT_RANGE, .min = 0, .max = 1),
- OPT_FLOAT("search", ms_search, M_OPT_MIN, .min = 0),
- OPT_CHOICE("speed", speed_opt, 0,
- ({"pitch", SCALE_PITCH},
- {"tempo", SCALE_TEMPO},
- {"none", 0},
- {"both", SCALE_TEMPO | SCALE_PITCH})),
- {0}
+ struct priv *s = f->priv;
+ s->opts = talloc_steal(s, options);
+ s->speed = 1.0;
+ s->cur_format = talloc_steal(s, mp_aframe_create());
+ s->out_pool = mp_aframe_pool_create(s);
+
+ struct mp_autoconvert *conv = mp_autoconvert_create(f);
+ if (!conv)
+ abort();
+
+ mp_autoconvert_add_afmt(conv, AF_FORMAT_S16);
+ mp_autoconvert_add_afmt(conv, AF_FORMAT_FLOAT);
+
+ mp_pin_connect(conv->f->pins[0], f->ppins[0]);
+ s->in_pin = conv->f->pins[1];
+
+ return f;
+}
+
+#define OPT_BASE_STRUCT struct f_opts
+
+const struct mp_user_filter_entry af_scaletempo = {
+ .desc = {
+ .description = "Scale audio tempo while maintaining pitch",
+ .name = "scaletempo",
+ .priv_size = sizeof(OPT_BASE_STRUCT),
+ .priv_defaults = &(const OPT_BASE_STRUCT) {
+ .ms_stride = 60,
+ .percent_overlap = .20,
+ .ms_search = 14,
+ .speed_opt = SCALE_TEMPO,
+ .scale_nominal = 1.0,
+ },
+ .options = (const struct m_option[]) {
+ OPT_FLOAT("scale", scale_nominal, M_OPT_MIN, .min = 0.01),
+ OPT_FLOAT("stride", ms_stride, M_OPT_MIN, .min = 0.01),
+ OPT_FLOAT("overlap", percent_overlap, M_OPT_RANGE, .min = 0, .max = 1),
+ OPT_FLOAT("search", ms_search, M_OPT_MIN, .min = 0),
+ OPT_CHOICE("speed", speed_opt, 0,
+ ({"pitch", SCALE_PITCH},
+ {"tempo", SCALE_TEMPO},
+ {"none", 0},
+ {"both", SCALE_TEMPO | SCALE_PITCH})),
+ {0}
+ },
},
+ .create = af_scaletempo_create,
};