gstreamer/gst/equalizer/gstiirequalizer.c
Luis de Bethencourt c884a3b3a5 equalizer: fix dynamic changes on bands
When we are in passthrough, the transform function doesn't run and if the
passthrough check is in this function it will never be deactivated. Fix this by
checking directly whenever a gain is changed.

Also set the passthrough to TRUE at init because the gains default to 0, so we
can passthrough until any gain property is changed.

https://bugzilla.gnome.org/show_bug.cgi?id=748068
2015-04-22 10:38:39 +01:00

920 lines
30 KiB
C

/* GStreamer
* Copyright (C) <2004> Benjamin Otte <otte@gnome.org>
* <2007> Stefan Kost <ensonic@users.sf.net>
* <2007> Sebastian Dröge <slomo@circular-chaos.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "gstiirequalizer.h"
#include "gstiirequalizernbands.h"
#include "gstiirequalizer3bands.h"
#include "gstiirequalizer10bands.h"
#include "gst/glib-compat-private.h"
GST_DEBUG_CATEGORY (equalizer_debug);
#define GST_CAT_DEFAULT equalizer_debug
#define BANDS_LOCK(equ) g_mutex_lock(&equ->bands_lock)
#define BANDS_UNLOCK(equ) g_mutex_unlock(&equ->bands_lock)
static void gst_iir_equalizer_child_proxy_interface_init (gpointer g_iface,
gpointer iface_data);
static void gst_iir_equalizer_finalize (GObject * object);
static gboolean gst_iir_equalizer_setup (GstAudioFilter * filter,
const GstAudioInfo * info);
static GstFlowReturn gst_iir_equalizer_transform_ip (GstBaseTransform * btrans,
GstBuffer * buf);
static void set_passthrough (GstIirEqualizer * equ);
#define ALLOWED_CAPS \
"audio/x-raw," \
" format=(string) {"GST_AUDIO_NE(S16)","GST_AUDIO_NE(F32)"," \
GST_AUDIO_NE(F64)" }, " \
" rate=(int)[1000,MAX]," \
" channels=(int)[1,MAX]," \
" layout=(string)interleaved"
#define gst_iir_equalizer_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstIirEqualizer, gst_iir_equalizer,
GST_TYPE_AUDIO_FILTER,
G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
gst_iir_equalizer_child_proxy_interface_init)
G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL));
/* child object */
enum
{
PROP_GAIN = 1,
PROP_FREQ,
PROP_BANDWIDTH,
PROP_TYPE
};
typedef enum
{
BAND_TYPE_PEAK = 0,
BAND_TYPE_LOW_SHELF,
BAND_TYPE_HIGH_SHELF
} GstIirEqualizerBandType;
#define GST_TYPE_IIR_EQUALIZER_BAND_TYPE (gst_iir_equalizer_band_type_get_type ())
static GType
gst_iir_equalizer_band_type_get_type (void)
{
static GType gtype = 0;
if (gtype == 0) {
static const GEnumValue values[] = {
{BAND_TYPE_PEAK, "Peak filter (default for inner bands)", "peak"},
{BAND_TYPE_LOW_SHELF, "Low shelf filter (default for first band)",
"low-shelf"},
{BAND_TYPE_HIGH_SHELF, "High shelf filter (default for last band)",
"high-shelf"},
{0, NULL, NULL}
};
gtype = g_enum_register_static ("GstIirEqualizerBandType", values);
}
return gtype;
}
typedef struct _GstIirEqualizerBandClass GstIirEqualizerBandClass;
#define GST_TYPE_IIR_EQUALIZER_BAND \
(gst_iir_equalizer_band_get_type())
#define GST_IIR_EQUALIZER_BAND(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_IIR_EQUALIZER_BAND,GstIirEqualizerBand))
#define GST_IIR_EQUALIZER_BAND_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_IIR_EQUALIZER_BAND,GstIirEqualizerBandClass))
#define GST_IS_IIR_EQUALIZER_BAND(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_IIR_EQUALIZER_BAND))
#define GST_IS_IIR_EQUALIZER_BAND_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_IIR_EQUALIZER_BAND))
struct _GstIirEqualizerBand
{
GstObject object;
/*< private > */
/* center frequency and gain */
gdouble freq;
gdouble gain;
gdouble width;
GstIirEqualizerBandType type;
/* second order iir filter */
gdouble b1, b2; /* IIR coefficients for outputs */
gdouble a0, a1, a2; /* IIR coefficients for inputs */
};
struct _GstIirEqualizerBandClass
{
GstObjectClass parent_class;
};
static GType gst_iir_equalizer_band_get_type (void);
static void
gst_iir_equalizer_band_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstIirEqualizerBand *band = GST_IIR_EQUALIZER_BAND (object);
GstIirEqualizer *equ =
GST_IIR_EQUALIZER (gst_object_get_parent (GST_OBJECT (band)));
switch (prop_id) {
case PROP_GAIN:{
gdouble gain;
gain = g_value_get_double (value);
GST_DEBUG_OBJECT (band, "gain = %lf -> %lf", band->gain, gain);
if (gain != band->gain) {
BANDS_LOCK (equ);
equ->need_new_coefficients = TRUE;
band->gain = gain;
set_passthrough (equ);
BANDS_UNLOCK (equ);
GST_DEBUG_OBJECT (band, "changed gain = %lf ", band->gain);
}
break;
}
case PROP_FREQ:{
gdouble freq;
freq = g_value_get_double (value);
GST_DEBUG_OBJECT (band, "freq = %lf -> %lf", band->freq, freq);
if (freq != band->freq) {
BANDS_LOCK (equ);
equ->need_new_coefficients = TRUE;
band->freq = freq;
BANDS_UNLOCK (equ);
GST_DEBUG_OBJECT (band, "changed freq = %lf ", band->freq);
}
break;
}
case PROP_BANDWIDTH:{
gdouble width;
width = g_value_get_double (value);
GST_DEBUG_OBJECT (band, "width = %lf -> %lf", band->width, width);
if (width != band->width) {
BANDS_LOCK (equ);
equ->need_new_coefficients = TRUE;
band->width = width;
BANDS_UNLOCK (equ);
GST_DEBUG_OBJECT (band, "changed width = %lf ", band->width);
}
break;
}
case PROP_TYPE:{
GstIirEqualizerBandType type;
type = g_value_get_enum (value);
GST_DEBUG_OBJECT (band, "type = %d -> %d", band->type, type);
if (type != band->type) {
BANDS_LOCK (equ);
equ->need_new_coefficients = TRUE;
band->type = type;
BANDS_UNLOCK (equ);
GST_DEBUG_OBJECT (band, "changed type = %d ", band->type);
}
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
gst_object_unref (equ);
}
static void
gst_iir_equalizer_band_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstIirEqualizerBand *band = GST_IIR_EQUALIZER_BAND (object);
switch (prop_id) {
case PROP_GAIN:
g_value_set_double (value, band->gain);
break;
case PROP_FREQ:
g_value_set_double (value, band->freq);
break;
case PROP_BANDWIDTH:
g_value_set_double (value, band->width);
break;
case PROP_TYPE:
g_value_set_enum (value, band->type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_iir_equalizer_band_class_init (GstIirEqualizerBandClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gst_iir_equalizer_band_set_property;
gobject_class->get_property = gst_iir_equalizer_band_get_property;
g_object_class_install_property (gobject_class, PROP_GAIN,
g_param_spec_double ("gain", "gain",
"gain for the frequency band ranging from -24.0 dB to +12.0 dB",
-24.0, 12.0, 0.0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (gobject_class, PROP_FREQ,
g_param_spec_double ("freq", "freq",
"center frequency of the band",
0.0, 100000.0, 0.0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (gobject_class, PROP_BANDWIDTH,
g_param_spec_double ("bandwidth", "bandwidth",
"difference between bandedges in Hz",
0.0, 100000.0, 1.0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
g_object_class_install_property (gobject_class, PROP_TYPE,
g_param_spec_enum ("type", "Type",
"Filter type", GST_TYPE_IIR_EQUALIZER_BAND_TYPE,
BAND_TYPE_PEAK,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
}
static void
gst_iir_equalizer_band_init (GstIirEqualizerBand * band,
GstIirEqualizerBandClass * klass)
{
band->freq = 0.0;
band->gain = 0.0;
band->width = 1.0;
band->type = BAND_TYPE_PEAK;
}
static GType
gst_iir_equalizer_band_get_type (void)
{
static GType type = 0;
if (G_UNLIKELY (!type)) {
const GTypeInfo type_info = {
sizeof (GstIirEqualizerBandClass),
NULL,
NULL,
(GClassInitFunc) gst_iir_equalizer_band_class_init,
NULL,
NULL,
sizeof (GstIirEqualizerBand),
0,
(GInstanceInitFunc) gst_iir_equalizer_band_init,
};
type =
g_type_register_static (GST_TYPE_OBJECT, "GstIirEqualizerBand",
&type_info, 0);
}
return (type);
}
/* child proxy iface */
static GObject *
gst_iir_equalizer_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
guint index)
{
GstIirEqualizer *equ = GST_IIR_EQUALIZER (child_proxy);
GObject *ret;
BANDS_LOCK (equ);
if (G_UNLIKELY (index >= equ->freq_band_count)) {
BANDS_UNLOCK (equ);
g_return_val_if_fail (index < equ->freq_band_count, NULL);
}
ret = g_object_ref (equ->bands[index]);
BANDS_UNLOCK (equ);
GST_LOG_OBJECT (equ, "return child[%d] %" GST_PTR_FORMAT, index, ret);
return ret;
}
static guint
gst_iir_equalizer_child_proxy_get_children_count (GstChildProxy * child_proxy)
{
GstIirEqualizer *equ = GST_IIR_EQUALIZER (child_proxy);
GST_LOG ("we have %d children", equ->freq_band_count);
return equ->freq_band_count;
}
static void
gst_iir_equalizer_child_proxy_interface_init (gpointer g_iface,
gpointer iface_data)
{
GstChildProxyInterface *iface = g_iface;
GST_DEBUG ("initializing iface");
iface->get_child_by_index = gst_iir_equalizer_child_proxy_get_child_by_index;
iface->get_children_count = gst_iir_equalizer_child_proxy_get_children_count;
}
/* equalizer implementation */
static void
gst_iir_equalizer_class_init (GstIirEqualizerClass * klass)
{
GstAudioFilterClass *audio_filter_class = (GstAudioFilterClass *) klass;
GstBaseTransformClass *btrans_class = (GstBaseTransformClass *) klass;
GObjectClass *gobject_class = (GObjectClass *) klass;
GstCaps *caps;
gobject_class->finalize = gst_iir_equalizer_finalize;
audio_filter_class->setup = gst_iir_equalizer_setup;
btrans_class->transform_ip = gst_iir_equalizer_transform_ip;
btrans_class->transform_ip_on_passthrough = FALSE;
caps = gst_caps_from_string (ALLOWED_CAPS);
gst_audio_filter_class_add_pad_templates (audio_filter_class, caps);
gst_caps_unref (caps);
}
static void
gst_iir_equalizer_init (GstIirEqualizer * eq)
{
g_mutex_init (&eq->bands_lock);
/* Band gains are 0 by default, passthrough until they are changed */
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (eq), TRUE);
}
static void
gst_iir_equalizer_finalize (GObject * object)
{
GstIirEqualizer *equ = GST_IIR_EQUALIZER (object);
gint i;
for (i = 0; i < equ->freq_band_count; i++) {
if (equ->bands[i])
gst_object_unparent (GST_OBJECT (equ->bands[i]));
equ->bands[i] = NULL;
}
equ->freq_band_count = 0;
g_free (equ->bands);
g_free (equ->history);
g_mutex_clear (&equ->bands_lock);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/* Filter taken from
*
* The Equivalence of Various Methods of Computing
* Biquad Coefficients for Audio Parametric Equalizers
*
* by Robert Bristow-Johnson
*
* http://www.aes.org/e-lib/browse.cfm?elib=6326
* http://www.musicdsp.org/files/EQ-Coefficients.pdf
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
*
* The bandwidth method that we use here is the preferred
* one from this article transformed from octaves to frequency
* in Hz.
*/
static inline gdouble
arg_to_scale (gdouble arg)
{
return (pow (10.0, arg / 40.0));
}
static gdouble
calculate_omega (gdouble freq, gint rate)
{
gdouble omega;
if (freq / rate >= 0.5)
omega = G_PI;
else if (freq <= 0.0)
omega = 0.0;
else
omega = 2.0 * G_PI * (freq / rate);
return omega;
}
static gdouble
calculate_bw (GstIirEqualizerBand * band, gint rate)
{
gdouble bw = 0.0;
if (band->width / rate >= 0.5) {
/* If bandwidth == 0.5 the calculation below fails as tan(G_PI/2)
* is undefined. So set the bandwidth to a slightly smaller value.
*/
bw = G_PI - 0.00000001;
} else if (band->width <= 0.0) {
/* If bandwidth == 0 this band won't change anything so set
* the coefficients accordingly. The coefficient calculation
* below would create coefficients that for some reason amplify
* the band.
*/
band->a0 = 1.0;
band->a1 = 0.0;
band->a2 = 0.0;
band->b1 = 0.0;
band->b2 = 0.0;
} else {
bw = 2.0 * G_PI * (band->width / rate);
}
return bw;
}
static void
setup_peak_filter (GstIirEqualizer * equ, GstIirEqualizerBand * band)
{
gint rate = GST_AUDIO_FILTER_RATE (equ);
g_return_if_fail (rate);
{
gdouble gain, omega, bw;
gdouble alpha, alpha1, alpha2, b0;
gain = arg_to_scale (band->gain);
omega = calculate_omega (band->freq, rate);
bw = calculate_bw (band, rate);
if (bw == 0.0)
goto out;
alpha = tan (bw / 2.0);
alpha1 = alpha * gain;
alpha2 = alpha / gain;
b0 = (1.0 + alpha2);
band->a0 = (1.0 + alpha1) / b0;
band->a1 = (-2.0 * cos (omega)) / b0;
band->a2 = (1.0 - alpha1) / b0;
band->b1 = (2.0 * cos (omega)) / b0;
band->b2 = -(1.0 - alpha2) / b0;
out:
GST_INFO
("gain = %5.1f, width= %7.2f, freq = %7.2f, a0 = %7.5g, a1 = %7.5g, a2=%7.5g b1 = %7.5g, b2 = %7.5g",
band->gain, band->width, band->freq, band->a0, band->a1, band->a2,
band->b1, band->b2);
}
}
static void
setup_low_shelf_filter (GstIirEqualizer * equ, GstIirEqualizerBand * band)
{
gint rate = GST_AUDIO_FILTER_RATE (equ);
g_return_if_fail (rate);
{
gdouble gain, omega, bw;
gdouble alpha, delta, b0;
gdouble egp, egm;
gain = arg_to_scale (band->gain);
omega = calculate_omega (band->freq, rate);
bw = calculate_bw (band, rate);
if (bw == 0.0)
goto out;
egm = gain - 1.0;
egp = gain + 1.0;
alpha = tan (bw / 2.0);
delta = 2.0 * sqrt (gain) * alpha;
b0 = egp + egm * cos (omega) + delta;
band->a0 = ((egp - egm * cos (omega) + delta) * gain) / b0;
band->a1 = ((egm - egp * cos (omega)) * 2.0 * gain) / b0;
band->a2 = ((egp - egm * cos (omega) - delta) * gain) / b0;
band->b1 = ((egm + egp * cos (omega)) * 2.0) / b0;
band->b2 = -((egp + egm * cos (omega) - delta)) / b0;
out:
GST_INFO
("gain = %5.1f, width= %7.2f, freq = %7.2f, a0 = %7.5g, a1 = %7.5g, a2=%7.5g b1 = %7.5g, b2 = %7.5g",
band->gain, band->width, band->freq, band->a0, band->a1, band->a2,
band->b1, band->b2);
}
}
static void
setup_high_shelf_filter (GstIirEqualizer * equ, GstIirEqualizerBand * band)
{
gint rate = GST_AUDIO_FILTER_RATE (equ);
g_return_if_fail (rate);
{
gdouble gain, omega, bw;
gdouble alpha, delta, b0;
gdouble egp, egm;
gain = arg_to_scale (band->gain);
omega = calculate_omega (band->freq, rate);
bw = calculate_bw (band, rate);
if (bw == 0.0)
goto out;
egm = gain - 1.0;
egp = gain + 1.0;
alpha = tan (bw / 2.0);
delta = 2.0 * sqrt (gain) * alpha;
b0 = egp - egm * cos (omega) + delta;
band->a0 = ((egp + egm * cos (omega) + delta) * gain) / b0;
band->a1 = ((egm + egp * cos (omega)) * -2.0 * gain) / b0;
band->a2 = ((egp + egm * cos (omega) - delta) * gain) / b0;
band->b1 = ((egm - egp * cos (omega)) * -2.0) / b0;
band->b2 = -((egp - egm * cos (omega) - delta)) / b0;
out:
GST_INFO
("gain = %5.1f, width= %7.2f, freq = %7.2f, a0 = %7.5g, a1 = %7.5g, a2=%7.5g b1 = %7.5g, b2 = %7.5g",
band->gain, band->width, band->freq, band->a0, band->a1, band->a2,
band->b1, band->b2);
}
}
/* Must be called with bands_lock and transform lock! */
static void
set_passthrough (GstIirEqualizer * equ)
{
gint i;
gboolean passthrough = TRUE;
for (i = 0; i < equ->freq_band_count; i++) {
passthrough = passthrough && (equ->bands[i]->gain == 0.0);
}
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (equ), passthrough);
GST_DEBUG ("Passthrough mode: %d\n", passthrough);
}
/* Must be called with bands_lock and transform lock! */
static void
update_coefficients (GstIirEqualizer * equ)
{
gint i, n = equ->freq_band_count;
for (i = 0; i < n; i++) {
if (equ->bands[i]->type == BAND_TYPE_PEAK)
setup_peak_filter (equ, equ->bands[i]);
else if (equ->bands[i]->type == BAND_TYPE_LOW_SHELF)
setup_low_shelf_filter (equ, equ->bands[i]);
else
setup_high_shelf_filter (equ, equ->bands[i]);
}
equ->need_new_coefficients = FALSE;
}
/* Must be called with transform lock! */
static void
alloc_history (GstIirEqualizer * equ, const GstAudioInfo * info)
{
/* free + alloc = no memcpy */
g_free (equ->history);
equ->history =
g_malloc0 (equ->history_size * GST_AUDIO_INFO_CHANNELS (info) *
equ->freq_band_count);
}
void
gst_iir_equalizer_compute_frequencies (GstIirEqualizer * equ, guint new_count)
{
guint old_count, i;
gdouble freq0, freq1, step;
gchar name[20];
if (equ->freq_band_count == new_count)
return;
BANDS_LOCK (equ);
if (G_UNLIKELY (equ->freq_band_count == new_count)) {
BANDS_UNLOCK (equ);
return;
}
old_count = equ->freq_band_count;
equ->freq_band_count = new_count;
GST_DEBUG ("bands %u -> %u", old_count, new_count);
if (old_count < new_count) {
/* add new bands */
equ->bands = g_realloc (equ->bands, sizeof (GstObject *) * new_count);
for (i = old_count; i < new_count; i++) {
/* otherwise they get names like 'iirequalizerband5' */
sprintf (name, "band%u", i);
equ->bands[i] = g_object_new (GST_TYPE_IIR_EQUALIZER_BAND,
"name", name, NULL);
GST_DEBUG ("adding band[%d]=%p", i, equ->bands[i]);
gst_object_set_parent (GST_OBJECT (equ->bands[i]), GST_OBJECT (equ));
gst_child_proxy_child_added (GST_CHILD_PROXY (equ),
G_OBJECT (equ->bands[i]), name);
}
} else {
/* free unused bands */
for (i = new_count; i < old_count; i++) {
GST_DEBUG ("removing band[%d]=%p", i, equ->bands[i]);
gst_child_proxy_child_removed (GST_CHILD_PROXY (equ),
G_OBJECT (equ->bands[i]), GST_OBJECT_NAME (equ->bands[i]));
gst_object_unparent (GST_OBJECT (equ->bands[i]));
equ->bands[i] = NULL;
}
}
alloc_history (equ, GST_AUDIO_FILTER_INFO (equ));
/* set center frequencies and name band objects
* FIXME: arg! we can't change the name of parented objects :(
* application should read band->freq to get the name
*/
step = pow (HIGHEST_FREQ / LOWEST_FREQ, 1.0 / new_count);
freq0 = LOWEST_FREQ;
for (i = 0; i < new_count; i++) {
freq1 = freq0 * step;
if (i == 0)
equ->bands[i]->type = BAND_TYPE_LOW_SHELF;
else if (i == new_count - 1)
equ->bands[i]->type = BAND_TYPE_HIGH_SHELF;
else
equ->bands[i]->type = BAND_TYPE_PEAK;
equ->bands[i]->freq = freq0 + ((freq1 - freq0) / 2.0);
equ->bands[i]->width = freq1 - freq0;
GST_DEBUG ("band[%2d] = '%lf'", i, equ->bands[i]->freq);
g_object_notify (G_OBJECT (equ->bands[i]), "bandwidth");
g_object_notify (G_OBJECT (equ->bands[i]), "freq");
g_object_notify (G_OBJECT (equ->bands[i]), "type");
/*
if(equ->bands[i]->freq<10000.0)
sprintf (name,"%dHz",(gint)equ->bands[i]->freq);
else
sprintf (name,"%dkHz",(gint)(equ->bands[i]->freq/1000.0));
gst_object_set_name( GST_OBJECT (equ->bands[i]), name);
GST_DEBUG ("band[%2d] = '%s'",i,name);
*/
freq0 = freq1;
}
equ->need_new_coefficients = TRUE;
BANDS_UNLOCK (equ);
}
/* start of code that is type specific */
#define CREATE_OPTIMIZED_FUNCTIONS_INT(TYPE,BIG_TYPE,MIN_VAL,MAX_VAL) \
typedef struct { \
BIG_TYPE x1, x2; /* history of input values for a filter */ \
BIG_TYPE y1, y2; /* history of output values for a filter */ \
} SecondOrderHistory ## TYPE; \
\
static inline BIG_TYPE \
one_step_ ## TYPE (GstIirEqualizerBand *filter, \
SecondOrderHistory ## TYPE *history, BIG_TYPE input) \
{ \
/* calculate output */ \
BIG_TYPE output = filter->a0 * input + \
filter->a1 * history->x1 + filter->a2 * history->x2 + \
filter->b1 * history->y1 + filter->b2 * history->y2; \
/* update history */ \
history->y2 = history->y1; \
history->y1 = output; \
history->x2 = history->x1; \
history->x1 = input; \
\
return output; \
} \
\
static const guint \
history_size_ ## TYPE = sizeof (SecondOrderHistory ## TYPE); \
\
static void \
gst_iir_equ_process_ ## TYPE (GstIirEqualizer *equ, guint8 *data, \
guint size, guint channels) \
{ \
guint frames = size / channels / sizeof (TYPE); \
guint i, c, f, nf = equ->freq_band_count; \
BIG_TYPE cur; \
GstIirEqualizerBand **filters = equ->bands; \
\
for (i = 0; i < frames; i++) { \
SecondOrderHistory ## TYPE *history = equ->history; \
for (c = 0; c < channels; c++) { \
cur = *((TYPE *) data); \
for (f = 0; f < nf; f++) { \
cur = one_step_ ## TYPE (filters[f], history, cur); \
history++; \
} \
cur = CLAMP (cur, MIN_VAL, MAX_VAL); \
*((TYPE *) data) = (TYPE) floor (cur); \
data += sizeof (TYPE); \
} \
} \
}
#define CREATE_OPTIMIZED_FUNCTIONS(TYPE) \
typedef struct { \
TYPE x1, x2; /* history of input values for a filter */ \
TYPE y1, y2; /* history of output values for a filter */ \
} SecondOrderHistory ## TYPE; \
\
static inline TYPE \
one_step_ ## TYPE (GstIirEqualizerBand *filter, \
SecondOrderHistory ## TYPE *history, TYPE input) \
{ \
/* calculate output */ \
TYPE output = filter->a0 * input + filter->a1 * history->x1 + \
filter->a2 * history->x2 + filter->b1 * history->y1 + \
filter->b2 * history->y2; \
/* update history */ \
history->y2 = history->y1; \
history->y1 = output; \
history->x2 = history->x1; \
history->x1 = input; \
\
return output; \
} \
\
static const guint \
history_size_ ## TYPE = sizeof (SecondOrderHistory ## TYPE); \
\
static void \
gst_iir_equ_process_ ## TYPE (GstIirEqualizer *equ, guint8 *data, \
guint size, guint channels) \
{ \
guint frames = size / channels / sizeof (TYPE); \
guint i, c, f, nf = equ->freq_band_count; \
TYPE cur; \
GstIirEqualizerBand **filters = equ->bands; \
\
for (i = 0; i < frames; i++) { \
SecondOrderHistory ## TYPE *history = equ->history; \
for (c = 0; c < channels; c++) { \
cur = *((TYPE *) data); \
for (f = 0; f < nf; f++) { \
cur = one_step_ ## TYPE (filters[f], history, cur); \
history++; \
} \
*((TYPE *) data) = (TYPE) cur; \
data += sizeof (TYPE); \
} \
} \
}
CREATE_OPTIMIZED_FUNCTIONS_INT (gint16, gfloat, -32768.0, 32767.0);
CREATE_OPTIMIZED_FUNCTIONS (gfloat);
CREATE_OPTIMIZED_FUNCTIONS (gdouble);
static GstFlowReturn
gst_iir_equalizer_transform_ip (GstBaseTransform * btrans, GstBuffer * buf)
{
GstAudioFilter *filter = GST_AUDIO_FILTER (btrans);
GstIirEqualizer *equ = GST_IIR_EQUALIZER (btrans);
GstClockTime timestamp;
GstMapInfo map;
gint channels = GST_AUDIO_FILTER_CHANNELS (filter);
gboolean need_new_coefficients;
if (G_UNLIKELY (channels < 1 || equ->process == NULL))
return GST_FLOW_NOT_NEGOTIATED;
BANDS_LOCK (equ);
need_new_coefficients = equ->need_new_coefficients;
BANDS_UNLOCK (equ);
timestamp = GST_BUFFER_TIMESTAMP (buf);
timestamp =
gst_segment_to_stream_time (&btrans->segment, GST_FORMAT_TIME, timestamp);
if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
GstIirEqualizerBand **filters = equ->bands;
guint f, nf = equ->freq_band_count;
gst_object_sync_values (GST_OBJECT (equ), timestamp);
/* sync values for bands too */
/* FIXME: iterating equ->bands is not thread-safe here */
for (f = 0; f < nf; f++) {
gst_object_sync_values (GST_OBJECT (filters[f]), timestamp);
}
}
BANDS_LOCK (equ);
if (need_new_coefficients) {
update_coefficients (equ);
}
BANDS_UNLOCK (equ);
gst_buffer_map (buf, &map, GST_MAP_READWRITE);
equ->process (equ, map.data, map.size, channels);
gst_buffer_unmap (buf, &map);
return GST_FLOW_OK;
}
static gboolean
gst_iir_equalizer_setup (GstAudioFilter * audio, const GstAudioInfo * info)
{
GstIirEqualizer *equ = GST_IIR_EQUALIZER (audio);
switch (GST_AUDIO_INFO_FORMAT (info)) {
case GST_AUDIO_FORMAT_S16:
equ->history_size = history_size_gint16;
equ->process = gst_iir_equ_process_gint16;
break;
case GST_AUDIO_FORMAT_F32:
equ->history_size = history_size_gfloat;
equ->process = gst_iir_equ_process_gfloat;
break;
case GST_AUDIO_FORMAT_F64:
equ->history_size = history_size_gdouble;
equ->process = gst_iir_equ_process_gdouble;
break;
default:
return FALSE;
}
alloc_history (equ, info);
return TRUE;
}
static gboolean
plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (equalizer_debug, "equalizer", 0, "equalizer");
if (!(gst_element_register (plugin, "equalizer-nbands", GST_RANK_NONE,
GST_TYPE_IIR_EQUALIZER_NBANDS)))
return FALSE;
if (!(gst_element_register (plugin, "equalizer-3bands", GST_RANK_NONE,
GST_TYPE_IIR_EQUALIZER_3BANDS)))
return FALSE;
if (!(gst_element_register (plugin, "equalizer-10bands", GST_RANK_NONE,
GST_TYPE_IIR_EQUALIZER_10BANDS)))
return FALSE;
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
equalizer,
"GStreamer audio equalizers",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)