diff options
Diffstat (limited to 'audio/filter/af_rubberband.c')
-rw-r--r-- | audio/filter/af_rubberband.c | 446 |
1 files changed, 276 insertions, 170 deletions
diff --git a/audio/filter/af_rubberband.c b/audio/filter/af_rubberband.c index 58cf077d8b..6c8c773e62 100644 --- a/audio/filter/af_rubberband.c +++ b/audio/filter/af_rubberband.c @@ -20,242 +20,348 @@ #include <rubberband/rubberband-c.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" + +// command line options +struct f_opts { + int transients, detector, phase, window, + smoothing, formant, pitch, channels; + double scale; +}; struct priv { + struct f_opts *opts; + + struct mp_pin *in_pin; + struct mp_aframe *cur_format; + struct mp_aframe_pool *out_pool; + bool sent_final; RubberBandState rubber; double speed; double pitch; - struct mp_audio *pending; - bool needs_reset; + struct mp_aframe *pending; // Estimate how much librubberband has buffered internally. // I could not find a way to do this with the librubberband API. double rubber_delay; - // command line options - int opt_transients, opt_detector, opt_phase, opt_window, - opt_smoothing, opt_formant, opt_pitch, opt_channels; }; -static void update_speed(struct af_instance *af, double new_speed) +static void update_speed(struct priv *p, double new_speed) { - struct priv *p = af->priv; - p->speed = new_speed; - rubberband_set_time_ratio(p->rubber, 1.0 / p->speed); + if (p->rubber) + rubberband_set_time_ratio(p->rubber, 1.0 / p->speed); } -static bool update_pitch(struct af_instance *af, double new_pitch) +static bool update_pitch(struct priv *p, double new_pitch) { if (new_pitch < 0.01 || new_pitch > 100.0) return false; - struct priv *p = af->priv; - p->pitch = new_pitch; - rubberband_set_pitch_scale(p->rubber, p->pitch); + if (p->rubber) + rubberband_set_pitch_scale(p->rubber, p->pitch); return true; } -static int control(struct af_instance *af, int cmd, void *arg) +static bool init_rubberband(struct mp_filter *f) { - struct priv *p = af->priv; + struct priv *p = f->priv; - switch (cmd) { - case AF_CONTROL_REINIT: { - struct mp_audio *in = arg; - struct mp_audio orig_in = *in; - struct mp_audio *out = af->data; + assert(!p->rubber); + assert(p->pending); - in->format = AF_FORMAT_FLOATP; - mp_audio_copy_config(out, in); - - if (p->rubber) - rubberband_delete(p->rubber); - - int opts = p->opt_transients | p->opt_detector | p->opt_phase | - p->opt_window | p->opt_smoothing | p->opt_formant | - p->opt_pitch | p-> opt_channels | - RubberBandOptionProcessRealTime; - - p->rubber = rubberband_new(in->rate, in->channels.num, opts, 1.0, 1.0); - if (!p->rubber) { - MP_FATAL(af, "librubberband initialization failed.\n"); - return AF_ERROR; - } + int opts = p->opts->transients | p->opts->detector | p->opts->phase | + p->opts->window | p->opts->smoothing | p->opts->formant | + p->opts->pitch | p-> opts->channels | + RubberBandOptionProcessRealTime; - update_speed(af, p->speed); - update_pitch(af, p->pitch); - control(af, AF_CONTROL_RESET, NULL); + int rate = mp_aframe_get_rate(p->pending); + int channels = mp_aframe_get_channels(p->pending); + if (mp_aframe_get_format(p->pending) != AF_FORMAT_FLOATP) + return false; - return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE; - } - case AF_CONTROL_SET_PLAYBACK_SPEED: { - update_speed(af, *(double *)arg); - return AF_OK; - } - case AF_CONTROL_RESET: - if (p->rubber) - rubberband_reset(p->rubber); - talloc_free(p->pending); - p->pending = NULL; - p->rubber_delay = 0; - return AF_OK; - case AF_CONTROL_COMMAND: { - char **args = arg; - char *endptr; - double pitch = p->pitch; - if (!strcmp(args[0], "set-pitch")) { - pitch = strtod(args[1], &endptr); - if (*endptr) - return CONTROL_ERROR; - return update_pitch(af, pitch) ? CONTROL_OK : CONTROL_ERROR; - } else if (!strcmp(args[0], "multiply-pitch")) { - double mult = strtod(args[1], &endptr); - if (*endptr || mult <= 0) - return CONTROL_ERROR; - pitch *= mult; - return update_pitch(af, pitch) ? CONTROL_OK : CONTROL_ERROR; - } else { - return CONTROL_ERROR; - } - } + p->rubber = rubberband_new(rate, channels, opts, 1.0, 1.0); + if (!p->rubber) { + MP_FATAL(f, "librubberband initialization failed.\n"); + return false; } - return AF_UNKNOWN; -} -static int filter_frame(struct af_instance *af, struct mp_audio *data) -{ - struct priv *p = af->priv; + mp_aframe_config_copy(p->cur_format, p->pending); - talloc_free(p->pending); - p->pending = data; + update_speed(p, p->speed); + update_pitch(p, p->pitch); - return 0; + return true; } -static int filter_out(struct af_instance *af) +static void process(struct mp_filter *f) { - struct priv *p = af->priv; + struct priv *p = f->priv; + + if (!mp_pin_in_needs_data(f->ppins[1])) + return; - while (rubberband_available(p->rubber) <= 0) { + while (!p->rubber || !p->pending || rubberband_available(p->rubber) <= 0) { const float *dummy[MP_NUM_CHANNELS] = {0}; const float **in_data = dummy; size_t in_samples = 0; - if (p->pending) { - if (!p->pending->samples) - break; - // recover from previous EOF - if (p->needs_reset) { - rubberband_reset(p->rubber); - p->rubber_delay = 0; + bool eof = false; + if (!p->pending || !mp_aframe_get_size(p->pending)) { + struct mp_frame frame = mp_pin_out_read(p->in_pin); + if (frame.type == MP_FRAME_AUDIO) { + TA_FREEP(&p->pending); + p->pending = frame.data; + } else if (frame.type == MP_FRAME_EOF) { + eof = true; + } else if (frame.type) { + MP_ERR(f, "unexpected frame type\n"); + goto error; + } else { + return; // no new data yet } - p->needs_reset = false; + } + assert(p->pending || eof); + if (!p->rubber) { + if (!p->pending) { + mp_pin_in_write(f->ppins[1], MP_EOF_FRAME); + return; + } + if (!init_rubberband(f)) + goto error; + } + + bool format_change = + p->pending && !mp_aframe_config_equals(p->pending, p->cur_format); + + if (p->pending && !format_change) { size_t needs = rubberband_get_samples_required(p->rubber); - in_data = (void *)&p->pending->planes; - in_samples = MPMIN(p->pending->samples, needs); + uint8_t **planes = mp_aframe_get_data_ro(p->pending); + int num_planes = mp_aframe_get_planes(p->pending); + for (int n = 0; n < num_planes; n++) + in_data[n] = (void *)planes[n]; + in_samples = MPMIN(mp_aframe_get_size(p->pending), needs); } - if (p->needs_reset) - break; // previous EOF - p->needs_reset = !p->pending; // EOF + bool final = format_change || eof; + if (!p->sent_final) + rubberband_process(p->rubber, in_data, in_samples, final); + p->sent_final |= final; - rubberband_process(p->rubber, in_data, in_samples, p->needs_reset); p->rubber_delay += in_samples; - if (!p->pending) - break; - mp_audio_skip_samples(p->pending, in_samples); + if (p->pending && !format_change) + mp_aframe_skip_samples(p->pending, in_samples); + + if (rubberband_available(p->rubber) > 0) { + if (eof) + mp_pin_out_repeat_eof(p->in_pin); // drain more next time + } else { + if (eof) { + mp_pin_in_write(f->ppins[1], MP_EOF_FRAME); + rubberband_reset(p->rubber); + TA_FREEP(&p->pending); + p->sent_final = false; + return; + } else if (format_change) { + // go on with proper reinit on the next iteration + rubberband_delete(p->rubber); + p->sent_final = false; + p->rubber = NULL; + } + } } + assert(p->pending); + int out_samples = rubberband_available(p->rubber); if (out_samples > 0) { - struct mp_audio *out = - mp_audio_pool_get(af->out_pool, af->data, out_samples); - if (!out) - return -1; - if (p->pending) - mp_audio_copy_config(out, p->pending); - - float **out_data = (void *)&out->planes; - out->samples = rubberband_retrieve(p->rubber, out_data, out->samples); - p->rubber_delay -= out->samples * p->speed; - - af_add_output_frame(af, out); + struct mp_aframe *out = mp_aframe_new_ref(p->cur_format); + if (mp_aframe_pool_allocate(p->out_pool, out, out_samples) < 0) { + talloc_free(out); + goto error; + } + + mp_aframe_copy_attributes(out, p->pending); + + float *out_data[MP_NUM_CHANNELS] = {0}; + uint8_t **planes = mp_aframe_get_data_rw(out); + assert(planes); + int num_planes = mp_aframe_get_planes(out); + for (int n = 0; n < num_planes; n++) + out_data[n] = (void *)planes[n]; + + out_samples = rubberband_retrieve(p->rubber, out_data, out_samples); + + if (!out_samples) { + mp_filter_internal_mark_progress(f); // unexpected, just try again + talloc_free(out); + return; + } + + mp_aframe_set_size(out, out_samples); + + p->rubber_delay -= out_samples * p->speed; + + double pts = mp_aframe_get_pts(p->pending); + if (pts != MP_NOPTS_VALUE) { + // Note: rubberband_get_latency() does not do what you'd expect. + double delay = p->rubber_delay / mp_aframe_get_effective_rate(out); + mp_aframe_set_pts(out, pts - delay); + } + + mp_aframe_mul_speed(out, p->speed); + + mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out)); } - int delay_samples = p->rubber_delay; - if (p->pending) - delay_samples += p->pending->samples; - af->delay = delay_samples / (af->data->rate * p->speed); + return; +error: + mp_filter_internal_mark_failed(f); +} - return 0; +static bool command(struct mp_filter *f, struct mp_filter_command *cmd) +{ + struct priv *p = f->priv; + + switch (cmd->type) { + case MP_FILTER_COMMAND_TEXT: { + char *endptr = NULL; + double pitch = p->pitch; + if (!strcmp(cmd->cmd, "set-pitch")) { + pitch = strtod(cmd->arg, &endptr); + if (*endptr) + return false; + return update_pitch(p, pitch); + } else if (!strcmp(cmd->cmd, "multiply-pitch")) { + double mult = strtod(cmd->arg, &endptr); + if (*endptr || mult <= 0) + return false; + pitch *= mult; + return update_pitch(p, pitch); + } + return false; + } + case MP_FILTER_COMMAND_SET_SPEED: + update_speed(p, cmd->speed); + return true; + } + + return false; } -static void uninit(struct af_instance *af) +static void reset(struct mp_filter *f) { - struct priv *p = af->priv; + struct priv *p = f->priv; if (p->rubber) - rubberband_delete(p->rubber); - talloc_free(p->pending); + rubberband_reset(p->rubber); + p->sent_final = false; + TA_FREEP(&p->pending); } -static int af_open(struct af_instance *af) +static void destroy(struct mp_filter *f) { - af->control = control; - af->filter_frame = filter_frame; - af->filter_out = filter_out; - af->uninit = uninit; - return AF_OK; + struct priv *p = f->priv; + + if (p->rubber) + rubberband_delete(p->rubber); + talloc_free(p->pending); } -#define OPT_BASE_STRUCT struct priv -const struct af_info af_info_rubberband = { - .info = "Pitch conversion with librubberband", +static const struct mp_filter_info af_rubberband_filter = { .name = "rubberband", - .open = af_open, .priv_size = sizeof(struct priv), - .priv_defaults = &(const struct priv) { - .speed = 1.0, - .pitch = 1.0, - .opt_pitch = RubberBandOptionPitchHighConsistency, - .opt_transients = RubberBandOptionTransientsMixed, - .opt_formant = RubberBandOptionFormantPreserved, - .opt_channels = RubberBandOptionChannelsTogether, - }, - .options = (const struct m_option[]) { - OPT_CHOICE("transients", opt_transients, 0, - ({"crisp", RubberBandOptionTransientsCrisp}, - {"mixed", RubberBandOptionTransientsMixed}, - {"smooth", RubberBandOptionTransientsSmooth})), - OPT_CHOICE("detector", opt_detector, 0, - ({"compound", RubberBandOptionDetectorCompound}, - {"percussive", RubberBandOptionDetectorPercussive}, - {"soft", RubberBandOptionDetectorSoft})), - OPT_CHOICE("phase", opt_phase, 0, - ({"laminar", RubberBandOptionPhaseLaminar}, - {"independent", RubberBandOptionPhaseIndependent})), - OPT_CHOICE("window", opt_window, 0, - ({"standard", RubberBandOptionWindowStandard}, - {"short", RubberBandOptionWindowShort}, - {"long", RubberBandOptionWindowLong})), - OPT_CHOICE("smoothing", opt_smoothing, 0, - ({"off", RubberBandOptionSmoothingOff}, - {"on", RubberBandOptionSmoothingOn})), - OPT_CHOICE("formant", opt_formant, 0, - ({"shifted", RubberBandOptionFormantShifted}, - {"preserved", RubberBandOptionFormantPreserved})), - OPT_CHOICE("pitch", opt_pitch, 0, - ({"quality", RubberBandOptionPitchHighQuality}, - {"speed", RubberBandOptionPitchHighSpeed}, - {"consistency", RubberBandOptionPitchHighConsistency})), - OPT_CHOICE("channels", opt_channels, 0, - ({"apart", RubberBandOptionChannelsApart}, - {"together", RubberBandOptionChannelsTogether})), - OPT_DOUBLE("pitch-scale", pitch, M_OPT_RANGE, .min = 0.01, .max = 100), - {0} + .process = process, + .command = command, + .reset = reset, + .destroy = destroy, +}; + +static struct mp_filter *af_rubberband_create(struct mp_filter *parent, + void *options) +{ + struct mp_filter *f = mp_filter_create(parent, &af_rubberband_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->pitch = p->opts->scale; + p->cur_format = talloc_steal(p, mp_aframe_create()); + p->out_pool = mp_aframe_pool_create(p); + + struct mp_autoconvert *conv = mp_autoconvert_create(f); + if (!conv) + abort(); + + mp_autoconvert_add_afmt(conv, AF_FORMAT_FLOATP); + + mp_pin_connect(conv->f->pins[0], f->ppins[0]); + p->in_pin = conv->f->pins[1]; + + return f; +} + +#define OPT_BASE_STRUCT struct f_opts + +const struct mp_user_filter_entry af_rubberband = { + .desc = { + .description = "Pitch conversion with librubberband", + .name = "rubberband", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT) { + .scale = 1.0, + .pitch = RubberBandOptionPitchHighConsistency, + .transients = RubberBandOptionTransientsMixed, + .formant = RubberBandOptionFormantPreserved, + .channels = RubberBandOptionChannelsTogether, + }, + .options = (const struct m_option[]) { + OPT_CHOICE("transients", transients, 0, + ({"crisp", RubberBandOptionTransientsCrisp}, + {"mixed", RubberBandOptionTransientsMixed}, + {"smooth", RubberBandOptionTransientsSmooth})), + OPT_CHOICE("detector", detector, 0, + ({"compound", RubberBandOptionDetectorCompound}, + {"percussive", RubberBandOptionDetectorPercussive}, + {"soft", RubberBandOptionDetectorSoft})), + OPT_CHOICE("phase", phase, 0, + ({"laminar", RubberBandOptionPhaseLaminar}, + {"independent", RubberBandOptionPhaseIndependent})), + OPT_CHOICE("window", window, 0, + ({"standard", RubberBandOptionWindowStandard}, + {"short", RubberBandOptionWindowShort}, + {"long", RubberBandOptionWindowLong})), + OPT_CHOICE("smoothing", smoothing, 0, + ({"off", RubberBandOptionSmoothingOff}, + {"on", RubberBandOptionSmoothingOn})), + OPT_CHOICE("formant", formant, 0, + ({"shifted", RubberBandOptionFormantShifted}, + {"preserved", RubberBandOptionFormantPreserved})), + OPT_CHOICE("pitch", pitch, 0, + ({"quality", RubberBandOptionPitchHighQuality}, + {"speed", RubberBandOptionPitchHighSpeed}, + {"consistency", RubberBandOptionPitchHighConsistency})), + OPT_CHOICE("channels", channels, 0, + ({"apart", RubberBandOptionChannelsApart}, + {"together", RubberBandOptionChannelsTogether})), + OPT_DOUBLE("pitch-scale", scale, M_OPT_RANGE, .min = 0.01, .max = 100), + {0} + }, }, + .create = af_rubberband_create, }; |