audioresample: sinc filter performance improvements

Original idea comes from Jyri Sarha
( http://lists.xiph.org/pipermail/speex-dev/2011-September/008243.html ).
Patch was discovered by Branislav Katreniak
( branislav.katreniak@streamunlimited.com ) for StreamUnlimited
( http://streamunlimited.com/ ). Tests showed up to 5x speed increase in
the resampler in the 44.1<->48kHz case.
I added the sinc-filter-mode and sinc-filter-auto-threshold properties
and the auto mode threshold tests, and adapted the code to GStreamer 1.0.

Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
This commit is contained in:
Carlos Rafael Giani 2012-10-07 03:00:52 +02:00 committed by Sebastian Dröge
parent d8b42ae80c
commit c41faa3d8e
5 changed files with 222 additions and 19 deletions

View file

@ -25,6 +25,14 @@
* audioresample resamples raw audio buffers to different sample rates using * audioresample resamples raw audio buffers to different sample rates using
* a configurable windowing function to enhance quality. * a configurable windowing function to enhance quality.
* *
* By default, the resampler uses a reduced sinc table, with cubic interpolation filling in
* the gaps. This ensures that the table does not become too big. However, the interpolation
* increases the CPU usage considerably. As an alternative, a full sinc table can be used.
* Doing so can drastically reduce CPU usage (4x faster with 44.1 -> 48 kHz conversions for
* example), at the cost of increased memory consumption, plus the sinc table takes longer
* to initialize when the element is created. A third mode exists, which uses the full table
* unless said table would become too large, in which case the interpolated one is used instead.
*
* <refsect2> * <refsect2>
* <title>Example launch line</title> * <title>Example launch line</title>
* |[ * |[
@ -62,10 +70,14 @@ GST_DEBUG_CATEGORY (audio_resample_debug);
GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE); GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
#endif #endif
#define GST_TYPE_SPEEX_RESAMPLER_SINC_FILTER_MODE (speex_resampler_sinc_filter_mode_get_type ())
enum enum
{ {
PROP_0, PROP_0,
PROP_QUALITY PROP_QUALITY,
PROP_SINC_FILTER_MODE,
PROP_SINC_FILTER_AUTO_THRESHOLD
}; };
#if G_BYTE_ORDER == G_LITTLE_ENDIAN #if G_BYTE_ORDER == G_LITTLE_ENDIAN
@ -104,6 +116,9 @@ static void gst_audio_resample_set_property (GObject * object,
static void gst_audio_resample_get_property (GObject * object, static void gst_audio_resample_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec); guint prop_id, GValue * value, GParamSpec * pspec);
static GType
speex_resampler_sinc_filter_mode_get_type (void);
/* vmethods */ /* vmethods */
static gboolean gst_audio_resample_get_unit_size (GstBaseTransform * base, static gboolean gst_audio_resample_get_unit_size (GstBaseTransform * base,
GstCaps * caps, gsize * size); GstCaps * caps, gsize * size);
@ -144,6 +159,20 @@ gst_audio_resample_class_init (GstAudioResampleClass * klass)
SPEEX_RESAMPLER_QUALITY_DEFAULT, SPEEX_RESAMPLER_QUALITY_DEFAULT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_SINC_FILTER_MODE,
g_param_spec_enum ("sinc-filter-mode", "Sinc filter table mode",
"What sinc filter table mode to use",
GST_TYPE_SPEEX_RESAMPLER_SINC_FILTER_MODE,
SPEEX_RESAMPLER_SINC_FILTER_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_SINC_FILTER_AUTO_THRESHOLD,
g_param_spec_uint ("sinc-filter-auto-threshold", "Sinc filter auto mode threshold",
"Memory usage threshold to use if sinc filter mode is AUTO, given in bytes",
0, G_MAXUINT,
SPEEX_RESAMPLER_SINC_FILTER_AUTO_THRESHOLD_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_pad_template (gstelement_class, gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&gst_audio_resample_src_template)); gst_static_pad_template_get (&gst_audio_resample_src_template));
gst_element_class_add_pad_template (gstelement_class, gst_element_class_add_pad_template (gstelement_class,
@ -181,6 +210,8 @@ gst_audio_resample_init (GstAudioResample * resample)
GstBaseTransform *trans = GST_BASE_TRANSFORM (resample); GstBaseTransform *trans = GST_BASE_TRANSFORM (resample);
resample->quality = SPEEX_RESAMPLER_QUALITY_DEFAULT; resample->quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
resample->sinc_filter_mode = SPEEX_RESAMPLER_SINC_FILTER_DEFAULT;
resample->sinc_filter_auto_threshold = SPEEX_RESAMPLER_SINC_FILTER_AUTO_THRESHOLD_DEFAULT;
gst_base_transform_set_gap_aware (trans, TRUE); gst_base_transform_set_gap_aware (trans, TRUE);
gst_pad_set_query_function (trans->srcpad, gst_audio_resample_query); gst_pad_set_query_function (trans->srcpad, gst_audio_resample_query);
@ -349,13 +380,16 @@ gst_audio_resample_get_funcs (gint width, gboolean fp)
static SpeexResamplerState * static SpeexResamplerState *
gst_audio_resample_init_state (GstAudioResample * resample, gint width, gst_audio_resample_init_state (GstAudioResample * resample, gint width,
gint channels, gint inrate, gint outrate, gint quality, gboolean fp) gint channels, gint inrate, gint outrate, gint quality, gboolean fp,
SpeexResamplerSincFilterMode sinc_filter_mode,
guint32 sinc_filter_auto_threshold)
{ {
SpeexResamplerState *ret = NULL; SpeexResamplerState *ret = NULL;
gint err = RESAMPLER_ERR_SUCCESS; gint err = RESAMPLER_ERR_SUCCESS;
const SpeexResampleFuncs *funcs = gst_audio_resample_get_funcs (width, fp); const SpeexResampleFuncs *funcs = gst_audio_resample_get_funcs (width, fp);
ret = funcs->init (channels, inrate, outrate, quality, &err); ret = funcs->init (channels, inrate, outrate, quality,
sinc_filter_mode, sinc_filter_auto_threshold, &err);
if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) { if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) {
GST_ERROR_OBJECT (resample, "Failed to create resampler state: %s", GST_ERROR_OBJECT (resample, "Failed to create resampler state: %s",
@ -363,6 +397,11 @@ gst_audio_resample_init_state (GstAudioResample * resample, gint width,
return NULL; return NULL;
} }
if (sinc_filter_mode == SPEEX_RESAMPLER_SINC_FILTER_AUTO) {
GST_INFO_OBJECT (resample, "Using the %s sinc filter table",
funcs->get_sinc_filter_mode(ret) ? "full" : "interpolated");
}
funcs->skip_zeros (ret); funcs->skip_zeros (ret);
return ret; return ret;
@ -370,7 +409,9 @@ gst_audio_resample_init_state (GstAudioResample * resample, gint width,
static gboolean static gboolean
gst_audio_resample_update_state (GstAudioResample * resample, gint width, gst_audio_resample_update_state (GstAudioResample * resample, gint width,
gint channels, gint inrate, gint outrate, gint quality, gboolean fp) gint channels, gint inrate, gint outrate, gint quality, gboolean fp,
SpeexResamplerSincFilterMode sinc_filter_mode,
guint32 sinc_filter_auto_threshold)
{ {
gboolean ret = TRUE; gboolean ret = TRUE;
gboolean updated_latency = FALSE; gboolean updated_latency = FALSE;
@ -381,11 +422,12 @@ gst_audio_resample_update_state (GstAudioResample * resample, gint width,
if (resample->state == NULL) { if (resample->state == NULL) {
ret = TRUE; ret = TRUE;
} else if (resample->channels != channels || fp != resample->fp } else if (resample->channels != channels || fp != resample->fp
|| width != resample->width) { || width != resample->width || sinc_filter_mode != resample->sinc_filter_mode
|| sinc_filter_auto_threshold != resample->sinc_filter_auto_threshold) {
resample->funcs->destroy (resample->state); resample->funcs->destroy (resample->state);
resample->state = resample->state =
gst_audio_resample_init_state (resample, width, channels, inrate, gst_audio_resample_init_state (resample, width, channels, inrate,
outrate, quality, fp); outrate, quality, fp, sinc_filter_mode, sinc_filter_auto_threshold);
resample->funcs = gst_audio_resample_get_funcs (width, fp); resample->funcs = gst_audio_resample_get_funcs (width, fp);
ret = (resample->state != NULL); ret = (resample->state != NULL);
@ -417,6 +459,8 @@ gst_audio_resample_update_state (GstAudioResample * resample, gint width,
resample->quality = quality; resample->quality = quality;
resample->inrate = inrate; resample->inrate = inrate;
resample->outrate = outrate; resample->outrate = outrate;
resample->sinc_filter_mode = sinc_filter_mode;
resample->sinc_filter_auto_threshold = sinc_filter_auto_threshold;
if (updated_latency) if (updated_latency)
gst_element_post_message (GST_ELEMENT (resample), gst_element_post_message (GST_ELEMENT (resample),
@ -526,7 +570,8 @@ gst_audio_resample_set_caps (GstBaseTransform * base, GstCaps * incaps,
ret = ret =
gst_audio_resample_update_state (resample, width, channels, inrate, gst_audio_resample_update_state (resample, width, channels, inrate,
outrate, resample->quality, fp); outrate, resample->quality, fp, resample->sinc_filter_mode,
resample->sinc_filter_auto_threshold);
if (G_UNLIKELY (!ret)) if (G_UNLIKELY (!ret))
return FALSE; return FALSE;
@ -1126,7 +1171,8 @@ gst_audio_resample_transform (GstBaseTransform * base, GstBuffer * inbuf,
if (G_UNLIKELY (!(resample->state = if (G_UNLIKELY (!(resample->state =
gst_audio_resample_init_state (resample, resample->width, gst_audio_resample_init_state (resample, resample->width,
resample->channels, resample->inrate, resample->outrate, resample->channels, resample->inrate, resample->outrate,
resample->quality, resample->fp)))) resample->quality, resample->fp, resample->sinc_filter_mode,
resample->sinc_filter_auto_threshold))))
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
resample->funcs = resample->funcs =
@ -1279,8 +1325,31 @@ gst_audio_resample_set_property (GObject * object, guint prop_id,
gst_audio_resample_update_state (resample, resample->width, gst_audio_resample_update_state (resample, resample->width,
resample->channels, resample->inrate, resample->outrate, resample->channels, resample->inrate, resample->outrate,
quality, resample->fp); quality, resample->fp, resample->sinc_filter_mode,
resample->sinc_filter_auto_threshold);
break; break;
case PROP_SINC_FILTER_MODE: {
/* FIXME locking! */
SpeexResamplerSincFilterMode sinc_filter_mode = g_value_get_enum (value);
gst_audio_resample_update_state (resample, resample->width,
resample->channels, resample->inrate, resample->outrate,
resample->quality, resample->fp, sinc_filter_mode,
resample->sinc_filter_auto_threshold);
break;
}
case PROP_SINC_FILTER_AUTO_THRESHOLD: {
/* FIXME locking! */
guint32 sinc_filter_auto_threshold = g_value_get_uint (value);
gst_audio_resample_update_state (resample, resample->width,
resample->channels, resample->inrate, resample->outrate,
resample->quality, resample->fp, resample->sinc_filter_mode,
sinc_filter_auto_threshold);
break;
}
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -1299,12 +1368,39 @@ gst_audio_resample_get_property (GObject * object, guint prop_id,
case PROP_QUALITY: case PROP_QUALITY:
g_value_set_int (value, resample->quality); g_value_set_int (value, resample->quality);
break; break;
case PROP_SINC_FILTER_MODE:
g_value_set_enum(value, resample->sinc_filter_mode);
break;
case PROP_SINC_FILTER_AUTO_THRESHOLD:
g_value_set_uint(value, resample->sinc_filter_auto_threshold);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
} }
} }
static GType
speex_resampler_sinc_filter_mode_get_type (void)
{
static GType speex_resampler_sinc_filter_mode_type = 0;
if (!speex_resampler_sinc_filter_mode_type) {
static GEnumValue sinc_filter_modes[] = {
{ SPEEX_RESAMPLER_SINC_FILTER_INTERPOLATED, "Use interpolated sinc table", "interpolated" },
{ SPEEX_RESAMPLER_SINC_FILTER_FULL, "Use full sinc table", "full" },
{ SPEEX_RESAMPLER_SINC_FILTER_AUTO, "Use full table if table size below threshold", "auto" },
{ 0, NULL, NULL },
};
speex_resampler_sinc_filter_mode_type = g_enum_register_static (
"SpeexResamplerSincFilterMode",
sinc_filter_modes);
}
return speex_resampler_sinc_filter_mode_type;
}
/* FIXME: should have a benchmark fallback for the case where orc is disabled */ /* FIXME: should have a benchmark fallback for the case where orc is disabled */
#if defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC) #if defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC)
@ -1367,13 +1463,19 @@ _benchmark_integer_resampling (void)
orc_profile_init (&a); orc_profile_init (&a);
orc_profile_init (&b); orc_profile_init (&b);
sta = resample_float_resampler_init (1, 48000, 24000, 4, NULL); sta = resample_float_resampler_init (1, 48000, 24000, 4,
SPEEX_RESAMPLER_SINC_FILTER_INTERPOLATED,
SPEEX_RESAMPLER_SINC_FILTER_AUTO_THRESHOLD_DEFAULT,
NULL);
if (sta == NULL) { if (sta == NULL) {
GST_ERROR ("Failed to create float resampler state"); GST_ERROR ("Failed to create float resampler state");
return FALSE; return FALSE;
} }
stb = resample_int_resampler_init (1, 48000, 24000, 4, NULL); stb = resample_int_resampler_init (1, 48000, 24000, 4,
SPEEX_RESAMPLER_SINC_FILTER_INTERPOLATED,
SPEEX_RESAMPLER_SINC_FILTER_AUTO_THRESHOLD_DEFAULT,
NULL);
if (stb == NULL) { if (stb == NULL) {
resample_float_resampler_destroy (sta); resample_float_resampler_destroy (sta);
GST_ERROR ("Failed to create int resampler state"); GST_ERROR ("Failed to create int resampler state");

View file

@ -77,6 +77,9 @@ struct _GstAudioResample {
gint inrate; gint inrate;
gint outrate; gint outrate;
SpeexResamplerSincFilterMode sinc_filter_mode;
guint32 sinc_filter_auto_threshold;
guint8 *tmp_in; guint8 *tmp_in;
guint tmp_in_size; guint tmp_in_size;

View file

@ -184,6 +184,7 @@ struct SpeexResamplerState_
spx_uint32_t oversample; spx_uint32_t oversample;
int initialised; int initialised;
int started; int started;
int use_full_sinc_table;
/* These are per-channel */ /* These are per-channel */
spx_int32_t *last_sample; spx_int32_t *last_sample;
@ -754,7 +755,8 @@ update_filter (SpeexResamplerState * st)
} }
/* Choose the resampling type that requires the least amount of memory */ /* Choose the resampling type that requires the least amount of memory */
if (st->den_rate <= st->oversample) { /* Or if the full sinc table is explicitely requested, use that */
if (st->use_full_sinc_table || (st->den_rate <= st->oversample)) {
spx_uint32_t i; spx_uint32_t i;
if (!st->sinc_table) if (!st->sinc_table)
st->sinc_table = st->sinc_table =
@ -918,10 +920,11 @@ update_filter (SpeexResamplerState * st)
EXPORT SpeexResamplerState * EXPORT SpeexResamplerState *
speex_resampler_init (spx_uint32_t nb_channels, spx_uint32_t in_rate, speex_resampler_init (spx_uint32_t nb_channels, spx_uint32_t in_rate,
spx_uint32_t out_rate, int quality, int *err) spx_uint32_t out_rate, int quality, SpeexResamplerSincFilterMode sinc_filter_mode,
spx_uint32_t sinc_filter_auto_threshold, int *err)
{ {
return speex_resampler_init_frac (nb_channels, in_rate, out_rate, in_rate, return speex_resampler_init_frac (nb_channels, in_rate, out_rate, in_rate,
out_rate, quality, err); out_rate, quality, sinc_filter_mode, sinc_filter_auto_threshold, err);
} }
#if defined HAVE_ORC && !defined DISABLE_ORC #if defined HAVE_ORC && !defined DISABLE_ORC
@ -940,15 +943,33 @@ check_insn_set (SpeexResamplerState * st, const char *name)
EXPORT SpeexResamplerState * EXPORT SpeexResamplerState *
speex_resampler_init_frac (spx_uint32_t nb_channels, spx_uint32_t ratio_num, speex_resampler_init_frac (spx_uint32_t nb_channels, spx_uint32_t ratio_num,
spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate,
int quality, int *err) int quality, SpeexResamplerSincFilterMode sinc_filter_mode,
spx_uint32_t sinc_filter_auto_threshold, int *err)
{ {
spx_uint32_t i; spx_uint32_t i;
SpeexResamplerState *st; SpeexResamplerState *st;
int use_full_sinc_table = 0;
if (quality > 10 || quality < 0) { if (quality > 10 || quality < 0) {
if (err) if (err)
*err = RESAMPLER_ERR_INVALID_ARG; *err = RESAMPLER_ERR_INVALID_ARG;
return NULL; return NULL;
} }
switch (sinc_filter_mode) {
case RESAMPLER_SINC_FILTER_INTERPOLATED:
use_full_sinc_table = 0;
break;
case RESAMPLER_SINC_FILTER_FULL:
use_full_sinc_table = 1;
break;
case RESAMPLER_SINC_FILTER_AUTO:
/* Handled below */
break;
default:
if (err)
*err = RESAMPLER_ERR_INVALID_ARG;
return NULL;
}
st = (SpeexResamplerState *) speex_alloc (sizeof (SpeexResamplerState)); st = (SpeexResamplerState *) speex_alloc (sizeof (SpeexResamplerState));
st->initialised = 0; st->initialised = 0;
st->started = 0; st->started = 0;
@ -962,6 +983,7 @@ speex_resampler_init_frac (spx_uint32_t nb_channels, spx_uint32_t ratio_num,
st->filt_len = 0; st->filt_len = 0;
st->mem = 0; st->mem = 0;
st->resampler_ptr = 0; st->resampler_ptr = 0;
st->use_full_sinc_table = use_full_sinc_table;
st->cutoff = 1.f; st->cutoff = 1.f;
st->nb_channels = nb_channels; st->nb_channels = nb_channels;
@ -1004,6 +1026,16 @@ speex_resampler_init_frac (spx_uint32_t nb_channels, spx_uint32_t ratio_num,
speex_resampler_set_quality (st, quality); speex_resampler_set_quality (st, quality);
speex_resampler_set_rate_frac (st, ratio_num, ratio_den, in_rate, out_rate); speex_resampler_set_rate_frac (st, ratio_num, ratio_den, in_rate, out_rate);
if (sinc_filter_mode == RESAMPLER_SINC_FILTER_AUTO) {
/*
Estimate how big the filter table would become if the full mode were to be used
calculations used correspond to the ones in update_filter()
if the size is bigger than the threshold, use interpolated sinc instead
*/
spx_uint32_t base_filter_length = st->filt_len = quality_map[st->quality].base_length;
spx_uint32_t filter_table_size = base_filter_length * st->den_rate * sizeof(spx_uint16_t);
st->use_full_sinc_table = (filter_table_size > sinc_filter_auto_threshold) ? 0 : 1;
}
update_filter (st); update_filter (st);
@ -1391,6 +1423,12 @@ speex_resampler_get_filt_len (SpeexResamplerState * st)
return st->filt_len; return st->filt_len;
} }
EXPORT int
speex_resampler_get_sinc_filter_mode (SpeexResamplerState * st)
{
return st->use_full_sinc_table;
}
EXPORT int EXPORT int
speex_resampler_skip_zeros (SpeexResamplerState * st) speex_resampler_skip_zeros (SpeexResamplerState * st)
{ {

View file

@ -74,6 +74,7 @@
#define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency) #define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency)
#define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency) #define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency)
#define speex_resampler_get_filt_len CAT_PREFIX(RANDOM_PREFIX,_resampler_get_filt_len) #define speex_resampler_get_filt_len CAT_PREFIX(RANDOM_PREFIX,_resampler_get_filt_len)
#define speex_resampler_get_sinc_filter_mode CAT_PREFIX(RANDOM_PREFIX,_resampler_get_sinc_filter_mode)
#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros) #define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros)
#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem) #define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror) #define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
@ -113,6 +114,15 @@ enum {
RESAMPLER_ERR_MAX_ERROR RESAMPLER_ERR_MAX_ERROR
}; };
typedef enum {
RESAMPLER_SINC_FILTER_INTERPOLATED = 0,
RESAMPLER_SINC_FILTER_FULL = 1,
RESAMPLER_SINC_FILTER_AUTO = 2
} SpeexResamplerSincFilterMode;
#define RESAMPLER_SINC_FILTER_DEFAULT RESAMPLER_SINC_FILTER_INTERPOLATED
#define RESAMPLER_SINC_FILTER_AUTO_THRESHOLD_DEFAULT (1 * 1048576)
struct SpeexResamplerState_; struct SpeexResamplerState_;
typedef struct SpeexResamplerState_ SpeexResamplerState; typedef struct SpeexResamplerState_ SpeexResamplerState;
@ -122,13 +132,23 @@ typedef struct SpeexResamplerState_ SpeexResamplerState;
* @param out_rate Output sampling rate (integer number of Hz). * @param out_rate Output sampling rate (integer number of Hz).
* @param quality Resampling quality between 0 and 10, where 0 has poor quality * @param quality Resampling quality between 0 and 10, where 0 has poor quality
* and 10 has very high quality. * and 10 has very high quality.
* @param sinc_filter_mode Sinc filter table mode to use
* @param sinc_filter_auto_threshold Threshold to use if sinc filter mode is auto, in bytes
* @return Newly created resampler state * @return Newly created resampler state
* @retval NULL Error: not enough memory * @retval NULL Error: not enough memory
*
* If a full filter table would be larger than the auto threshold, and sinc_filter_mode is AUTO,
* the resample uses the interpolated mode instead
*
* @note A full sinc table can significantly improve the resampler's performance, but calculating the table
* takes longer, as opposed to the interpolated variant
*/ */
SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
spx_uint32_t in_rate, spx_uint32_t in_rate,
spx_uint32_t out_rate, spx_uint32_t out_rate,
int quality, int quality,
SpeexResamplerSincFilterMode sinc_filter_mode,
spx_uint32_t sinc_filter_auto_threshold,
int *err); int *err);
/** Create a new resampler with fractional input/output rates. The sampling /** Create a new resampler with fractional input/output rates. The sampling
@ -141,8 +161,16 @@ SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz). * @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
* @param quality Resampling quality between 0 and 10, where 0 has poor quality * @param quality Resampling quality between 0 and 10, where 0 has poor quality
* and 10 has very high quality. * and 10 has very high quality.
* @param sinc_filter_mode Sinc filter table mode to use
* @param sinc_filter_auto_threshold Threshold to use if sinc filter mode is auto, in bytes
* @return Newly created resampler state * @return Newly created resampler state
* @retval NULL Error: not enough memory * @retval NULL Error: not enough memory
*
* If a full filter table would be larger than the auto threshold, and sinc_filter_mode is AUTO,
* the resample uses the interpolated mode instead
*
* @note A full sinc table can significantly improve the resampler's performance, but calculating the table
* takes longer, as opposed to the interpolated variant
*/ */
SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels,
spx_uint32_t ratio_num, spx_uint32_t ratio_num,
@ -150,6 +178,8 @@ SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels,
spx_uint32_t in_rate, spx_uint32_t in_rate,
spx_uint32_t out_rate, spx_uint32_t out_rate,
int quality, int quality,
SpeexResamplerSincFilterMode sinc_filter_mode,
spx_uint32_t sinc_filter_auto_threshold,
int *err); int *err);
/** Destroy a resampler state. /** Destroy a resampler state.
@ -339,6 +369,12 @@ int speex_resampler_get_output_latency(SpeexResamplerState *st);
*/ */
int speex_resampler_get_filt_len(SpeexResamplerState *st); int speex_resampler_get_filt_len(SpeexResamplerState *st);
/** Returns 1 if the full sinc filter table is used, 0 if the interpolated one is used
* @param st Resampler state
* @return Sinc filter mode
*/
int speex_resampler_get_sinc_filter_mode(SpeexResamplerState *st);
/** Make sure that the first samples to go out of the resamplers don't have /** Make sure that the first samples to go out of the resamplers don't have
* leading zeros. This is only useful before starting to use a newly created * leading zeros. This is only useful before starting to use a newly created
* resampler. It is recommended to use that when resampling an audio file, as * resampler. It is recommended to use that when resampling an audio file, as

View file

@ -26,6 +26,9 @@
#define SPEEX_RESAMPLER_QUALITY_VOIP 3 #define SPEEX_RESAMPLER_QUALITY_VOIP 3
#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5 #define SPEEX_RESAMPLER_QUALITY_DESKTOP 5
#define SPEEX_RESAMPLER_SINC_FILTER_DEFAULT SPEEX_RESAMPLER_SINC_FILTER_INTERPOLATED
#define SPEEX_RESAMPLER_SINC_FILTER_AUTO_THRESHOLD_DEFAULT (1 * 1048576)
enum enum
{ {
RESAMPLER_ERR_SUCCESS = 0, RESAMPLER_ERR_SUCCESS = 0,
@ -37,11 +40,19 @@ enum
RESAMPLER_ERR_MAX_ERROR RESAMPLER_ERR_MAX_ERROR
}; };
typedef enum {
SPEEX_RESAMPLER_SINC_FILTER_INTERPOLATED = 0,
SPEEX_RESAMPLER_SINC_FILTER_FULL = 1,
SPEEX_RESAMPLER_SINC_FILTER_AUTO = 2
} SpeexResamplerSincFilterMode;
typedef struct SpeexResamplerState_ SpeexResamplerState; typedef struct SpeexResamplerState_ SpeexResamplerState;
typedef struct { typedef struct {
SpeexResamplerState *(*init) (guint32 nb_channels, SpeexResamplerState *(*init) (guint32 nb_channels,
guint32 in_rate, guint32 out_rate, gint quality, gint * err); guint32 in_rate, guint32 out_rate, gint quality,
SpeexResamplerSincFilterMode sinc_filter_mode,
guint32 sinc_filter_auto_threshold, gint * err);
void (*destroy) (SpeexResamplerState * st); void (*destroy) (SpeexResamplerState * st);
int (*process) (SpeexResamplerState * int (*process) (SpeexResamplerState *
st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len); st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len);
@ -53,6 +64,7 @@ typedef struct {
guint32 * ratio_num, guint32 * ratio_den); guint32 * ratio_num, guint32 * ratio_den);
int (*get_input_latency) (SpeexResamplerState * st); int (*get_input_latency) (SpeexResamplerState * st);
int (*get_filt_len) (SpeexResamplerState * st); int (*get_filt_len) (SpeexResamplerState * st);
int (*get_sinc_filter_mode) (SpeexResamplerState * st);
int (*set_quality) (SpeexResamplerState * st, gint quality); int (*set_quality) (SpeexResamplerState * st, gint quality);
int (*reset_mem) (SpeexResamplerState * st); int (*reset_mem) (SpeexResamplerState * st);
int (*skip_zeros) (SpeexResamplerState * st); int (*skip_zeros) (SpeexResamplerState * st);
@ -61,7 +73,9 @@ typedef struct {
} SpeexResampleFuncs; } SpeexResampleFuncs;
SpeexResamplerState *resample_float_resampler_init (guint32 nb_channels, SpeexResamplerState *resample_float_resampler_init (guint32 nb_channels,
guint32 in_rate, guint32 out_rate, gint quality, gint * err); guint32 in_rate, guint32 out_rate, gint quality,
SpeexResamplerSincFilterMode sinc_filter_mode,
guint32 sinc_filter_auto_threshold, gint * err);
void resample_float_resampler_destroy (SpeexResamplerState * st); void resample_float_resampler_destroy (SpeexResamplerState * st);
int resample_float_resampler_process_interleaved_float (SpeexResamplerState * int resample_float_resampler_process_interleaved_float (SpeexResamplerState *
st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len); st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len);
@ -73,6 +87,7 @@ void resample_float_resampler_get_ratio (SpeexResamplerState * st,
guint32 * ratio_num, guint32 * ratio_den); guint32 * ratio_num, guint32 * ratio_den);
int resample_float_resampler_get_input_latency (SpeexResamplerState * st); int resample_float_resampler_get_input_latency (SpeexResamplerState * st);
int resample_float_resampler_get_filt_len (SpeexResamplerState * st); int resample_float_resampler_get_filt_len (SpeexResamplerState * st);
int resample_float_resampler_get_sinc_filter_mode (SpeexResamplerState * st);
int resample_float_resampler_set_quality (SpeexResamplerState * st, gint quality); int resample_float_resampler_set_quality (SpeexResamplerState * st, gint quality);
int resample_float_resampler_reset_mem (SpeexResamplerState * st); int resample_float_resampler_reset_mem (SpeexResamplerState * st);
int resample_float_resampler_skip_zeros (SpeexResamplerState * st); int resample_float_resampler_skip_zeros (SpeexResamplerState * st);
@ -88,6 +103,7 @@ static const SpeexResampleFuncs float_funcs =
resample_float_resampler_get_ratio, resample_float_resampler_get_ratio,
resample_float_resampler_get_input_latency, resample_float_resampler_get_input_latency,
resample_float_resampler_get_filt_len, resample_float_resampler_get_filt_len,
resample_float_resampler_get_sinc_filter_mode,
resample_float_resampler_set_quality, resample_float_resampler_set_quality,
resample_float_resampler_reset_mem, resample_float_resampler_reset_mem,
resample_float_resampler_skip_zeros, resample_float_resampler_skip_zeros,
@ -96,7 +112,9 @@ static const SpeexResampleFuncs float_funcs =
}; };
SpeexResamplerState *resample_double_resampler_init (guint32 nb_channels, SpeexResamplerState *resample_double_resampler_init (guint32 nb_channels,
guint32 in_rate, guint32 out_rate, gint quality, gint * err); guint32 in_rate, guint32 out_rate, gint quality,
SpeexResamplerSincFilterMode sinc_filter_mode,
guint32 sinc_filter_auto_threshold, gint * err);
void resample_double_resampler_destroy (SpeexResamplerState * st); void resample_double_resampler_destroy (SpeexResamplerState * st);
int resample_double_resampler_process_interleaved_float (SpeexResamplerState * int resample_double_resampler_process_interleaved_float (SpeexResamplerState *
st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len); st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len);
@ -108,6 +126,7 @@ void resample_double_resampler_get_ratio (SpeexResamplerState * st,
guint32 * ratio_num, guint32 * ratio_den); guint32 * ratio_num, guint32 * ratio_den);
int resample_double_resampler_get_input_latency (SpeexResamplerState * st); int resample_double_resampler_get_input_latency (SpeexResamplerState * st);
int resample_double_resampler_get_filt_len (SpeexResamplerState * st); int resample_double_resampler_get_filt_len (SpeexResamplerState * st);
int resample_double_resampler_get_sinc_filter_mode (SpeexResamplerState * st);
int resample_double_resampler_set_quality (SpeexResamplerState * st, gint quality); int resample_double_resampler_set_quality (SpeexResamplerState * st, gint quality);
int resample_double_resampler_reset_mem (SpeexResamplerState * st); int resample_double_resampler_reset_mem (SpeexResamplerState * st);
int resample_double_resampler_skip_zeros (SpeexResamplerState * st); int resample_double_resampler_skip_zeros (SpeexResamplerState * st);
@ -123,6 +142,7 @@ static const SpeexResampleFuncs double_funcs =
resample_double_resampler_get_ratio, resample_double_resampler_get_ratio,
resample_double_resampler_get_input_latency, resample_double_resampler_get_input_latency,
resample_double_resampler_get_filt_len, resample_double_resampler_get_filt_len,
resample_double_resampler_get_sinc_filter_mode,
resample_double_resampler_set_quality, resample_double_resampler_set_quality,
resample_double_resampler_reset_mem, resample_double_resampler_reset_mem,
resample_double_resampler_skip_zeros, resample_double_resampler_skip_zeros,
@ -131,7 +151,9 @@ static const SpeexResampleFuncs double_funcs =
}; };
SpeexResamplerState *resample_int_resampler_init (guint32 nb_channels, SpeexResamplerState *resample_int_resampler_init (guint32 nb_channels,
guint32 in_rate, guint32 out_rate, gint quality, gint * err); guint32 in_rate, guint32 out_rate, gint quality,
SpeexResamplerSincFilterMode sinc_filter_mode,
guint32 sinc_filter_auto_threshold, gint * err);
void resample_int_resampler_destroy (SpeexResamplerState * st); void resample_int_resampler_destroy (SpeexResamplerState * st);
int resample_int_resampler_process_interleaved_int (SpeexResamplerState * int resample_int_resampler_process_interleaved_int (SpeexResamplerState *
st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len); st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len);
@ -143,6 +165,7 @@ void resample_int_resampler_get_ratio (SpeexResamplerState * st,
guint32 * ratio_num, guint32 * ratio_den); guint32 * ratio_num, guint32 * ratio_den);
int resample_int_resampler_get_input_latency (SpeexResamplerState * st); int resample_int_resampler_get_input_latency (SpeexResamplerState * st);
int resample_int_resampler_get_filt_len (SpeexResamplerState * st); int resample_int_resampler_get_filt_len (SpeexResamplerState * st);
int resample_int_resampler_get_sinc_filter_mode (SpeexResamplerState * st);
int resample_int_resampler_set_quality (SpeexResamplerState * st, gint quality); int resample_int_resampler_set_quality (SpeexResamplerState * st, gint quality);
int resample_int_resampler_reset_mem (SpeexResamplerState * st); int resample_int_resampler_reset_mem (SpeexResamplerState * st);
int resample_int_resampler_skip_zeros (SpeexResamplerState * st); int resample_int_resampler_skip_zeros (SpeexResamplerState * st);
@ -158,6 +181,7 @@ static const SpeexResampleFuncs int_funcs =
resample_int_resampler_get_ratio, resample_int_resampler_get_ratio,
resample_int_resampler_get_input_latency, resample_int_resampler_get_input_latency,
resample_int_resampler_get_filt_len, resample_int_resampler_get_filt_len,
resample_int_resampler_get_sinc_filter_mode,
resample_int_resampler_set_quality, resample_int_resampler_set_quality,
resample_int_resampler_reset_mem, resample_int_resampler_reset_mem,
resample_int_resampler_skip_zeros, resample_int_resampler_skip_zeros,