[MPlayer-dev-eng] [PATCH] af_scaletempo
Robert Juliano
juliano.1 at osu.edu
Wed Sep 12 23:44:46 CEST 2007
Here's a new patch. Changes include:
- fixed memory allocation bug that caused crashes
- reworked variable names
- cached some calculations used in the main loops
- switched from array notation to pointers, and
- implemented an integer version (which I've commented out
since it benchmarks slower).
usage: mplayer -af scaletempo [-speed scale]
It's linked to playback_speed, so the audio scales to match
speed whenever the speed changes (by -speed, keyboard, menu,
command, or property).
Since this filter exposes a bug in lib/af.c:af_calc_insize_constrained
you'll also want the af_calc_insize_constrained_overflow_quickfix from:
http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/2007-June/052315.html
Let me know if you have any issues.
robert
-------------- next part --------------
diff --git a/libaf/Makefile b/libaf/Makefile
index a4a7b14..e11643e 100644
--- a/libaf/Makefile
+++ b/libaf/Makefile
@@ -16,6 +16,7 @@ SRCS_COMMON = af.c \
af_karaoke.c \
af_pan.c \
af_resample.c \
+ af_scaletempo.c \
af_sinesuppress.c \
af_sub.c \
af_surround.c \
diff --git a/libaf/af.c b/libaf/af.c
index d95a3c5..fc9d5fa 100644
--- a/libaf/af.c
+++ b/libaf/af.c
@@ -31,6 +31,7 @@ extern af_info_t af_info_ladspa;
extern af_info_t af_info_center;
extern af_info_t af_info_sinesuppress;
extern af_info_t af_info_karaoke;
+extern af_info_t af_info_scaletempo;
static af_info_t* filter_list[]={
&af_info_dummy,
@@ -61,6 +62,7 @@ static af_info_t* filter_list[]={
&af_info_center,
&af_info_sinesuppress,
&af_info_karaoke,
+ &af_info_scaletempo,
NULL
};
diff --git a/libaf/af_scaletempo.c b/libaf/af_scaletempo.c
new file mode 100644
index 0000000..ce75d57
--- /dev/null
+++ b/libaf/af_scaletempo.c
@@ -0,0 +1,355 @@
+/*
+ * scaletempo audio filter
+ * (cc) GPL 2007 MPlayer / Robert Juliano
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MPlayer; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * scale tempo while maintaining pitch
+ * (WSOLA technique with cross correlation)
+ * inspired by SoundTouch library by Olli Parviainen
+ *
+ * basic algorithm
+ * - produce 'stride' output samples per loop
+ * - consume stride*speed input samples per loop
+ *
+ * to produce smoother transitions between strides, blend next ns_overlap
+ * samples from last stride with correlated samples of current input
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "af.h"
+
+//#define USE_INT
+#ifdef USE_INT
+#define SAMPLETYPE int16_t
+#define LONG_SAMPLETYPE int32_t
+#else
+#define SAMPLETYPE float
+#define LONG_SAMPLETYPE float
+#endif
+
+// Data for specific instances of this filter
+typedef struct af_scaletempo_s
+{
+ float scale;
+ int samples_overlap;
+ int frames_seek;
+ float frames_stride_scaled;
+ float frames_stride_error;
+ int bytes_stride;
+ SAMPLETYPE * window;
+ SAMPLETYPE * overlap;
+ SAMPLETYPE * pre_corr;
+ int bytes_window;
+ int bytes_filled;
+ int bytes_to_slide;
+ int bytes_per_frame;
+ int num_channels;
+ // cached values
+ int bytes_overlap;
+ int bytes_standing;
+ float bytes_stride_scaled;
+ int samples_standing;
+ float * blend_overlap;
+ float * blend_pre_corr;
+ int sloping_divider;
+} af_scaletempo_t;
+
+int fill_window(struct af_instance_s* af, af_data_t* data, int offset) {
+ af_scaletempo_t* s = af->setup;
+ int bytes_in = data->len - offset;
+ int offset_unchanged = offset;
+
+ if (s->bytes_to_slide > 0) {
+ if (s->bytes_to_slide < s->bytes_filled) {
+ int bytes_move = s->bytes_filled - s->bytes_to_slide;
+ memmove(s->window,
+ (int8_t *)s->window + s->bytes_to_slide,
+ bytes_move);
+ s->bytes_to_slide = 0;
+ s->bytes_filled = bytes_move;
+ } else {
+ int bytes_skip;
+ s->bytes_to_slide -= s->bytes_filled;
+ bytes_skip = min(s->bytes_to_slide, bytes_in);
+ s->bytes_filled = 0;
+ s->bytes_to_slide -= bytes_skip;
+ offset += bytes_skip;
+ bytes_in -= bytes_skip;
+ }
+ }
+
+ if (bytes_in > 0) {
+ int bytes_copy = min(s->bytes_window - s->bytes_filled, bytes_in);
+ memcpy((int8_t *)s->window + s->bytes_filled,
+ (int8_t *)data->audio + offset,
+ bytes_copy);
+ s->bytes_filled += bytes_copy;
+ offset += bytes_copy;
+ }
+
+ return offset - offset_unchanged;
+}
+
+int best_overlap_off(struct af_instance_s* af) {
+ af_scaletempo_t* s = af->setup;
+ SAMPLETYPE *po, *pc, *seek_start;
+ float * pb;
+ LONG_SAMPLETYPE best_corr = INT_MIN;
+ int best_off = 0;
+ int len, off;
+
+ po = s->overlap;
+ pc = s->pre_corr;
+ pb = s->blend_pre_corr;
+ len = s->samples_overlap;
+ while (len--) {
+ *pc = *po * *pb;
+ pc++; po++; pb++;
+ }
+
+ seek_start = s->window;
+ for (off=0; off<s->frames_seek; off++) {
+ LONG_SAMPLETYPE corr = 0;
+ SAMPLETYPE * pw = seek_start;
+ pc = s->pre_corr;
+ len = s->samples_overlap;
+ while (len--) {
+#ifdef USE_INT
+ corr += ( *pc * *pw ) / s->sloping_divider;
+#else
+ corr += *pc * *pw;
+#endif
+ pc++; pw++;
+ }
+ if (corr > best_corr) {
+ best_corr = corr;
+ best_off = off;
+ }
+ seek_start += s->num_channels;
+ }
+
+ return best_off;
+}
+
+// Filter data through filter
+static af_data_t* play(struct af_instance_s* af, af_data_t* io_data)
+{
+ af_scaletempo_t* s = af->setup;
+ int offset_in;
+ int max_bytes_out;
+ SAMPLETYPE * pout;
+
+ // RESIZE_LOCAL_BUFFER - can't use macro
+ max_bytes_out = ((int)(io_data->len / s->bytes_stride_scaled) + 1) * s->bytes_stride;
+ if (max_bytes_out > af->data->len) {
+ af_msg(AF_MSG_VERBOSE,"[libaf] Reallocating memory in module %s, "
+ "old len = %i, new len = %i\n",af->info->name,af->data->len,max_bytes_out);
+ af->data->audio = realloc(af->data->audio, max_bytes_out);
+ if (!af->data->audio) {
+ af_msg(AF_MSG_FATAL, "[libaf] Could not allocate memory\n");
+ return NULL;
+ }
+ af->data->len = max_bytes_out;
+ }
+
+ offset_in = fill_window(af, io_data, 0);
+ pout = af->data->audio;
+ while (s->bytes_filled >= s->bytes_window) {
+ SAMPLETYPE * po = s->overlap;
+ SAMPLETYPE * pw = s->window;
+ float * pb = s->blend_overlap;
+ int len, ti;
+ float tf;
+
+ pw += best_overlap_off(af);
+ len = s->samples_overlap;
+ while (len--) {
+ *pout = (1 - *pb) * *po + *pb * *pw;
+ pout++; pb++; po++; pw++;
+ }
+ memcpy(pout, pw, s->bytes_standing);
+ pout += s->samples_standing;
+
+ memcpy(s->overlap, pw + s->samples_standing, s->bytes_overlap);
+ tf = s->frames_stride_scaled + s->frames_stride_error;
+ ti = (int)tf;
+ s->frames_stride_error = tf - ti;
+ s->bytes_to_slide = ti * s->bytes_per_frame;
+
+ offset_in += fill_window(af, io_data, offset_in);
+ }
+
+ io_data->audio = af->data->audio;
+ io_data->len = (int)pout - (int)af->data->audio;
+ return io_data;
+}
+
+// Initialization and runtime control
+static int control(struct af_instance_s* af, int cmd, void* arg)
+{
+ af_scaletempo_t* s = af->setup;
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ af_data_t* io_data = (af_data_t*)arg;
+ int rate = io_data->rate;
+ int nch = io_data->nch;
+ int bps = sizeof(SAMPLETYPE);
+ int frames_window, frames_overlap;
+ int i;
+ float * pb;
+
+ frames_window = rate * 82 / 1000;
+ frames_overlap = rate * 1 / 1000;
+ s->frames_seek = rate * 14 / 1000;
+
+ s->bytes_window = (frames_window + s->frames_seek) * bps * nch;
+ s->samples_overlap = frames_overlap * nch;
+ s->bytes_overlap = frames_overlap * nch * bps;
+ s->bytes_stride = (frames_window - frames_overlap) * bps * nch;
+ s->bytes_standing = s->bytes_stride - s->bytes_overlap;
+ s->samples_standing = (int)(s->bytes_standing / bps);
+
+ s->bytes_stride_scaled = s->bytes_stride * s->scale;
+ s->frames_stride_scaled = (frames_window - frames_overlap) * s->scale;
+ s->frames_stride_error = 0;
+ af->mul.n = s->bytes_stride;
+ af->mul.d = s->bytes_stride_scaled;
+ af_frac_cancel(&af->mul);
+
+ s->bytes_per_frame = bps * nch;
+ s->num_channels = nch;
+
+ s->window = realloc(s->window, s->bytes_window);
+ s->overlap = realloc(s->overlap, s->bytes_overlap);
+ s->pre_corr = realloc(s->pre_corr, s->bytes_overlap);
+ s->blend_overlap = realloc(s->blend_overlap, frames_overlap * sizeof(float) * nch);
+ s->blend_pre_corr = realloc(s->blend_pre_corr, frames_overlap * sizeof(float) * nch);
+ if(!(s->window && s->overlap && s->pre_corr && s->blend_overlap && s->blend_pre_corr)) {
+ af_msg(AF_MSG_FATAL, "[scaletempo] Out of memory\n");
+ return AF_ERROR;
+ }
+
+ pb = s->blend_overlap;
+ for (i=0; i<frames_overlap; i++) {
+ float t = i / (float)frames_overlap;
+ int ch = nch;
+ while (ch--) {
+ *pb = t;
+ pb++;
+ }
+ }
+
+ pb = s->blend_pre_corr;
+ for (i=0; i<frames_overlap; i++) {
+ SAMPLETYPE t = i * (frames_overlap - 1);
+ int ch = nch;
+ while (ch--) {
+ *pb = t;
+ pb++;
+ }
+ }
+
+ af->data->rate = io_data->rate;
+ af->data->nch = io_data->nch;
+#ifdef USE_INT
+ af->data->format = AF_FORMAT_S16_NE;
+ s->sloping_divider = nch * (frames_overlap * frames_overlap) / 6;
+#else
+ af->data->format = AF_FORMAT_FLOAT_NE;
+#endif
+ af->data->bps = sizeof(SAMPLETYPE);
+
+ af_msg (AF_MSG_DEBUG0,
+ "%4i seek, "
+ "%5i window_size, "
+ "%4i overlap, "
+ "%5i standing, "
+ "%5i stride, "
+ "%6.2f stride_scaled\n",
+ s->frames_seek,
+ (int)(s->bytes_window / nch / bps),
+ (int)(s->bytes_overlap / nch / bps),
+ (int)(s->bytes_standing / nch / bps),
+ (int)(s->bytes_stride / nch / bps),
+ s->frames_stride_scaled);
+
+ return af_test_output(af,(af_data_t*)arg);
+ }
+ case AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET:
+ case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_SET:
+ s->scale = *(float*)arg;
+ return AF_OK;
+ case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_GET:
+ *(float*)arg = s->scale;
+ return AF_OK;
+ case AF_CONTROL_COMMAND_LINE:{
+ float f;
+ sscanf((char*)arg,"%f", &f);
+ return control(af, AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_SET, &f);
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance_s* af)
+{
+ af_scaletempo_t* s = af->setup;
+ free(af->data->audio);
+ free(af->data);
+ free(s->window);
+ free(s->overlap);
+ free(s->pre_corr);
+ free(s->blend_overlap);
+ free(s->blend_pre_corr);
+ free(af->setup);
+}
+
+// Allocate memory and set function pointers
+static int af_open(af_instance_t* af){
+ af_scaletempo_t* s;
+
+ af->control = control;
+ af->uninit = uninit;
+ af->play = play;
+ af->mul.n = 1;
+ af->mul.d = 1;
+ af->data = calloc(1,sizeof(af_data_t));
+ af->setup = calloc(1,sizeof(af_scaletempo_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+
+ s = af->setup;
+ s->scale = 1.0;
+
+ return AF_OK;
+}
+
+// Description of this filter
+af_info_t af_info_scaletempo = {
+ "Scale audio tempo while maintaining pitch",
+ "scaletempo",
+ "Robert Juliano",
+ "",
+ AF_FLAGS_REENTRANT,
+ af_open
+};
diff --git a/libaf/control.h b/libaf/control.h
index 80eff7c..7c6a8dd 100644
--- a/libaf/control.h
+++ b/libaf/control.h
@@ -231,4 +231,7 @@ typedef struct af_control_ext_s{
#define AF_CONTROL_SS_FREQ 0x00002300 | AF_CONTROL_FILTER_SPECIFIC
#define AF_CONTROL_SS_DECAY 0x00002400 | AF_CONTROL_FILTER_SPECIFIC
+#define AF_CONTROL_PLAYBACK_SPEED 0x00002500 | AF_CONTROL_FILTER_SPECIFIC
+#define AF_CONTROL_SCALETEMPO_AMOUNT 0x00002600 | AF_CONTROL_FILTER_SPECIFIC
+
#endif /*__af_control_h */
diff --git a/mplayer.c b/mplayer.c
index c0ca8d5..5847712 100644
--- a/mplayer.c
+++ b/mplayer.c
@@ -1203,14 +1203,12 @@ int build_afilter_chain(sh_audio_t *sh_audio, ao_data_t *ao_data)
mpctx->mixer.afilter = NULL;
return 0;
}
- new_srate = sh_audio->samplerate * playback_speed;
- if (new_srate != ao_data->samplerate) {
- // limits are taken from libaf/af_resample.c
- if (new_srate < 8000)
- new_srate = 8000;
- if (new_srate > 192000)
- new_srate = 192000;
- playback_speed = (float)new_srate / (float)sh_audio->samplerate;
+ if(af_control_any_rev(sh_audio->afilter,
+ AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET,
+ &playback_speed)) {
+ new_srate = sh_audio->samplerate;
+ } else {
+ new_srate = sh_audio->samplerate * playback_speed;
}
result = init_audio_filters(sh_audio, new_srate,
sh_audio->channels, sh_audio->sample_format,
@@ -1497,7 +1495,7 @@ if(mpctx->sh_audio){
// first init to detect best values
if(!preinit_audio_filters(mpctx->sh_audio,
// input:
- (int)(mpctx->sh_audio->samplerate*playback_speed),
+ mpctx->sh_audio->samplerate,
mpctx->sh_audio->channels, mpctx->sh_audio->sample_format,
// output:
&ao_data.samplerate, &ao_data.channels, &ao_data.format)){
--
1.5.1.6
More information about the MPlayer-dev-eng
mailing list