* audioresample resamples raw audio buffers to different sample rates using
* 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>
* <title>Example launch line</title>
* |[
GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
#endif
+#define GST_TYPE_SPEEX_RESAMPLER_SINC_FILTER_MODE (speex_resampler_sinc_filter_mode_get_type ())
+
enum
{
PROP_0,
- PROP_QUALITY
+ PROP_QUALITY,
+ PROP_SINC_FILTER_MODE,
+ PROP_SINC_FILTER_AUTO_THRESHOLD
};
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
static void gst_audio_resample_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
+static GType
+speex_resampler_sinc_filter_mode_get_type (void);
+
/* vmethods */
static gboolean gst_audio_resample_get_unit_size (GstBaseTransform * base,
GstCaps * caps, gsize * size);
SPEEX_RESAMPLER_QUALITY_DEFAULT,
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_static_pad_template_get (&gst_audio_resample_src_template));
gst_element_class_add_pad_template (gstelement_class,
GstBaseTransform *trans = GST_BASE_TRANSFORM (resample);
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_pad_set_query_function (trans->srcpad, gst_audio_resample_query);
static SpeexResamplerState *
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;
gint err = RESAMPLER_ERR_SUCCESS;
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)) {
GST_ERROR_OBJECT (resample, "Failed to create resampler state: %s",
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);
return ret;
static gboolean
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 updated_latency = FALSE;
if (resample->state == NULL) {
ret = TRUE;
} 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->state =
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);
ret = (resample->state != NULL);
resample->quality = quality;
resample->inrate = inrate;
resample->outrate = outrate;
+ resample->sinc_filter_mode = sinc_filter_mode;
+ resample->sinc_filter_auto_threshold = sinc_filter_auto_threshold;
if (updated_latency)
gst_element_post_message (GST_ELEMENT (resample),
ret =
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))
return FALSE;
if (G_UNLIKELY (!(resample->state =
gst_audio_resample_init_state (resample, resample->width,
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;
resample->funcs =
gst_audio_resample_update_state (resample, resample->width,
resample->channels, resample->inrate, resample->outrate,
- quality, resample->fp);
+ quality, resample->fp, resample->sinc_filter_mode,
+ resample->sinc_filter_auto_threshold);
+ 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:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_QUALITY:
g_value_set_int (value, resample->quality);
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:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
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 */
#if defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC)
orc_profile_init (&a);
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) {
GST_ERROR ("Failed to create float resampler state");
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) {
resample_float_resampler_destroy (sta);
GST_ERROR ("Failed to create int resampler state");
gint inrate;
gint outrate;
+ SpeexResamplerSincFilterMode sinc_filter_mode;
+ guint32 sinc_filter_auto_threshold;
+
guint8 *tmp_in;
guint tmp_in_size;
spx_uint32_t oversample;
int initialised;
int started;
+ int use_full_sinc_table;
/* These are per-channel */
spx_int32_t *last_sample;
}
/* 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;
if (!st->sinc_table)
st->sinc_table =
EXPORT SpeexResamplerState *
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,
- out_rate, quality, err);
+ out_rate, quality, sinc_filter_mode, sinc_filter_auto_threshold, err);
}
#if defined HAVE_ORC && !defined DISABLE_ORC
EXPORT SpeexResamplerState *
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,
- int quality, int *err)
+ int quality, SpeexResamplerSincFilterMode sinc_filter_mode,
+ spx_uint32_t sinc_filter_auto_threshold, int *err)
{
spx_uint32_t i;
SpeexResamplerState *st;
+ int use_full_sinc_table = 0;
if (quality > 10 || quality < 0) {
if (err)
*err = RESAMPLER_ERR_INVALID_ARG;
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->initialised = 0;
st->started = 0;
st->filt_len = 0;
st->mem = 0;
st->resampler_ptr = 0;
+ st->use_full_sinc_table = use_full_sinc_table;
st->cutoff = 1.f;
st->nb_channels = nb_channels;
speex_resampler_set_quality (st, quality);
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);
}
EXPORT int
+speex_resampler_get_sinc_filter_mode (SpeexResamplerState * st)
+{
+ return st->use_full_sinc_table;
+}
+
+EXPORT int
speex_resampler_skip_zeros (SpeexResamplerState * st)
{
spx_uint32_t i;
#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_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_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
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_;
typedef struct SpeexResamplerState_ SpeexResamplerState;
* @param out_rate Output sampling rate (integer number of Hz).
* @param quality Resampling quality between 0 and 10, where 0 has poor 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
* @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,
spx_uint32_t in_rate,
spx_uint32_t out_rate,
int quality,
+ SpeexResamplerSincFilterMode sinc_filter_mode,
+ spx_uint32_t sinc_filter_auto_threshold,
int *err);
/** Create a new resampler with fractional input/output rates. The sampling
* @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
* 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
* @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,
spx_uint32_t ratio_num,
spx_uint32_t in_rate,
spx_uint32_t out_rate,
int quality,
+ SpeexResamplerSincFilterMode sinc_filter_mode,
+ spx_uint32_t sinc_filter_auto_threshold,
int *err);
/** Destroy a resampler state.
*/
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
* 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
#define SPEEX_RESAMPLER_QUALITY_VOIP 3
#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
{
RESAMPLER_ERR_SUCCESS = 0,
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 *(*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);
int (*process) (SpeexResamplerState *
st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len);
guint32 * ratio_num, guint32 * ratio_den);
int (*get_input_latency) (SpeexResamplerState * st);
int (*get_filt_len) (SpeexResamplerState * st);
+ int (*get_sinc_filter_mode) (SpeexResamplerState * st);
int (*set_quality) (SpeexResamplerState * st, gint quality);
int (*reset_mem) (SpeexResamplerState * st);
int (*skip_zeros) (SpeexResamplerState * st);
} SpeexResampleFuncs;
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);
int resample_float_resampler_process_interleaved_float (SpeexResamplerState *
st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len);
guint32 * ratio_num, guint32 * ratio_den);
int resample_float_resampler_get_input_latency (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_reset_mem (SpeexResamplerState * st);
int resample_float_resampler_skip_zeros (SpeexResamplerState * st);
resample_float_resampler_get_ratio,
resample_float_resampler_get_input_latency,
resample_float_resampler_get_filt_len,
+ resample_float_resampler_get_sinc_filter_mode,
resample_float_resampler_set_quality,
resample_float_resampler_reset_mem,
resample_float_resampler_skip_zeros,
};
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);
int resample_double_resampler_process_interleaved_float (SpeexResamplerState *
st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len);
guint32 * ratio_num, guint32 * ratio_den);
int resample_double_resampler_get_input_latency (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_reset_mem (SpeexResamplerState * st);
int resample_double_resampler_skip_zeros (SpeexResamplerState * st);
resample_double_resampler_get_ratio,
resample_double_resampler_get_input_latency,
resample_double_resampler_get_filt_len,
+ resample_double_resampler_get_sinc_filter_mode,
resample_double_resampler_set_quality,
resample_double_resampler_reset_mem,
resample_double_resampler_skip_zeros,
};
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);
int resample_int_resampler_process_interleaved_int (SpeexResamplerState *
st, const guint8 * in, guint32 * in_len, guint8 * out, guint32 * out_len);
guint32 * ratio_num, guint32 * ratio_den);
int resample_int_resampler_get_input_latency (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_reset_mem (SpeexResamplerState * st);
int resample_int_resampler_skip_zeros (SpeexResamplerState * st);
resample_int_resampler_get_ratio,
resample_int_resampler_get_input_latency,
resample_int_resampler_get_filt_len,
+ resample_int_resampler_get_sinc_filter_mode,
resample_int_resampler_set_quality,
resample_int_resampler_reset_mem,
resample_int_resampler_skip_zeros,